Compare commits

...
Author SHA1 Message Date
johnQ 237631147a chore(skill): 清理全部 23 条历史校验警告
- 11 个超 20KB 的 SKILL.md 拆分,大章节移入 references/details.md
- 7 处非标准字段(arguments/user_invocable)移入 metadata
- 修 req-dev 4 条相对链接;validate 跳过行内代码避免 ![](...) 误报
- validate-skills.py: 23 warns -> 0 warns,0 errors
2026-10-31 22:21:07 +08:00
johnQ 7d47762303 fix(install): audit 跳过 deprecated 技能,避免把不装的技能算成漂移 2026-10-31 22:19:32 +08:00
qiudl a7a753b833 Merge pull request 'feat(skill): 技能治理 v0.2 —— 分桶/调用模式/校验/安全/清单/安装矩阵' (#12) from feat/skill-management into main
feat(skill): 技能治理 v0.2(分桶/调用模式/校验/安全/清单/安装矩阵)
2026-10-31 14:18:15 +00:00
johnQ aecd2ce521 docs: README 加技能路由表入口,CHANGELOG 记录降级与分类调整 2026-10-31 22:18:10 +08:00
johnQ 41a1032ccf chore(skill): 按 SCOPE 降级两个无增量技能
- verification-before-completion / ai-proj-helper-entry 标记 deprecated
  (3 组难用例均无增量:核心规则已在用户级 AGENTS.md)
- 入口路由内容转成 docs/skill-routing.md(索引不算规则,不占技能目录)
- catalog: promoted 58(40 model-invoked + 18 user-invoked)、beta 1、deprecated 2
2026-10-31 22:17:06 +08:00
johnQ d5bdfcfb7c refactor(skill): 18 个命令型技能转 user-invoked,压目录预算
- biz-* 4 个、integration-* 8 个、dev-android/ios/pda、publish、ai-proj-cicd-release、ai-proj-macos-release
- 均加 metadata.invocation: user-invoked + disable-model-invocation: true(DSH 实测生效)
- catalog: promoted/model-invoked 40、promoted/user-invoked 18、beta 3
2026-10-31 22:16:39 +08:00
johnQ d43b414fc9 feat(skill): 技能治理 v0.2 —— 分桶/调用模式/校验/安全/清单/安装矩阵
- 新增 SCOPE.md 收录门槛 + .out-of-scope/ 拒绝档案
- 生命周期分桶 promoted/beta/deprecated,marketplace 只发布 promoted
- 调用模式 model-invoked/user-invoked(user-invoked 不占模型目录)
- 新增脚本 validate-skills.py / generate-catalog.py / scan-skill.sh(+allowlist)
- 新增 beta 技能 skill-creator、ai-proj-helper-entry、verification-before-completion(各带 evals)
- install-skills.sh: --agent dsh / --link / --audit / --include-beta / --no-validate
- 融合:dev-review 接收评审意见、req-design 依赖边、agent-swarm 并行编排、dev-test TDD
- catalog.json 61 个技能(58 promoted / 3 beta);validate 0 错误;scan 0 高危;现有测试通过
2026-10-31 22:03:13 +08:00
johnQ dde146995e feat(req): 新增「复用盘点」门禁(Gate 5 + PRD §1.5)
- req 技能:4 道门禁 → 5 道,新增 Gate 5「复用盘点先于估工」;无 file:line 证据或标「未排查」禁止提审、禁止按全新增估工
- req-prd 模板:新增 §1.5「现有实现与复用盘点」强制章节(能力点 / file:line / 复用|改造|新增 / 理由)
- 触发:DS Bot 已有头像上传却被当新增估工的事故复盘
- 版本:req-plugin 2.0.0 → 2.1.0,req-prd-plugin 2.2.0 → 2.3.0;marketplace.json 同步再生成
2026-10-27 11:03:47 +08:00
johnQ d5550d62c5 docs(slark-dsh-cicd): 修正参考文件错别字(REQ-20261007-0003) 2026-10-07 17:07:15 +08:00
johnQ c74fe71f0a feat(slark-dsh-cicd): 新增 DSH/Slark 发布实战技能(REQ-20261007-0003)
- 新建 skills-dev/slark-dsh-cicd-plugin:plugin.json + skills/SKILL.md +
  skills/references/fork-release-and-post-checks.md
- 覆盖两条发布线(DSH 插件 pack 双资产 / Slark 宿主重打包+签名)、权限与身份三连
  (push:true 才可写,ls-remote 通 ≠ 能 push)、无上游写权限的 fork 发布且原仓库零改动、
  干净单提交 + git diff --stat <OURS> 树一致性 gate、改指向后必须重新 pack、
  draft→publish 与「更新检查看不到新版」的因果、GitHub HTTPS/fetch 卡死/无 macOS timeout
  的判死处置、发布后 SHA 逐项核对(verify-remote 仅 Windows)、app.asar 完整性约束、
  ai-proj staging→released 与 /req done 证据要求及文档接口故障绕过
- 与 slark-cicd 明确分工并交叉引用,不重复 staging/生产/Desktop 细节
- update-plugin-meta.py PLUGIN_MAP 增加 slark-dsh-cicd-plugin (skill/dev)
- generate-marketplace.py devops 分类补充 slark-dsh-cicd;marketplace.json 仅新增该条目
- 校验:claude plugin validate . 通过(警告为该仓库既有 install_name 字段提示)、git diff --check 干净
2026-10-07 17:06:36 +08:00
johnQ 2e0f7fe68c feat(dev-coding): 新增「Step 0.5 Shell 环境自检」— node/npm 缺 PATH + HOME 未设置
在 macOS + Slark/DSH 实测定位到:bash 工具以 bash -c(非登录非交互)执行,
子进程环境等于宿主进程环境(PATH 不被清洗,HOME/USER/TMPDIR/LANG 未设置),
宿主由 GUI 应用拉起继承最小 PATH → node/npm/git 直接不可用。

沉淀内容:
- 5 条根因(不读 rc / PATH 不被清洗 / GUI 宿主 / HOME 未设置 / shell-env 只认 DSH_*)
- 两行 export 模板:HOME + node v22.22.3 PATH(勿混用 homebrew 的 v26)
- HOME 必须补的理由:~ 靠 passwd 展开易误判,git 报 fatal: $HOME not set,
  set -u 脚本报 unbound variable;「node 能跑」≠ 环境完整
- 交付纪律:禁止把「没有 shell / 不能构建」写成交付结论,未跑构建就如实标注
2026-10-07 07:42:00 +08:00
qiudl 3ffee89840 Merge pull request '[REQ-20260825-0006] Harden Slark handoff recovery guidance' (#10) 2026-08-25 21:15:44 +00:00
92 changed files with 6295 additions and 3939 deletions
+15 -2
View File
@@ -414,6 +414,19 @@
], ],
"strict": false "strict": false
}, },
{
"name": "slark-dsh-cicd-plugin",
"source": "./skills-dev/slark-dsh-cicd-plugin",
"description": "DSH 插件包与 Slark 宿主补丁的发布实战技能:npm run pack 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、draft Release、发布后 SHA 核对、asar 完整性与 ai-proj 状态对齐。",
"version": "1.0.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"strict": false
},
{ {
"name": "req-audit-plugin", "name": "req-audit-plugin",
"source": "./skills-req/req-audit-plugin", "source": "./skills-req/req-audit-plugin",
@@ -483,7 +496,7 @@
"name": "req-plugin", "name": "req-plugin",
"source": "./skills-req/req-plugin", "source": "./skills-req/req-plugin",
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。", "description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
"version": "2.0.0", "version": "2.1.0",
"category": "productivity", "category": "productivity",
"keywords": [ "keywords": [
"project-management", "project-management",
@@ -496,7 +509,7 @@
"name": "req-prd-plugin", "name": "req-prd-plugin",
"source": "./skills-req/req-prd-plugin", "source": "./skills-req/req-prd-plugin",
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。", "description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
"version": "2.2.0", "version": "2.3.0",
"category": "productivity", "category": "productivity",
"keywords": [ "keywords": [
"project-management", "project-management",
@@ -0,0 +1,19 @@
# 已拒绝:为某一种 harness 单开分支
- 状态:拒绝(长期有效)
- 日期:2026-10-31
## 想法
为 Codex / DSH / Claude 某一个客户端的特例,单独复制一份技能或加一个 harness 分支。
## 为什么拒绝
1. 复制出来的两份技能会立刻开始漂移,评测也要做两遍;
2. 客户端差异应交给安装器(目录、调用模式、字段映射)处理,不该写进技能内容;
3. 特例需求通常是个人的工作流调整,属于 `AGENTS.md` / `CLAUDE.md` 或 fork 的范围。
## 允许的替代路径
- 客户端差异 → 安装器加 target / 字段映射;
- 个人流程 → 个人目录或 fork。
+20
View File
@@ -0,0 +1,20 @@
# 已拒绝:新增「随手想到」的技能
- 状态:拒绝(长期有效)
- 日期:2026-10-31
## 想法
「我觉得应该有个技能做 X」——从想象出发新增技能,而不是从真实失败出发。
## 为什么拒绝
1. 技能目录是每次会话启动就要付的上下文预算,加一个就多一份;
2. 没有真实失败做依据的技能,写出来是泛泛而谈,实际触发率极低;
3. 技能之间会互相抢触发(description 相似),新增一个会稀释已有技能的准确率。
## 允许的替代路径
- 先用现有技能做一遍真实任务,记录卡点;
- 确实组合不出来,再按 `SCOPE.md` 门槛提,附真实失败证据;
- 只是个人偏好 → 放 `skills-personal/`,不进团队库。
+19
View File
@@ -0,0 +1,19 @@
# 已拒绝:把个人工作流调优塞进团队技能
- 状态:拒绝(长期有效)
- 日期:2026-10-31
## 想法
把某个人的偏好(提交信息格式、分支命名、审查语气、工具习惯)直接写进团队技能。
## 为什么拒绝
1. 团队技能要能被所有人用,个人偏好会变成隐式规训;
2. 一旦写进技能,改起来要走 PR,个人改不动;
3. 这类偏好本来就该放个人 `AGENTS.md` / `CLAUDE.md`。
## 允许的替代路径
- 个人偏好 → 个人配置文件或 `skills-personal/`;
- 如果是团队共识(不是个人偏好),走 `SCOPE.md` 门槛并附真实失败。
+32
View File
@@ -0,0 +1,32 @@
# Changelog
## Unreleased — 2026-10-31
### 新增
- 技能治理:`SCOPE.md` 收录门槛 + `.out-of-scope/` 拒绝档案。
- 生命周期分桶(`promoted` / `beta` / `deprecated`)与调用模式(`model-invoked` / `user-invoked`)。
- `scripts/validate-skills.py`:规范/大小/引用/调用模式/文档校验。
- `scripts/generate-catalog.py`:生成可审阅的 `catalog.json`。
- `scripts/scan-skill.sh` + `scripts/scan-allowlist.txt`:入库前安全扫描。
- 新技能(beta):`skill-creator`、`ai-proj-helper-entry`、`verification-before-completion`。
- `install-skills.sh`:`--agent dsh`、`--link`、`--audit`、`--include-beta`、`--no-validate`。
### 变更
- 18 个命令型技能转 `user-invoked`(biz/integration/dev-android|ios|pda/publish/发布链),目录预算减少 18 项。
- `dev-review`:增加「接收与响应评审意见」。
- `req-design`:增加「任务依赖边(blocking edges)」。
- `agent-swarm`:增加「并行编排流程」。
- `dev-test`:增加「TDD 循环」。
- `generate-marketplace.py`:只发布 `promoted`,beta/deprecated 不随 marketplace 分发。
- 按 SCOPE 降级两个无增量技能:`verification-before-completion`、`ai-proj-helper-entry`(规则已在用户级 AGENTS.md),入口路由内容转为 `docs/skill-routing.md`。
### 清理(2026-10-31)
- 11 个超过 20KB 的 `SKILL.md` 拆分:大章节移入 `references/details.md`,正文全部回到 20KB 预算内。
- 7 处非标准 frontmatter 字段(`arguments`、`user_invocable`)移入 `metadata`。
- 修复 `req-dev` 的 4 条相对链接;校验器改为跳过行内代码,消除 `![](...)` 误报。
- `validate-skills.py`:23 个警告 → **0 个警告**。
+7
View File
@@ -52,3 +52,10 @@ skills:
- `skills-personal/` 不被 Git 跟踪,用于存放个人配置和工具 - `skills-personal/` 不被 Git 跟踪,用于存放个人配置和工具
- 其余 `skills-*` 目录均由 Git 版本控制 - 其余 `skills-*` 目录均由 Git 版本控制
- 只有受 Git 跟踪的分类目录会被 `generate-marketplace.py` 扫描并加入公开 marketplace;`skills-personal/` 始终排除 - 只有受 Git 跟踪的分类目录会被 `generate-marketplace.py` 扫描并加入公开 marketplace;`skills-personal/` 始终排除
## 技能治理规则
- 公开 marketplace 只发布 `status=promoted`;`beta`/`deprecated` 不进 marketplace(`generate-marketplace.py` 已按 status 过滤)。
- 每个 `plugin.json` 建议带 `status`,每个 `SKILL.md` 在 `metadata.invocation` 标 `model-invoked` / `user-invoked`。
- 任何技能增删改后,回改 `catalog.json` 与入口技能 `ai-proj-helper-entry`。
- 提交前依次跑:`python3 scripts/validate-skills.py`、`./scripts/scan-skill.sh`、`python3 scripts/generate-catalog.py`。
+15
View File
@@ -195,3 +195,18 @@ A: SSE 模式直连远程服务器,零依赖开箱即用(推荐)。stdio
- **管理员**:Donglin Lai (qiudl) - **管理员**:Donglin Lai (qiudl)
- **邮箱**:qiudl@zhiyuncai.com - **邮箱**:qiudl@zhiyuncai.com
- **仓库**:https://gitea.pipexerp.com/pipexerp/ai-proj-helper - **仓库**:https://gitea.pipexerp.com/pipexerp/ai-proj-helper
## 技能治理(2026-10-31 起)
- **真相源**:本仓库。`~/.agents/skills`、`~/.claude/skills`、`$DSH_HOME/skills` 都只是安装产物。
- **收录门槛**见 [SCOPE.md](./SCOPE.md):只收「真实失败 + 组合不出来」,被拒的想法记在 `.out-of-scope/`。
- **生命周期**:`promoted`(默认安装)/ `beta`(需 `--include-beta`)/ `deprecated`(不安装);新技能一律先进 beta。
- **调用模式**:`model-invoked`(占模型目录预算)vs `user-invoked`(只由人触发,不占目录预算)。
- **安装**:
- Codex:`./install-skills.sh --agent codex`
- DSH:`DSH_HOME=<path> ./install-skills.sh --agent dsh --link`
- Claude(兼容):`./install-skills.sh --agent claude`
- 看漂移:`./install-skills.sh --audit`
- **校验 / 安全 / 清单**:`python3 scripts/validate-skills.py`、`./scripts/scan-skill.sh`、`python3 scripts/generate-catalog.py`
- **建技能**:走 `skill-creator` 技能(收录门槛、分桶、调用模式、evals、四段式文档、路由回改),不要手写。
- **路由表**:`docs/skill-routing.md`(什么任务用哪个技能,人读;由原 `ai-proj-helper-entry` 技能降级而来)。
+45
View File
@@ -0,0 +1,45 @@
# Scope — ai-proj-helper 技能的收录门槛
本仓库是团队每天在用的技能集,**不是技能收藏夹**。技能目录会在每次会话启动时占用模型的上下文预算,所以「多加一个」永远是有代价的。
## 门槛
新技能或改动**默认拒绝**,只有同时满足两条才进入评审:
1. **真实失败**:能说清在哪个真实任务/会话里出了问题——跑了什么、期望什么、实际什么。假设性的「这样会更好」不算。
2. **组合不出来**:用现有技能组合做不到。能组合的,改现有技能,不新增。
规模不重要:一个词的修改和一次重写按同一标准审。
## 分桶
| 桶 | 含义 | 安装 | 进 README / plugin.json |
| --- | --- | --- | --- |
| `promoted` | 已验证、日常在用 | 默认 | 必须 |
| `beta` | 公开试跑、收反馈 | `--include-beta` | 不进默认清单 |
| `deprecated` | 不再使用 | 不安装 | 只留文档,标弃用 |
新技能一律先进 `beta`;promoted 需要真实使用记录 + evals 通过。
## 调用模式
- `model-invoked`(默认):进模型目录,agent 自己判断何时用。
- `user-invoked`:只由人下命令触发,不占目录预算;必须写 `disable-model-invocation: true` + `metadata.invocation: user-invoked`。
判定:这件事是不是「人下命令才开始」?是 → user-invoked。
## 已经拒绝的想法
每个被拒概念一个文件,写在 [`.out-of-scope/`](./.out-of-scope/) 下,先读再提。当前已有:
- [`new-skills.md`](./.out-of-scope/new-skills.md)
- [`harness-specific-branches.md`](./.out-of-scope/harness-specific-branches.md)
- [`personal-workflow-tweaks.md`](./.out-of-scope/personal-workflow-tweaks.md)
## 提交流程
1. 先过 [`skill-creator`](./skills-core/skill-creator-plugin/skills/SKILL.md) 的收录门槛与质量门禁。
2. `python3 scripts/validate-skills.py` 通过。
3. `./scripts/scan-skill.sh` 无高危项。
4. `python3 scripts/generate-catalog.py` 更新 `catalog.json`。
5. 版本号 + CHANGELOG 更新,走 PR。
+21
View File
@@ -67,3 +67,24 @@ Claude Code 需要显式选择目标:
**如何移除技能?** **如何移除技能?**
删除插件目录、重新生成 marketplace、提交变更。使用者随后执行 `./install-skills.sh --dry-run --cleanup`,确认后再去掉 `--dry-run`。 删除插件目录、重新生成 marketplace、提交变更。使用者随后执行 `./install-skills.sh --dry-run --cleanup`,确认后再去掉 `--dry-run`。
## 安装矩阵(2026-10 更新)
| agent | 安装目录 | 默认模式 | 状态文件 |
| --- | --- | --- | --- |
| `codex`(默认) | `~/.agents/skills` | copy | `~/.agents/.ai-proj-helper-installed-skills.json` |
| `dsh` | `$DSH_HOME/skills` | copy(开发机建议 `--link`) | `$DSH_HOME/.ai-proj-helper-installed-skills.json` |
| `claude`(兼容) | `~/.claude/skills` | copy | `~/.claude/.installed-skills.json` |
```bash
# 本机(DSH 开发机,改仓库即时生效)
DSH_HOME="<your DSH_HOME>" ./install-skills.sh --agent dsh --link
# 远程/其他机器
./install-skills.sh --agent codex
# 看谁落后了
./install-skills.sh --agent codex --audit
```
`beta` 技能默认不装:需要时加 `--include-beta`。`promoted/beta/deprecated` 见 `SCOPE.md`。
+685
View File
@@ -0,0 +1,685 @@
{
"repo": "ai-proj-helper",
"generated_at": "2026-10-31T14:21:07Z",
"total": 61,
"counts": {
"promoted/user-invoked": 18,
"deprecated/user-invoked": 1,
"promoted/model-invoked": 40,
"beta/user-invoked": 1,
"deprecated/model-invoked": 1
},
"skills": [
{
"name": "biz-contract",
"category": "biz",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。",
"description_len": 78,
"path": "skills-biz/biz-contract-plugin/skills/SKILL.md",
"digest": "3c17cae1380d82a4"
},
{
"name": "biz-ops",
"category": "biz",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。",
"description_len": 71,
"path": "skills-biz/biz-ops-plugin/skills/SKILL.md",
"digest": "aa5433074ba277d6"
},
{
"name": "biz-plan",
"category": "biz",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。",
"description_len": 66,
"path": "skills-biz/biz-plan-plugin/skills/SKILL.md",
"digest": "b6f43a914b2f7b45"
},
{
"name": "finance",
"category": "biz",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。",
"description_len": 94,
"path": "skills-biz/finance-plugin/skills/SKILL.md",
"digest": "0a9209bc4bf4db3a"
},
{
"name": "ai-proj-helper-entry",
"category": "core",
"version": "0.1.0",
"status": "deprecated",
"invocation": "user-invoked",
"description": "ai-proj-helper 技能库路由:告诉你有哪些技能、什么任务用哪个、以及「REQ released + PDV 通过才算交付」的强制口径。人在开始需求/开发/评审/发布任务前主动调用。",
"description_len": 96,
"path": "skills-core/ai-proj-helper-entry-plugin/skills/SKILL.md",
"digest": "41d39b76d4c71dbf"
},
{
"name": "ai-proj",
"category": "core",
"version": "2.0.1",
"status": "promoted",
"invocation": "model-invoked",
"description": "ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。",
"description_len": 77,
"path": "skills-core/ai-proj-plugin/skills/SKILL.md",
"digest": "77ce4d2eb10e1d29"
},
{
"name": "pm-ask",
"category": "core",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "基于 ai-proj MCP + git log 真实数据的项目问答。禁止编造。当用户说\"/ask\"、\"问一下\"、\"统计一下\"、\"最近多少\"、\"谁做了多少\"、\"本月进度\"等项目相关问题时自动激活。",
"description_len": 98,
"path": "skills-core/pm-ask-plugin/skills/SKILL.md",
"digest": "e5b69a0b56985472"
},
{
"name": "pm-risk",
"category": "core",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "三维度项目风险扫描(需求层/代码层/流程层)。当用户说\"/risk\"、\"风险扫描\"、\"有什么风险\"、\"哪些需求停滞了\"、\"分支健康\"时自动激活。",
"description_len": 72,
"path": "skills-core/pm-risk-plugin/skills/SKILL.md",
"digest": "928cfec76018329f"
},
{
"name": "publish",
"category": "core",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。",
"description_len": 48,
"path": "skills-core/publish-plugin/skills/SKILL.md",
"digest": "48bfbb4149baddcc"
},
{
"name": "skill-creator",
"category": "core",
"version": "0.1.0",
"status": "beta",
"invocation": "user-invoked",
"description": "团队技能的标准流程:收录门槛、分桶生命周期、调用模式、SKILL.md 规范、evals 全量、四段式文档、路由回改。当要新建技能、大改现有技能、或评审一个技能是否合格时使用。",
"description_len": 88,
"path": "skills-core/skill-creator-plugin/skills/SKILL.md",
"digest": "18f18c43c3fa56a6"
},
{
"name": "agent-browser",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "浏览器自动化技能。用于网页交互、E2E冒烟测试、需求验收验证、前端开发验证、截图对比。基于 Vercel agent-browser CLI。",
"description_len": 71,
"path": "skills-dev/agent-browser-plugin/skills/SKILL.md",
"digest": "f382bffe3d1c84b2"
},
{
"name": "agent-swarm",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
"description_len": 153,
"path": "skills-dev/agent-swarm-plugin/skills/SKILL.md",
"digest": "a0d8a329ab9468f5"
},
{
"name": "ai-chat",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
"description_len": 78,
"path": "skills-dev/ai-chat-plugin/skills/SKILL.md",
"digest": "81193de50b43d95c"
},
{
"name": "ai-proj-cicd-release",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "Execute and audit the AI-Proj service CI/CD flow across Gitea gates, immutable image builds, staging verification, approved production release, rollback, and evidence capture. Use for AI-Proj CI status, release PRs, staging or production deployments, failed-release diagnosis, deployed commit/schema verification, and delivery-chain repair. Do not use for unrelated repositories or macOS application packaging.",
"description_len": 410,
"path": "skills-dev/ai-proj-cicd-release-plugin/skills/SKILL.md",
"digest": "49ef1b6865c2f1b3"
},
{
"name": "ai-proj-macos-release",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "Build, sign, notarize, publish, and verify the AI-Proj macOS Apple Silicon package through the repository's release chain. Use when asked to release, republish, update, or repair the downloadable macOS application, including Gatekeeper failures and download-manifest updates. Do not use for service deployments or unrelated applications.",
"description_len": 337,
"path": "skills-dev/ai-proj-macos-release-plugin/skills/SKILL.md",
"digest": "6f2496b941706137"
},
{
"name": "db-migration",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时由 req-design 推荐激活。",
"description_len": 77,
"path": "skills-dev/db-migration-plugin/skills/SKILL.md",
"digest": "f8d6ebbddbbf7a71"
},
{
"name": "defect-analysis",
"category": "dev",
"version": "1.1.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
"description_len": 68,
"path": "skills-dev/defect-analysis-plugin/skills/SKILL.md",
"digest": "23dfafda92d0c622"
},
{
"name": "deploy-rollback",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "回滚方案插件。部署后发现问题时的回滚策略和执行步骤。挂载在 deploy 阶段,部署出问题时激活。",
"description_len": 49,
"path": "skills-dev/deploy-rollback-plugin/skills/SKILL.md",
"digest": "75ebf66a3f74195a"
},
{
"name": "dev-android",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。",
"description_len": 72,
"path": "skills-dev/dev-android-plugin/skills/SKILL.md",
"digest": "fb058d1059b56cdd"
},
{
"name": "dev-arch",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "软件架构设计技能。用于系统设计、技术选型、架构评审、设计文档编写。当用户提到架构设计、系统设计、技术方案、API 设计相关任务时自动激活。",
"description_len": 69,
"path": "skills-dev/dev-arch-plugin/skills/SKILL.md",
"digest": "c9a419c83a307a11"
},
{
"name": "dev-cicd",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "CI/CD 流水线设计、优化与排查。适配 Gitea Actions + Go/Swift/Next.js/Docker 栈。当用户提到 CI、CD、流水线、pipeline、workflow、构建失败、runner 相关任务时自动激活。",
"description_len": 119,
"path": "skills-dev/dev-cicd-plugin/skills/SKILL.md",
"digest": "a763f0f140516411"
},
{
"name": "dev-coding",
"category": "dev",
"version": "2.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "软件编码开发技能。用于代码编写、功能实现、重构优化。集成 ai-proj CLI 进行任务管理和进度跟踪。核心支持 Go 后端 + Vue/React 前端开发。",
"description_len": 81,
"path": "skills-dev/dev-coding-plugin/skills/SKILL.md",
"digest": "1b754e668965132f"
},
{
"name": "dev-commit",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "智能 git commit — 分支保护 + 自动建功能分支 + Conventional Commits 生成。当用户说\"提交代码\"、\"commit\"、\"/commit\"、\"保存修改\"时自动激活。",
"description_len": 99,
"path": "skills-dev/dev-commit-plugin/skills/SKILL.md",
"digest": "1560658c4a30df45"
},
{
"name": "dev-deploy",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。",
"description_len": 90,
"path": "skills-dev/dev-deploy-plugin/skills/SKILL.md",
"digest": "fd15a9e9135bcc49"
},
{
"name": "dev-integration",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "前后端联调技能。API 契约验证、接口对接、联调报告生成。对应 req 流程 integration 阶段。纯后端需求自动跳过。",
"description_len": 64,
"path": "skills-dev/dev-integration-plugin/skills/SKILL.md",
"digest": "eff6be8eb57b7fef"
},
{
"name": "dev-ios",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。",
"description_len": 65,
"path": "skills-dev/dev-ios-plugin/skills/SKILL.md",
"digest": "44499e31f1abd5e2"
},
{
"name": "dev-mcp",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "MCP Bridge 开发插件。TypeScript MCP 服务开发,Token 管理,HTTP 客户端模式。当涉及 mcp-task-bridge 开发时按需加载。",
"description_len": 84,
"path": "skills-dev/dev-mcp-plugin/skills/SKILL.md",
"digest": "83d5912212fcfa4a"
},
{
"name": "dev-pda",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。",
"description_len": 57,
"path": "skills-dev/dev-pda-plugin/skills/SKILL.md",
"digest": "2524f2ecd4f2a834"
},
{
"name": "dev-review",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "代码评审技能。六视角对抗性扫描法(含 Karpathy Scope 审计),用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。",
"description_len": 91,
"path": "skills-dev/dev-review-plugin/skills/SKILL.md",
"digest": "a8a1ec79b85e3227"
},
{
"name": "dev-scaffold",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架。挂载在 dev 阶段,新建模块时激活。",
"description_len": 43,
"path": "skills-dev/dev-scaffold-plugin/skills/SKILL.md",
"digest": "4044787b1e468302"
},
{
"name": "dev-test",
"category": "dev",
"version": "2.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "软件测试技能。用于单元测试、集成测试、E2E测试、测试用例设计。支持 Go、Vue、React、iOS、Android 等多平台测试。",
"description_len": 67,
"path": "skills-dev/dev-test-plugin/skills/dev-test/SKILL.md",
"digest": "a03536a478eabe6c"
},
{
"name": "executing-plans",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints",
"description_len": 104,
"path": "skills-dev/executing-plans-plugin/skills/SKILL.md",
"digest": "8991a50091af83a4"
},
{
"name": "finishing-a-development-branch",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "Use when implementation is complete and all tests pass - verifies and creates PR",
"description_len": 80,
"path": "skills-dev/finishing-branch-plugin/skills/SKILL.md",
"digest": "1102047ccc060e60"
},
{
"name": "frontend-design",
"category": "dev",
"version": "1.0.1",
"status": "promoted",
"invocation": "model-invoked",
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.",
"description_len": 234,
"path": "skills-dev/frontend-design-plugin/skills/SKILL.md",
"digest": "84e9c104be7967cc"
},
{
"name": "karpathy-guidelines",
"category": "dev",
"version": "1.0.1",
"status": "promoted",
"invocation": "model-invoked",
"description": "Karpathy 四原则编码行为守则。减少 LLM 常见编码错误:过度实现、静默假设、顺手重构、无验证标准。在任意编码场景激活。",
"description_len": 64,
"path": "skills-dev/karpathy-guidelines-plugin/skills/SKILL.md",
"digest": "a68f347031110d6a"
},
{
"name": "pull-request",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "Use when starting new development tasks, creating pull requests, reviewing code, or managing PR feedback cycles on Gitea. Triggers on /pr commands or when user mentions PR, pull request, code review, or branch creation.",
"description_len": 219,
"path": "skills-dev/pull-request-plugin/skills/SKILL.md",
"digest": "40dac0d290782bb5"
},
{
"name": "review-checklist",
"category": "dev",
"version": "1.1.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "项目级代码评审检查清单。按项目积累特定检查项,挂载在 dev-review 下自动加载。每次 CR 时触发。",
"description_len": 54,
"path": "skills-dev/review-checklist-plugin/skills/SKILL.md",
"digest": "5bae13b6a8d92650"
},
{
"name": "slark-cicd",
"category": "dev",
"version": "1.1.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/CD 可执行 runbook。覆盖本地预推快检、PR 与内网 ci/internal-gate 门禁、合并 origin/main、生产前强制 staging 验收、审批式生产发布,以及在 m5max 构建签名、公证并上传 macOS/Windows Desktop 包到 OSS。当用户要在 slark(qiudl/qiu-slark)里发布/上线、部署到生产或 staging、发布 Desktop 安装包、盯 CI/合并 PR、复现或验证运行时行为、或排查发布链/推送路由问题时使用。",
"description_len": 285,
"path": "skills-dev/slark-cicd-plugin/skills/SKILL.md",
"digest": "2919c19d8a38dd23"
},
{
"name": "slark-dsh-cicd",
"category": "dev",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "DSH 插件包与 Slark 宿主补丁的发布 runbook。覆盖 npm run pack 双 tgz 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、改指向后必须重新打包、draft→publish 节奏、GitHub HTTPS/SSH 与 fetch 卡死处置、发布后 SHA 核对、app.asar 完整性约束、ai-proj 发布状态对齐。当用户要发布或升级 DSH 插件(dsh-worktable 等)、发布/更新 Slark 宿主补丁、push 或下载 GitHub 失败、插件「检查更新」看不到新版本、需要 fork 发布、或排查 asar 完整性/签名问题时使用。",
"description_len": 306,
"path": "skills-dev/slark-dsh-cicd-plugin/skills/SKILL.md",
"digest": "44a1322e15d42845"
},
{
"name": "data-excel",
"category": "integration",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "Excel 数据处理与 BI 集成。通过自然语言操作 Excel 文件的读取、编辑、转换,并支持导入到 BI 系统(Metabase)进行可视化分析。当用户提到 Excel、表格处理、数据导入、BI 分析相关任务时自动激活。",
"description_len": 112,
"path": "skills-integration/data-excel-plugin/skills/SKILL.md",
"digest": "8dc4be8f88cfd866"
},
{
"name": "doubao-voice",
"category": "integration",
"version": "1.0.1",
"status": "promoted",
"invocation": "user-invoked",
"description": "豆包语音API调用。支持语音合成(TTS)和唱歌。当用户提到语音合成、文字转语音、唱歌、豆包语音相关任务时自动激活。",
"description_len": 58,
"path": "skills-integration/doubao-voice-plugin/skills/SKILL.md",
"digest": "7dc73d3b97e61fd2"
},
{
"name": "feishu-bitable",
"category": "integration",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "飞书多维表格操作。用于记录增删改查、批量操作、筛选排序、数据同步。当需要操作飞书多维表格时使用。",
"description_len": 48,
"path": "skills-integration/feishu-bitable-plugin/skills/SKILL.md",
"digest": "3bf37533007365a3"
},
{
"name": "feishu-docx",
"category": "integration",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "飞书云文档操作。用于创建、编辑云文档,插入内容块,会议纪要生成。当需要操作飞书云文档时使用。",
"description_len": 46,
"path": "skills-integration/feishu-docx-plugin/skills/SKILL.md",
"digest": "49d097bfa2c283ab"
},
{
"name": "feishu",
"category": "integration",
"version": "1.1.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "飞书文档与多维表格操作入口。当用户提到飞书、云文档、多维表格、Bitable 相关任务时自动激活。",
"description_len": 49,
"path": "skills-integration/feishu-plugin/skills/SKILL.md",
"digest": "973c6ac22104ee38"
},
{
"name": "siyuan",
"category": "integration",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "思源笔记 API 集成。通过自然语言创建、编辑、搜索笔记,管理笔记本和文档树。当用户提到思源笔记、SiYuan、笔记管理、知识库相关任务时自动激活。",
"description_len": 74,
"path": "skills-integration/siyuan-plugin/skills/SKILL.md",
"digest": "2736c2b708791763"
},
{
"name": "siyuan-to-feishu",
"category": "integration",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "将思源笔记导出为 PDF 并发送到飞书群。当用户提到\"发送笔记\"、\"导出PDF发飞书\"、\"/siyuan send\"、\"分享到飞书\"相关任务时自动激活。",
"description_len": 76,
"path": "skills-integration/siyuan-to-feishu-plugin/skills/SKILL.md",
"digest": "998dace910c1eb7a"
},
{
"name": "wecom",
"category": "integration",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "企业微信集成。通过自然语言发送消息、管理群机器人、操作审批流程、管理通讯录。当用户提到企业微信、微信工作、群机器人、企业号、wecom相关任务时自动激活。",
"description_len": 77,
"path": "skills-integration/wecom-plugin/skills/SKILL.md",
"digest": "7084d88ccb87ba9b"
},
{
"name": "req-audit",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
"description_len": 55,
"path": "skills-req/req-audit-plugin/skills/SKILL.md",
"digest": "ff2e6197bbb059c6"
},
{
"name": "req-compare",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "对比式需求分析插件。系统平移、竞品借鉴、版本升级时使用参考对象对比法编写 PRD。挂载在 analysis 阶段,需要对比分析时由 req-prd 推荐激活。",
"description_len": 79,
"path": "skills-req/req-compare-plugin/skills/SKILL.md",
"digest": "3428e3f37ef9e9e8"
},
{
"name": "req-design",
"category": "req",
"version": "2.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。",
"description_len": 96,
"path": "skills-req/req-design-plugin/skills/SKILL.md",
"digest": "74848800cab6e269"
},
{
"name": "req-dev",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。",
"description_len": 74,
"path": "skills-req/req-dev-plugin/skills/SKILL.md",
"digest": "494a5be5e7a11c0b"
},
{
"name": "req-lookback",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
"description_len": 47,
"path": "skills-req/req-lookback-plugin/skills/SKILL.md",
"digest": "cc6bff614a8f7bf4"
},
{
"name": "req",
"category": "req",
"version": "2.1.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。",
"description_len": 90,
"path": "skills-req/req-plugin/skills/SKILL.md",
"digest": "193cf871b73faff1"
},
{
"name": "req-prd",
"category": "req",
"version": "2.3.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。",
"description_len": 76,
"path": "skills-req/req-prd-plugin/skills/SKILL.md",
"digest": "7c4ad2baab6b2118"
},
{
"name": "req-prototype",
"category": "req",
"version": "2.2.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "原型生成与关联。生成可交互 HTML、上传到 ai-proj OSS 并关联 Requirement/PRD iframe,也支持 Stitch AI 视觉探索。",
"description_len": 81,
"path": "skills-req/req-prototype-plugin/skills/SKILL.md",
"digest": "7e362f4f895fb50a"
},
{
"name": "req-research",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "需求调研插件。代码审计、数据库分析、现有功能调研、技术可行性评估。挂载在 analysis 阶段,需要深度调研时由 req-prd 推荐激活。",
"description_len": 71,
"path": "skills-req/req-research-plugin/skills/SKILL.md",
"digest": "a94924f53bd4c80e"
},
{
"name": "req-retro",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
"description_len": 57,
"path": "skills-req/req-retro-plugin/skills/SKILL.md",
"digest": "636b7099d85e9549"
},
{
"name": "req-review",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "PRD 评审方法论。用于需求评审、PRD 文档审查、评审意见编写。当执行 /req review 或需要评审 PRD 文档时使用。",
"description_len": 65,
"path": "skills-req/req-review-plugin/skills/SKILL.md",
"digest": "cd1a5181d72f35f4"
},
{
"name": "req-test-gate",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。",
"description_len": 159,
"path": "skills-req/req-test-gate-plugin/skills/SKILL.md",
"digest": "ceebcc2516ad40d3"
},
{
"name": "req-workflow",
"category": "req",
"version": "1.0.0",
"status": "promoted",
"invocation": "model-invoked",
"description": "需求完整工作流。用于从创建到归档的完整流程、Hook 自动同步、测试环境流程。当需要了解需求完整生命周期或同步策略时使用。",
"description_len": 61,
"path": "skills-req/req-workflow-plugin/skills/SKILL.md",
"digest": "87499da3817d162d"
},
{
"name": "verification-before-completion",
"category": "req",
"version": "0.1.0",
"status": "deprecated",
"invocation": "model-invoked",
"description": "汇报「完成/修好/通过」或关闭任务、提交 PR、宣布 REQ 完成之前,必须先运行验证命令并附证据;没有证据不得声称完成。当准备说完成、关闭任务、或汇报交付前使用。",
"description_len": 82,
"path": "skills-req/verification-before-completion-plugin/skills/SKILL.md",
"digest": "7c23649f4a0a8798"
}
]
}
+38
View File
@@ -0,0 +1,38 @@
# 技能路由表(人读)
> 由原 `ai-proj-helper-entry` 技能降级而来:内容属于「索引」而不是「规则」,做成文档即可,不占技能目录、不用维护技能生命周期。
## 交付终点(强制)
开发任务交付终点是:需求按 REQ 流程推进到具备发布条件并完成上线(`delivery_stage=released` + PDV 验收通过)。
写代码、过单测、提 PR、合并都只是过程节点;任务 `completed` ≠ 需求完成。
## 主流程
讨论(`req`) → PRD(`req-prd`) → 评审(`req-review`) → 设计(`req-design`) → 开发(`dev-coding`/`karpathy-guidelines`) → 测试(`dev-test`+`req-test-gate`) → 代码评审(`dev-review`) → 发布(`slark-cicd`/`slark-dsh-cicd`/`publish`) → PDV 验收 → released
## 按任务选技能
| 任务 | 技能 |
| --- | --- |
| 讨论 / 调研 / 对比 | `req`、`req-research`、`req-compare` |
| PRD / 原型 | `req-prd`、`req-prototype` |
| PRD 评审 | `req-review` |
| 设计 / 拆任务 | `req-design`、`dev-arch`、`db-migration` |
| 编码 | `dev-coding`、`karpathy-guidelines` |
| 测试 | `dev-test`、`req-test-gate` |
| 代码评审 | `dev-review`、`review-checklist` |
| 发布(Slark 仓库/Desktop) | `slark-cicd` |
| 发布(DSH 插件/宿主补丁) | `slark-dsh-cicd` |
| 发布(ai-proj 服务 / macOS 包) | `ai-proj-cicd-release`、`ai-proj-macos-release` |
| 回滚 | `deploy-rollback` |
| 部署后审计 / 回归 | `req-audit`、`req-lookback` |
| 复盘 | `req-retro` |
| 项目管理 | `project-management`、`pm-risk`、`pm-ask` |
| 建/改技能 | `skill-creator` |
## 红线
- 不得自审批;不得绕过门禁(测试 Gate / 发布 Gate / 归档 Gate)
- 不得把「某步骤完成」当结束;不得无证据声称完成
- 不得把凭据/内网地址/绝对路径写进技能或文档
+9 -1
View File
@@ -60,7 +60,7 @@ def load_config():
def get_category_and_keywords(plugin_name): def get_category_and_keywords(plugin_name):
if any(x in plugin_name for x in ['dev-', 'coding', 'frontend']): if any(x in plugin_name for x in ['dev-', 'coding', 'frontend']):
return "development", ["development", "coding", "workflow"] return "development", ["development", "coding", "workflow"]
elif any(x in plugin_name for x in ['ops-', 'deploy', 'server', 'slark-cicd']): elif any(x in plugin_name for x in ['ops-', 'deploy', 'server', 'slark-cicd', 'slark-dsh-cicd']):
return "devops", ["devops", "deployment", "operations"] return "devops", ["devops", "deployment", "operations"]
elif any(x in plugin_name for x in ['ai-proj', 'req']): elif any(x in plugin_name for x in ['ai-proj', 'req']):
return "productivity", ["project-management", "tasks", "requirements"] return "productivity", ["project-management", "tasks", "requirements"]
@@ -96,6 +96,14 @@ def scan_plugins(directory, source_prefix, disabled):
with open(manifest_path) as f: with open(manifest_path) as f:
manifest = json.load(f) manifest = json.load(f)
# Lifecycle buckets: only promoted plugins ship in the public
# marketplace. beta/deprecated must be installed with --include-beta.
import os
status = manifest.get("status", "promoted")
if status in ("beta", "deprecated") and os.environ.get("ALLOW_BETA") != "1":
print(f" ⊘ {plugin_name} (status={status}, not shipped)")
continue
category, keywords = get_category_and_keywords(plugin_name) category, keywords = get_category_and_keywords(plugin_name)
plugins.append({ plugins.append({
+96 -4
View File
@@ -5,7 +5,11 @@
# ./install-skills.sh [options] # ./install-skills.sh [options]
# #
# Options: # Options:
# --agent <agent> Install target: codex (default) or claude # --agent <agent> Install target: codex (default), dsh, or claude
# --link Symlink skills from the repo instead of copying (dev machines)
# --audit Show installed vs repo versions for the selected target
# --include-beta Also install skills whose plugin.json status=beta
# --no-validate Skip scripts/validate-skills.py before installing
# --dry-run Preview changes without writing anything # --dry-run Preview changes without writing anything
# --category <cat> Only install plugins in dir_category=<cat> # --category <cat> Only install plugins in dir_category=<cat>
# Valid values: biz, core, dev, integration, personal, req # Valid values: biz, core, dev, integration, personal, req
@@ -30,6 +34,10 @@ FORCE=false
CLEANUP=false CLEANUP=false
LIST_ONLY=false LIST_ONLY=false
INSTALL_ACTION=false INSTALL_ACTION=false
LINK_MODE=false
AUDIT_ONLY=false
INCLUDE_BETA=false
NO_VALIDATE=false
# ── Colour helpers ───────────────────────────────────────────────────────────── # ── Colour helpers ─────────────────────────────────────────────────────────────
GREEN='\033[0;32m' GREEN='\033[0;32m'
@@ -48,9 +56,13 @@ dry() { echo -e "${YELLOW}[dry]${RESET} $*"; }
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--agent) --agent)
[[ $# -ge 2 ]] || { error "--agent requires codex or claude"; exit 1; } [[ $# -ge 2 ]] || { error "--agent requires codex, dsh or claude"; exit 1; }
AGENT_TARGET="$2"; shift ;; AGENT_TARGET="$2"; shift ;;
--dry-run) DRY_RUN=true ;; --dry-run) DRY_RUN=true ;;
--link) LINK_MODE=true ;;
--audit) AUDIT_ONLY=true ;;
--include-beta) INCLUDE_BETA=true ;;
--no-validate) NO_VALIDATE=true ;;
--force) FORCE=true ;; --force) FORCE=true ;;
--cleanup) CLEANUP=true ;; --cleanup) CLEANUP=true ;;
--list) LIST_ONLY=true ;; --list) LIST_ONLY=true ;;
@@ -77,13 +89,21 @@ case "$AGENT_TARGET" in
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.agents/skills}" SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.agents/skills}"
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.agents/.ai-proj-helper-installed-skills.json}" STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.agents/.ai-proj-helper-installed-skills.json}"
;; ;;
dsh)
if [[ -z "${DSH_HOME:-}" ]]; then
error "--agent dsh requires DSH_HOME (or set AI_PROJ_HELPER_SKILLS_DIR explicitly)"
exit 1
fi
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${DSH_HOME}/skills}"
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${DSH_HOME}/.ai-proj-helper-installed-skills.json}"
;;
claude) claude)
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.claude/skills}" SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.claude/skills}"
COMMANDS_DIR="${AI_PROJ_HELPER_COMMANDS_DIR:-${HOME}/.claude/commands}" COMMANDS_DIR="${AI_PROJ_HELPER_COMMANDS_DIR:-${HOME}/.claude/commands}"
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.claude/.installed-skills.json}" STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.claude/.installed-skills.json}"
;; ;;
*) *)
error "Unsupported agent: $AGENT_TARGET (expected codex or claude)" error "Unsupported agent: $AGENT_TARGET (expected codex, dsh or claude)"
exit 1 exit 1
;; ;;
esac esac
@@ -281,8 +301,19 @@ install_plugin() {
effective_install_type="skill" effective_install_type="skill"
fi fi
local status
status="$(read_field "$json_path" status)"
status="${status:-promoted}"
if [[ "$LIST_ONLY" == true ]]; then if [[ "$LIST_ONLY" == true ]]; then
echo " [$dir_category] $effective_install_type:$install_name v$version" echo " [$dir_category] $status $effective_install_type:$install_name v$version"
return
fi
if [[ "$status" == "deprecated" ]]; then
return
fi
if [[ "$status" == "beta" && "$INCLUDE_BETA" != true ]]; then
return return
fi fi
@@ -305,6 +336,21 @@ install_plugin() {
return return
fi fi
# Dev machines may symlink instead of copying: repository edits apply at once.
if [[ "$LINK_MODE" == true && "$effective_install_type" != "command" ]]; then
if [[ "$DRY_RUN" == true ]]; then
dry "$install_name → symlink $target_path → $src_dir"
else
mkdir -p "$SKILLS_DIR"
rm -rf "$target_path"
ln -sfn "$src_dir" "$target_path"
state_set "$install_name" "$version" "symlink" "$(content_digest "$src_dir")"
ok "$install_name → symlink (v$version)"
fi
INSTALL_ACTION=true
return
fi
# A recorded content digest distinguishes repository updates from user edits. # A recorded content digest distinguishes repository updates from user edits.
# Legacy state is adopted automatically only when the target is missing or # Legacy state is adopted automatically only when the target is missing or
# already identical to the repository source. # already identical to the repository source.
@@ -415,6 +461,40 @@ cleanup_removed() {
done < <(state_all_names) done < <(state_all_names)
} }
# ── Audit ──────────────────────────────────────────────────────────────────────
audit_target() {
python3 - "$REPO_DIR" "$STATE_FILE" "$AGENT_TARGET" "$SKILLS_DIR" <<'PYEOF'
import json, sys, pathlib
repo, state_file, agent, skills_dir = sys.argv[1:5]
root = pathlib.Path(repo)
state = {}
if pathlib.Path(state_file).is_file():
try: state = json.loads(pathlib.Path(state_file).read_text())
except Exception: state = {}
rows = []
for pj in sorted(root.glob('skills-*/*-plugin/.claude-plugin/plugin.json')):
try: meta = json.loads(pj.read_text())
except Exception: continue
name = meta.get('install_name')
if not name: continue
status = meta.get('status', 'promoted')
if status == 'deprecated':
continue
want = meta.get('version', '')
got = state.get(name, {}).get('version', '')
itype = state.get(name, {}).get('install_type', '')
drift = '' if got == want else ('missing' if not got else 'stale')
rows.append((name, status, want, got or '-', itype or '-', drift))
drift_rows = [r for r in rows if r[5]]
print(f'audit agent={agent} skills_dir={skills_dir}')
print(f'{"name":34} {"status":10} {"repo":8} {"installed":9} {"mode":8} drift')
for r in rows:
print(f'{r[0]:34} {r[1]:10} {r[2]:8} {r[3]:9} {r[4]:8} {r[5]}')
print(f'\n{len(rows)} 个技能,{len(drift_rows)} 个漂移(未安装/版本落后)')
raise SystemExit(1 if drift_rows else 0)
PYEOF
}
# ── Main ─────────────────────────────────────────────────────────────────────── # ── Main ───────────────────────────────────────────────────────────────────────
main() { main() {
if [[ "$LIST_ONLY" == true ]]; then if [[ "$LIST_ONLY" == true ]]; then
@@ -429,7 +509,19 @@ main() {
return return
fi fi
if [[ "$AUDIT_ONLY" == true ]]; then
audit_target
return
fi
info "Installing skills for $AGENT_TARGET from: $REPO_DIR" info "Installing skills for $AGENT_TARGET from: $REPO_DIR"
if [[ "$NO_VALIDATE" != true && -f "$REPO_DIR/scripts/validate-skills.py" ]]; then
if ! python3 "$REPO_DIR/scripts/validate-skills.py" >/tmp/ai-proj-helper-validate.log 2>&1; then
error "validate-skills.py 未通过,修复后再装(或加 --no-validate 跳过)"
tail -20 /tmp/ai-proj-helper-validate.log >&2 || true
exit 1
fi
fi
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written" [[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written"
[[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER" [[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER"
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Generate catalog.json — the reviewable list of every skill in this repo.
Usage: python3 scripts/generate-catalog.py [--check]
(default) write catalog.json
--check fail if catalog.json is out of date (for CI)
"""
from __future__ import annotations
import hashlib
import json
import re
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
CATALOG = ROOT / "catalog.json"
def frontmatter(text: str) -> dict:
if not text.startswith("---"):
return {}
end = text.find("\n---", 3)
if end == -1:
return {}
data: dict = {}
metadata: dict = {}
key = None
for line in text[3:end].splitlines():
if not line.strip():
continue
m = re.match(r"^([A-Za-z0-9_-]+):\s*(.*)$", line)
if m:
key = m.group(1)
data[key] = m.group(2).strip().strip('"').strip("'")
continue
m = re.match(r"^\s+([A-Za-z0-9_-]+):\s*(.*)$", line)
if m and key == "metadata":
metadata[m.group(1)] = m.group(2).strip().strip('"').strip("'")
if metadata:
data["metadata"] = metadata
return data
def build() -> dict:
skills = []
for plugin_json in sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json")):
plugin_dir = plugin_json.parent.parent
meta = json.loads(plugin_json.read_text(encoding="utf-8"))
skill_files = sorted((plugin_dir / "skills").rglob("SKILL.md"))
if not skill_files:
continue
skill_md = skill_files[0]
fm = frontmatter(skill_md.read_text(encoding="utf-8", errors="replace"))
desc = fm.get("description", "")
body = skill_md.read_text(encoding="utf-8", errors="replace")
skills.append(
{
"name": meta.get("install_name") or fm.get("name"),
"category": meta.get("dir_category"),
"version": meta.get("version"),
"status": meta.get("status", "promoted"),
"invocation": (fm.get("metadata") or {}).get("invocation", "model-invoked"),
"description": desc,
"description_len": len(desc),
"path": str(skill_md.relative_to(ROOT)),
"digest": hashlib.sha256(body.encode("utf-8")).hexdigest()[:16],
}
)
counts: dict = {}
for skill in skills:
key = f"{skill['status']}/{skill['invocation']}"
counts[key] = counts.get(key, 0) + 1
return {
"repo": "ai-proj-helper",
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"total": len(skills),
"counts": counts,
"skills": skills,
}
def main() -> int:
catalog = build()
text = json.dumps(catalog, ensure_ascii=False, indent=2) + "\n"
if "--check" in sys.argv:
if not CATALOG.is_file():
print("catalog.json 不存在,请运行 python3 scripts/generate-catalog.py")
return 1
current = json.loads(CATALOG.read_text(encoding="utf-8"))
current.pop("generated_at", None)
fresh = dict(catalog)
fresh.pop("generated_at", None)
if current != fresh:
print("catalog.json 已过期,请重新生成")
return 1
print("catalog.json 是最新的")
return 0
CATALOG.write_text(text, encoding="utf-8")
print(f"写入 {CATALOG.relative_to(ROOT)}:{catalog['total']} 个技能 {catalog['counts']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+5
View File
@@ -0,0 +1,5 @@
# 安全扫描允许清单(人工确认过的误报)
# 格式:<路径子串>::<匹配文本中的特征串>
# 例:某技能文档把危险命令当作“禁止清单”引用,而非真的要执行。
skills-dev/slark-cicd-plugin::NEVER_AUTO
skills-dev/slark-cicd-plugin::destructive
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# scan-skill.sh — security scan before a skill enters the team library.
#
# Usage: ./scripts/scan-skill.sh [plugin-dir ...]
# With no arguments scans every skills-*/*-plugin directory.
# Exit 0 = clean (warnings allowed), 1 = high severity finding.
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
HIGH=0
WARN=0
# High severity: destructive, remote-code-execution, secret material.
HIGH_PATTERNS=(
'rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*[[:space:]]+/($|[[:space:]])'
'curl[^|]*\|[[:space:]]*(ba|z)?sh'
'wget[^|]*\|[[:space:]]*(ba|z)?sh'
'chmod[[:space:]]+777'
'BEGIN (RSA|OPENSSH|EC|DSA) PRIVATE KEY'
'sk-[A-Za-z0-9]{16,}'
'AKIA[0-9A-Z]{16}'
'ghp_[A-Za-z0-9]{20,}'
)
# Warnings: machine-specific paths, plain http endpoints.
WARN_PATTERNS=(
'/Users/[A-Za-z0-9._-]+/'
'http://[A-Za-z0-9.-]+'
'password[[:space:]]*=[[:space:]]*[^$]'
)
targets=("$@")
if [[ ${#targets[@]} -eq 0 ]]; then
while IFS= read -r dir; do
targets+=("$(dirname "$(dirname "$dir")")")
done < <(find "$ROOT" -path "*/skills-*/*-plugin/.claude-plugin/plugin.json" | sort)
fi
ALLOWLIST="$ROOT/scripts/scan-allowlist.txt"
is_allowlisted() {
# is_allowlisted <abs_path> <matched_line>
local path="$1" line="$2" entry sub needle
[[ -f "$ALLOWLIST" ]] || return 1
while IFS= read -r entry; do
[[ -z "$entry" || "$entry" == \#* ]] && continue
sub="${entry%%::*}"
needle="${entry#*::}"
if [[ "$path" == *"$sub"* && "$line" == *"$needle"* ]]; then
return 0
fi
done < "$ALLOWLIST"
return 1
}
for dir in "${targets[@]}"; do
rel="${dir#"$ROOT"/}"
for pattern in "${HIGH_PATTERNS[@]}"; do
while IFS= read -r hit; do
abs="${hit%%:*}"
detail="${hit#*:}"
if is_allowlisted "$abs" "$hit"; then
echo "[allow] $rel: $detail"
continue
fi
echo "[HIGH] $rel: $detail"
HIGH=$((HIGH + 1))
done < <(grep -rnE --include='*.md' --include='*.sh' --include='*.py' --include='*.js' --include='*.ts' --include='*.json' -- "$pattern" "$dir" 2>/dev/null | cut -c1-200)
done
for pattern in "${WARN_PATTERNS[@]}"; do
while IFS= read -r hit; do
echo "[warn] $rel: ${hit%%:*}:${hit#*:}"
WARN=$((WARN + 1))
done < <(grep -rnE --include='*.md' --include='*.sh' --include='*.py' --include='*.js' --include='*.ts' -- "$pattern" "$dir" 2>/dev/null | cut -c1-200)
done
done
echo ""
echo "扫描完成:${#targets[@]} 个插件,$HIGH 个高危,$WARN 个警告"
if [[ "$HIGH" -gt 0 ]]; then
echo "存在高危项,禁止入库,除非逐条确认并记录原因。"
exit 1
fi
exit 0
+162
View File
@@ -0,0 +1,162 @@
#!/usr/bin/env python3
"""Validate plugins and SKILL.md files against the Agent Skills spec + team rules.
Usage: python3 scripts/validate-skills.py [--strict]
Exit 0 = pass, 1 = errors found.
"""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
ALLOWED_FRONTMATTER = {
"name", "description", "license", "compatibility", "metadata",
"allowed-tools", "disable-model-invocation", "version",
}
NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,63}$")
BODY_MAX_BYTES = 20 * 1024
DESC_MIN, DESC_MAX = 30, 1024
STATUSES = {"promoted", "beta", "deprecated"}
INVOCATIONS = {"model-invoked", "user-invoked"}
REQUIRED_PLUGIN_FIELDS = ("install_name", "install_type", "dir_category", "version")
errors: list[str] = []
warnings: list[str] = []
def parse_frontmatter(text: str):
"""Return (frontmatter dict, body text). Supports one level of nesting."""
if not text.startswith("---"):
return {}, text
end = text.find("\n---", 3)
if end == -1:
return {}, text
raw, body = text[3:end], text[end + 4:]
data: dict = {}
metadata: dict = {}
key = None
for line in raw.splitlines():
if not line.strip():
continue
m = re.match(r"^([A-Za-z0-9_-]+):\s*(.*)$", line)
if m:
key = m.group(1)
data[key] = m.group(2).strip().strip('"').strip("'")
continue
m = re.match(r"^\s+([A-Za-z0-9_-]+):\s*(.*)$", line)
if m and key == "metadata":
metadata[m.group(1)] = m.group(2).strip().strip('"').strip("'")
elif key:
data[key] = (data.get(key, "") + " " + line.strip()).strip()
if metadata:
data["metadata"] = metadata
return data, body
def check_plugin(plugin_json: Path):
plugin_dir = plugin_json.parent.parent
rel = plugin_dir.relative_to(ROOT)
try:
meta = json.loads(plugin_json.read_text(encoding="utf-8"))
except Exception as exc: # noqa: BLE001
errors.append(f"{rel}: plugin.json 解析失败: {exc}")
return
for field in REQUIRED_PLUGIN_FIELDS:
if not meta.get(field):
errors.append(f"{rel}: plugin.json 缺少 {field}")
status = meta.get("status", "")
if status and status not in STATUSES:
errors.append(f"{rel}: status 非法 {status!r}(应为 {sorted(STATUSES)})")
install_name = meta.get("install_name", "")
if install_name and not NAME_RE.match(install_name):
errors.append(f"{rel}: install_name 不合规 {install_name!r}")
skills_dir = plugin_dir / "skills"
skill_files = sorted(skills_dir.rglob("SKILL.md")) if skills_dir.is_dir() else []
if not skill_files:
errors.append(f"{rel}: 没有找到 skills/**/SKILL.md")
return
for skill_md in skill_files:
check_skill(rel, skill_md, meta)
def check_skill(rel: Path, skill_md: Path, meta: dict):
text = skill_md.read_text(encoding="utf-8", errors="replace")
fm, body = parse_frontmatter(text)
where = f"{rel}/{skill_md.name}"
name = fm.get("name", "")
if not name:
errors.append(f"{where}: frontmatter 缺少 name")
elif not NAME_RE.match(name):
errors.append(f"{where}: name 不合规 {name!r}(小写字母/数字/连字符,≤64)")
desc = fm.get("description", "")
if not desc:
errors.append(f"{where}: frontmatter 缺少 description")
elif not (DESC_MIN <= len(desc) <= DESC_MAX):
warnings.append(f"{where}: description 长度 {len(desc)},建议 {DESC_MIN}-{DESC_MAX}(决定是否被触发)")
for key in fm:
if key == "metadata":
continue
if key not in ALLOWED_FRONTMATTER:
warnings.append(f"{where}: 非标准 frontmatter 字段 {key!r},建议移入 metadata")
body_bytes = len(body.encode("utf-8"))
if body_bytes > BODY_MAX_BYTES:
warnings.append(f"{where}: 正文 {body_bytes} 字节,超过 20KB 预算,建议拆 references/")
metadata = fm.get("metadata") or {}
invocation = metadata.get("invocation", "")
if invocation and invocation not in INVOCATIONS:
errors.append(f"{where}: metadata.invocation 非法 {invocation!r}")
disable_model = str(fm.get("disable-model-invocation", "")).lower() == "true"
if invocation == "user-invoked" and not disable_model:
errors.append(f"{where}: user-invoked 技能必须设置 disable-model-invocation: true")
if invocation == "model-invoked" and disable_model:
errors.append(f"{where}: model-invoked 技能不应设置 disable-model-invocation: true")
status = meta.get("status", "")
if status == "promoted":
docs = ROOT / "docs" / "skills" / f"{meta.get('install_name', name)}.md"
if not docs.is_file():
warnings.append(f"{where}: promoted 技能缺少四段式文档 docs/skills/{docs.name}")
# Relative links must resolve (inline code spans are examples, skip them).
scan_body = re.sub(r"`[^`]*`", "", body)
for target in re.findall(r"\]\((\.[^)\s]+|[A-Za-z0-9_-]+/[^)\s]+)\)", scan_body):
if target.startswith(("http://", "https://", "#")):
continue
if not (skill_md.parent / target).exists():
warnings.append(f"{where}: 引用的相对路径不存在: {target}")
def main() -> int:
strict = "--strict" in sys.argv
plugins = sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json"))
if not plugins:
print("未找到任何 plugin.json", file=sys.stderr)
return 1
for plugin_json in plugins:
check_plugin(plugin_json)
for warning in warnings:
print(f"[warn] {warning}")
for error in errors:
print(f"[error] {error}")
print(f"\n检查 {len(plugins)} 个插件:{len(errors)} 个错误,{len(warnings)} 个警告")
if errors:
return 1
if strict and warnings:
print("--strict 模式下,警告视为失败")
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -7,5 +7,6 @@
}, },
"install_name": "biz-contract", "install_name": "biz-contract",
"install_type": "skill", "install_type": "skill",
"dir_category": "biz" "dir_category": "biz",
"invocation": "user-invoked"
} }
+6 -109
View File
@@ -1,10 +1,10 @@
--- ---
name: biz-contract name: biz-contract
description: 商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。 description: 商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 商务合同撰写技能 (biz-contract)
## 概述 ## 概述
本技能用于辅助商务合同的撰写与管理,支持多家公司主体、多种合同类型。 本技能用于辅助商务合同的撰写与管理,支持多家公司主体、多种合同类型。
@@ -631,112 +631,9 @@ api.publish_to_feishu(doc_id, "物流服务合同-妗晨与名风")
--- ---
## 飞书文档集成
本技能已集成飞书文档,可将合同直接创建到飞书云文档。 ## 详细内容
### 权限说明 以下章节已移到 [references/details.md](references/details.md):
**重要**:飞书文档默认对组织内权限是开放的,允许编辑。创建的合同文档在组织内成员均可查看和编辑。如需限制访问权限,请在飞书中手动调整文档权限设置。 - 飞书文档集成
### 可用工具
| 工具 | 功能 |
|------|------|
| `create_document` | 创建空白飞书文档 |
| `write_document` | 向已有文档写入内容 |
| `create_and_write_document` | 创建文档并写入内容(推荐) |
| `get_document_info` | 获取文档信息 |
### 使用示例
```
# 创建合同文档
使用 feishu-doc MCP 的 create_and_write_document 工具:
- title: "软件订阅服务合同-智慧云彩与妗晨"
- content: [Markdown 格式的合同内容]
- folder_token: [可选,指定文件夹]
```
### 获取文件夹 Token
1. 在飞书中打开目标文件夹
2. 复制 URL 中的 folder token(格式如:`fldcnXXXXXX`)
3. 调用工具时传入 folder_token 参数
### 飞书输出格式规范
#### 开票信息表格
物流合同中的开票信息应使用**表格**格式展示,列宽配置:
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 项目列宽 | 120px | 显示"名称"、"纳税人识别号"等标签 |
| 内容列宽 | 480px | 显示具体信息 |
```python
# 创建开票信息表格示例
from feishu_docx import FeishuDocx
docx = FeishuDocx()
doc = docx.create_document("物流服务合同")
# 创建表格(4行2列,指定列宽)
table = docx.create_table(doc["document_id"], rows=4, cols=2, col_widths=[120, 480])
# 填充内容
docx.fill_table(doc["document_id"], table, [
["名称", "重庆妗晨工贸有限公司"],
["纳税人识别号", "91500104MA7EJTPA6D"],
["地址、电话", "重庆市大渡口区跳磴镇海康路106号1-1 / 15213397998"],
["开户行及账号", "中国农业银行重庆大渡口天安支行 31230401040004386"],
])
```
#### 签章格式
推荐使用**独立区块式**签章格式:
```
【甲 方】
单位名称:重庆妗晨工贸有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
─────────────────────────────────────────────────────
【乙 方】
单位名称:北京名风新能源科技有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
```
**特点**:甲乙双方独立区块,留白充足,便于盖章签字。
#### 物流合同税率
| 服务类型 | 税率 | 发票类型 |
|----------|------|----------|
| 交通运输服务 | 9% | 增值税专用发票 |
#### 默认存储路径
| 平台 | 路径 |
|------|------|
| 飞书云文档 | ai-proj/01运营 (folder_token: `C80gfkRnzlonQ5d4AhOcOACDnNg`) |
| 思源笔记 | 商务合同笔记本 /物流合同/{甲方简称}-{乙方简称}-{年份} |
@@ -0,0 +1,111 @@
# skills 详细内容
## 飞书文档集成
本技能已集成飞书文档,可将合同直接创建到飞书云文档。
### 权限说明
**重要**:飞书文档默认对组织内权限是开放的,允许编辑。创建的合同文档在组织内成员均可查看和编辑。如需限制访问权限,请在飞书中手动调整文档权限设置。
### 可用工具
| 工具 | 功能 |
|------|------|
| `create_document` | 创建空白飞书文档 |
| `write_document` | 向已有文档写入内容 |
| `create_and_write_document` | 创建文档并写入内容(推荐) |
| `get_document_info` | 获取文档信息 |
### 使用示例
```
# 创建合同文档
使用 feishu-doc MCP 的 create_and_write_document 工具:
- title: "软件订阅服务合同-智慧云彩与妗晨"
- content: [Markdown 格式的合同内容]
- folder_token: [可选,指定文件夹]
```
### 获取文件夹 Token
1. 在飞书中打开目标文件夹
2. 复制 URL 中的 folder token(格式如:`fldcnXXXXXX`)
3. 调用工具时传入 folder_token 参数
### 飞书输出格式规范
#### 开票信息表格
物流合同中的开票信息应使用**表格**格式展示,列宽配置:
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 项目列宽 | 120px | 显示"名称"、"纳税人识别号"等标签 |
| 内容列宽 | 480px | 显示具体信息 |
```python
# 创建开票信息表格示例
from feishu_docx import FeishuDocx
docx = FeishuDocx()
doc = docx.create_document("物流服务合同")
# 创建表格(4行2列,指定列宽)
table = docx.create_table(doc["document_id"], rows=4, cols=2, col_widths=[120, 480])
# 填充内容
docx.fill_table(doc["document_id"], table, [
["名称", "重庆妗晨工贸有限公司"],
["纳税人识别号", "91500104MA7EJTPA6D"],
["地址、电话", "重庆市大渡口区跳磴镇海康路106号1-1 / 15213397998"],
["开户行及账号", "中国农业银行重庆大渡口天安支行 31230401040004386"],
])
```
#### 签章格式
推荐使用**独立区块式**签章格式:
```
【甲 方】
单位名称:重庆妗晨工贸有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
─────────────────────────────────────────────────────
【乙 方】
单位名称:北京名风新能源科技有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
```
**特点**:甲乙双方独立区块,留白充足,便于盖章签字。
#### 物流合同税率
| 服务类型 | 税率 | 发票类型 |
|----------|------|----------|
| 交通运输服务 | 9% | 增值税专用发票 |
#### 默认存储路径
| 平台 | 路径 |
|------|------|
| 飞书云文档 | ai-proj/01运营 (folder_token: `C80gfkRnzlonQ5d4AhOcOACDnNg`) |
| 思源笔记 | 商务合同笔记本 /物流合同/{甲方简称}-{乙方简称}-{年份} |
@@ -7,5 +7,6 @@
}, },
"install_name": "biz-ops", "install_name": "biz-ops",
"install_type": "skill", "install_type": "skill",
"dir_category": "biz" "dir_category": "biz",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: biz-ops name: biz-ops
description: 商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。 description: 商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 商务运营 (biz-ops) # 商务运营 (biz-ops)
@@ -7,5 +7,6 @@
}, },
"install_name": "biz-plan", "install_name": "biz-plan",
"install_type": "skill", "install_type": "skill",
"dir_category": "biz" "dir_category": "biz",
"invocation": "user-invoked"
} }
+6 -358
View File
@@ -1,10 +1,10 @@
--- ---
name: biz-plan name: biz-plan
description: 商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。 description: 商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 商业计划书撰写技能 (biz-plan)
## 概述 ## 概述
本技能用于辅助商业计划书的撰写与优化,特别适用于 B2B SaaS、供应链平台、产业互联网等领域。 本技能用于辅助商业计划书的撰写与优化,特别适用于 B2B SaaS、供应链平台、产业互联网等领域。
@@ -400,361 +400,9 @@ description: 商业计划书撰写。支持融资BP、战略规划、商业模
--- ---
## PPT 设计能力
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。 ## 详细内容
### PPT 输出方式 以下章节已移到 [references/details.md](references/details.md):
当用户要求生成 PPT 时,使用以下方式: - PPT 设计能力
1. **HTML 演示文稿**(推荐)
- 生成单个 HTML 文件
- 支持全屏演示(按 F 键)
- 支持键盘翻页(← →)
- 可直接打印为 PDF
- 响应式设计,适配各种屏幕
2. **Markdown 演示脚本**
- 输出每页内容的文字稿
- 用户可自行复制到 PPT 软件
### HTML PPT 模板
生成 PPT 时使用以下 HTML 模板结构:
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商业计划书 - [公司名称]</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
background: #1a1a2e;
color: #fff;
overflow: hidden;
}
.slide {
display: none;
width: 100vw;
height: 100vh;
padding: 60px 80px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
position: relative;
}
.slide.active {
display: flex;
flex-direction: column;
justify-content: center;
}
/* 封面样式 */
.slide.cover {
text-align: center;
justify-content: center;
align-items: center;
}
.slide.cover h1 {
font-size: 72px;
font-weight: 700;
margin-bottom: 20px;
background: linear-gradient(90deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.slide.cover .slogan {
font-size: 28px;
color: #a0aec0;
margin-bottom: 60px;
}
.slide.cover .info {
font-size: 18px;
color: #718096;
}
/* 内容页样式 */
.slide h2 {
font-size: 48px;
font-weight: 600;
margin-bottom: 40px;
color: #667eea;
}
.slide h3 {
font-size: 32px;
font-weight: 500;
margin-bottom: 24px;
color: #a0aec0;
}
.slide p, .slide li {
font-size: 24px;
line-height: 1.8;
color: #e2e8f0;
}
.slide ul {
list-style: none;
padding-left: 0;
}
.slide ul li {
padding: 12px 0;
padding-left: 40px;
position: relative;
}
.slide ul li::before {
content: "▸";
position: absolute;
left: 0;
color: #667eea;
}
/* 数据卡片 */
.metrics {
display: flex;
gap: 40px;
margin-top: 40px;
}
.metric-card {
flex: 1;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 16px;
padding: 32px;
text-align: center;
}
.metric-card .value {
font-size: 48px;
font-weight: 700;
color: #667eea;
}
.metric-card .label {
font-size: 18px;
color: #a0aec0;
margin-top: 8px;
}
/* 两栏布局 */
.two-columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
margin-top: 40px;
}
/* 流程图 */
.flow {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
margin-top: 40px;
}
.flow-item {
background: rgba(102, 126, 234, 0.15);
border-radius: 12px;
padding: 24px 32px;
text-align: center;
}
.flow-arrow {
color: #667eea;
font-size: 32px;
}
/* 页码 */
.page-number {
position: absolute;
bottom: 30px;
right: 40px;
font-size: 16px;
color: #4a5568;
}
/* 进度条 */
.progress {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 0.3s;
z-index: 100;
}
/* 导航提示 */
.nav-hint {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 14px;
color: #4a5568;
}
</style>
</head>
<body>
<div class="progress" id="progress"></div>
<!-- Slide 1: 封面 -->
<div class="slide cover active">
<h1>[公司名称]</h1>
<p class="slogan">[一句话 Slogan]</p>
<p class="info">[日期] · 商业计划书</p>
<span class="page-number">1 / N</span>
</div>
<!-- Slide 2: 公司定位 -->
<div class="slide">
<h2>公司定位</h2>
<h3>[一句话定位]</h3>
<div class="metrics">
<div class="metric-card">
<div class="value">[X]年</div>
<div class="label">深耕行业</div>
</div>
<div class="metric-card">
<div class="value">[X]家</div>
<div class="label">头部客户</div>
</div>
<div class="metric-card">
<div class="value">[X]轮</div>
<div class="label">完成融资</div>
</div>
</div>
<span class="page-number">2 / N</span>
</div>
<!-- 更多 slides... -->
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
<script>
const slides = document.querySelectorAll('.slide');
const progress = document.getElementById('progress');
let current = 0;
function showSlide(n) {
slides[current].classList.remove('active');
current = (n + slides.length) % slides.length;
slides[current].classList.add('active');
progress.style.width = ((current + 1) / slides.length * 100) + '%';
}
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
if (e.key === 'ArrowLeft') showSlide(current - 1);
if (e.key === 'f' || e.key === 'F') {
document.documentElement.requestFullscreen?.();
}
});
// 触摸滑动支持
let touchStartX = 0;
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
document.addEventListener('touchend', e => {
const diff = e.changedTouches[0].clientX - touchStartX;
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
});
</script>
</body>
</html>
```
### BP PPT 标准页面结构
生成商业计划书 PPT 时,按以下结构组织:
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|------|----------|----------|----------|
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
| 11 | 里程碑 | 发展历程 | 时间线 |
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
### PPT 设计原则
1. **一页一重点**:每页只传达一个核心信息
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
3. **数据可视化**:用数字卡片、图表代替文字描述
4. **留白充足**:内容不要铺满,保持呼吸感
5. **配色统一**:使用 2-3 种主色,保持一致性
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
### 配色方案
**深色科技风**(推荐):
- 背景:#1a1a2e → #16213e(渐变)
- 主色:#667eea(蓝紫色)
- 辅助:#764ba2(紫色)
- 文字:#e2e8f0(浅灰)
- 次要文字:#a0aec0(中灰)
**浅色商务风**:
- 背景:#ffffff
- 主色:#2196F3(蓝色)
- 辅助:#1976D2(深蓝)
- 文字:#212121(深灰)
- 次要文字:#616161(中灰)
### 使用示例
用户请求:
> 帮我生成商业计划书 PPT
输出方式:
1. 生成完整的 HTML 文件
2. 保存到用户指定位置(如桌面、项目目录)
3. 用户用浏览器打开即可演示
```bash
# 打开 PPT
open ~/Desktop/bp-presentation.html
# 或在浏览器中打开后按 F 进入全屏模式
```
### 导出为 PDF
HTML PPT 可通过浏览器打印功能导出为 PDF:
1. 在浏览器中打开 HTML 文件
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
3. 选择"另存为 PDF"
4. 设置页面大小为横向 A4 或 16:9
### 注意事项
1. **内容优先**:先确定 BP 内容,再生成 PPT
2. **数据占位**:敏感数据可用 [X] 占位
3. **迭代优化**:可分页生成,逐步完善
4. **本地演示**:HTML 文件完全离线可用,无需网络
@@ -0,0 +1,360 @@
# skills 详细内容
## PPT 设计能力
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
### PPT 输出方式
当用户要求生成 PPT 时,使用以下方式:
1. **HTML 演示文稿**(推荐)
- 生成单个 HTML 文件
- 支持全屏演示(按 F 键)
- 支持键盘翻页(← →)
- 可直接打印为 PDF
- 响应式设计,适配各种屏幕
2. **Markdown 演示脚本**
- 输出每页内容的文字稿
- 用户可自行复制到 PPT 软件
### HTML PPT 模板
生成 PPT 时使用以下 HTML 模板结构:
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商业计划书 - [公司名称]</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
background: #1a1a2e;
color: #fff;
overflow: hidden;
}
.slide {
display: none;
width: 100vw;
height: 100vh;
padding: 60px 80px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
position: relative;
}
.slide.active {
display: flex;
flex-direction: column;
justify-content: center;
}
/* 封面样式 */
.slide.cover {
text-align: center;
justify-content: center;
align-items: center;
}
.slide.cover h1 {
font-size: 72px;
font-weight: 700;
margin-bottom: 20px;
background: linear-gradient(90deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.slide.cover .slogan {
font-size: 28px;
color: #a0aec0;
margin-bottom: 60px;
}
.slide.cover .info {
font-size: 18px;
color: #718096;
}
/* 内容页样式 */
.slide h2 {
font-size: 48px;
font-weight: 600;
margin-bottom: 40px;
color: #667eea;
}
.slide h3 {
font-size: 32px;
font-weight: 500;
margin-bottom: 24px;
color: #a0aec0;
}
.slide p, .slide li {
font-size: 24px;
line-height: 1.8;
color: #e2e8f0;
}
.slide ul {
list-style: none;
padding-left: 0;
}
.slide ul li {
padding: 12px 0;
padding-left: 40px;
position: relative;
}
.slide ul li::before {
content: "▸";
position: absolute;
left: 0;
color: #667eea;
}
/* 数据卡片 */
.metrics {
display: flex;
gap: 40px;
margin-top: 40px;
}
.metric-card {
flex: 1;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 16px;
padding: 32px;
text-align: center;
}
.metric-card .value {
font-size: 48px;
font-weight: 700;
color: #667eea;
}
.metric-card .label {
font-size: 18px;
color: #a0aec0;
margin-top: 8px;
}
/* 两栏布局 */
.two-columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
margin-top: 40px;
}
/* 流程图 */
.flow {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
margin-top: 40px;
}
.flow-item {
background: rgba(102, 126, 234, 0.15);
border-radius: 12px;
padding: 24px 32px;
text-align: center;
}
.flow-arrow {
color: #667eea;
font-size: 32px;
}
/* 页码 */
.page-number {
position: absolute;
bottom: 30px;
right: 40px;
font-size: 16px;
color: #4a5568;
}
/* 进度条 */
.progress {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 0.3s;
z-index: 100;
}
/* 导航提示 */
.nav-hint {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 14px;
color: #4a5568;
}
</style>
</head>
<body>
<div class="progress" id="progress"></div>
<!-- Slide 1: 封面 -->
<div class="slide cover active">
<h1>[公司名称]</h1>
<p class="slogan">[一句话 Slogan]</p>
<p class="info">[日期] · 商业计划书</p>
<span class="page-number">1 / N</span>
</div>
<!-- Slide 2: 公司定位 -->
<div class="slide">
<h2>公司定位</h2>
<h3>[一句话定位]</h3>
<div class="metrics">
<div class="metric-card">
<div class="value">[X]年</div>
<div class="label">深耕行业</div>
</div>
<div class="metric-card">
<div class="value">[X]家</div>
<div class="label">头部客户</div>
</div>
<div class="metric-card">
<div class="value">[X]轮</div>
<div class="label">完成融资</div>
</div>
</div>
<span class="page-number">2 / N</span>
</div>
<!-- 更多 slides... -->
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
<script>
const slides = document.querySelectorAll('.slide');
const progress = document.getElementById('progress');
let current = 0;
function showSlide(n) {
slides[current].classList.remove('active');
current = (n + slides.length) % slides.length;
slides[current].classList.add('active');
progress.style.width = ((current + 1) / slides.length * 100) + '%';
}
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
if (e.key === 'ArrowLeft') showSlide(current - 1);
if (e.key === 'f' || e.key === 'F') {
document.documentElement.requestFullscreen?.();
}
});
// 触摸滑动支持
let touchStartX = 0;
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
document.addEventListener('touchend', e => {
const diff = e.changedTouches[0].clientX - touchStartX;
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
});
</script>
</body>
</html>
```
### BP PPT 标准页面结构
生成商业计划书 PPT 时,按以下结构组织:
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|------|----------|----------|----------|
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
| 11 | 里程碑 | 发展历程 | 时间线 |
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
### PPT 设计原则
1. **一页一重点**:每页只传达一个核心信息
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
3. **数据可视化**:用数字卡片、图表代替文字描述
4. **留白充足**:内容不要铺满,保持呼吸感
5. **配色统一**:使用 2-3 种主色,保持一致性
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
### 配色方案
**深色科技风**(推荐):
- 背景:#1a1a2e → #16213e(渐变)
- 主色:#667eea(蓝紫色)
- 辅助:#764ba2(紫色)
- 文字:#e2e8f0(浅灰)
- 次要文字:#a0aec0(中灰)
**浅色商务风**:
- 背景:#ffffff
- 主色:#2196F3(蓝色)
- 辅助:#1976D2(深蓝)
- 文字:#212121(深灰)
- 次要文字:#616161(中灰)
### 使用示例
用户请求:
> 帮我生成商业计划书 PPT
输出方式:
1. 生成完整的 HTML 文件
2. 保存到用户指定位置(如桌面、项目目录)
3. 用户用浏览器打开即可演示
```bash
# 打开 PPT
open ~/Desktop/bp-presentation.html
# 或在浏览器中打开后按 F 进入全屏模式
```
### 导出为 PDF
HTML PPT 可通过浏览器打印功能导出为 PDF:
1. 在浏览器中打开 HTML 文件
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
3. 选择"另存为 PDF"
4. 设置页面大小为横向 A4 或 16:9
### 注意事项
1. **内容优先**:先确定 BP 内容,再生成 PPT
2. **数据占位**:敏感数据可用 [X] 占位
3. **迭代优化**:可分页生成,逐步完善
4. **本地演示**:HTML 文件完全离线可用,无需网络
@@ -7,5 +7,6 @@
}, },
"install_name": "finance", "install_name": "finance",
"install_type": "skill", "install_type": "skill",
"dir_category": "biz" "dir_category": "biz",
"invocation": "user-invoked"
} }
+9 -454
View File
@@ -1,10 +1,10 @@
--- ---
name: finance name: finance
description: 财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。 description: 财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 财务对账技能
## 功能概述 ## 功能概述
- **银行对账**: 银行流水 vs 系统收付款记录匹配 - **银行对账**: 银行流水 vs 系统收付款记录匹配
@@ -478,457 +478,6 @@ def export_reconciliation_to_excel(
--- ---
## 常见对账场景
### 场景1: 月末银行对账
```python
# 1. 加载数据
bank_df = load_bank_statement('银行流水_202601.xlsx')
system_df = load_system_records('收付款记录_202601.xlsx')
# 2. 执行对账
result = reconcile(bank_df, system_df)
# 3. 查看汇总
print(f"已匹配: {result['summary']['matched_count']} 笔")
print(f"银行未达: {result['summary']['bank_only_count']} 笔, 金额 {result['summary']['bank_only_amount']:,.2f}")
print(f"企业未达: {result['summary']['system_only_count']} 笔, 金额 {result['summary']['system_only_amount']:,.2f}")
# 4. 生成调节表
report = generate_bank_reconciliation_report(
bank_balance=150000.00, # 银行对账单余额
book_balance=145000.00, # 企业账面余额
bank_only=result['bank_only'],
system_only=result['system_only'],
as_of_date='2026-01-31'
)
print(report)
# 5. 导出 Excel
export_reconciliation_to_excel(result, '银行对账_202601.xlsx', '2026-01-31')
```
### 场景2: 客户对账
```python
# 加载客户交易明细
ar_transactions = pd.read_excel('应收明细.xlsx')
# 筛选特定客户
customer_data = ar_transactions[ar_transactions['customer'] == '深圳XX公司']
# 生成对账单
statement = generate_ar_statement(
customer_name='深圳XX公司',
transactions=customer_data,
as_of_date='2026-01-31'
)
print(statement)
```
### 场景3: 账龄分析
```python
# 加载未收款数据
receivables = pd.read_excel('应收账款.xlsx')
# 账龄分析
aging = aging_analysis(
receivables=receivables,
as_of_date='2026-01-31',
aging_buckets=[30, 60, 90, 180, 365]
)
print(aging)
# 导出
aging.to_excel('账龄分析_202601.xlsx')
```
### 场景4: 扫描单据OCR识别与对账匹配(诉讼证据核对)
适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。
```python
from pdf2image import convert_from_path
import pytesseract
import pandas as pd
import re
def ocr_pdf_extract_orders(pdf_path: str, dpi: int = 150) -> pd.DataFrame:
"""
从 PDF 扫描件中 OCR 提取订单信息
Args:
pdf_path: PDF 文件路径
dpi: 图像分辨率,越高识别越准但速度越慢
Returns:
DataFrame 包含 order_no, page, tc_no 等字段
"""
print(f"正在转换 PDF...")
images = convert_from_path(pdf_path, dpi=dpi)
print(f"共 {len(images)} 页,开始 OCR 识别...")
all_records = []
for i, img in enumerate(images, 1):
if i % 10 == 0:
print(f" 处理进度: {i}/{len(images)}")
text = pytesseract.image_to_string(img, lang='chi_sim+eng')
# 提取多种订单号格式(根据实际业务调整)
hm_orders = re.findall(r'(HM\d{12,15})(?:-\d+)?', text) # 华住订单
hpo_orders = re.findall(r'(HPO-\d{8}-\d+)', text) # 华住采购订单
fy_orders = re.findall(r'(FY\d{9,})', text) # 凤悦订单
phgco_orders = re.findall(r'(PHGCO\d{9,})', text) # 锦江订单
tc_orders = re.findall(r'(TC\d{8,})', text) # 运单号
# 提取日期
dates = re.findall(r'(\d{4}-\d{2}-\d{2})', text)
all_orders = hm_orders + hpo_orders + fy_orders + phgco_orders
for order in all_orders:
all_records.append({
'order_no': order,
'page': i,
'tc_no': tc_orders[0] if tc_orders else None,
'date': dates[0] if dates else None,
'raw_text': text[:500] # 保留部分原文用于核查
})
df = pd.DataFrame(all_records)
df = df.drop_duplicates(subset=['order_no'])
print(f"提取完成,共 {len(df)} 条唯一订单")
return df
def extract_base_order_no(order_no: str) -> str:
"""
提取基础订单号(移除后缀如 -1, -2)
例如: HM202505227320-1 -> HM202505227320
"""
if pd.isna(order_no):
return None
order_str = str(order_no)
match = re.match(r'(HM\d{12,15}|HPO-\d{8}-\d+|FY\d{9,}|PHGCO\d{9,})', order_str)
return match.group(1) if match else order_str
def reconcile_scanned_with_excel(
scanned_df: pd.DataFrame,
excel_path: str,
main_sheet: str,
pending_sheet: str = None,
order_column: str = '华住/锦江订单号'
) -> dict:
"""
扫描单据与 Excel 对账单匹配
Args:
scanned_df: OCR 提取的订单 DataFrame
excel_path: Excel 对账单路径
main_sheet: 主数据 Sheet 名称
pending_sheet: 待结算/次月结 Sheet 名称(可选)
order_column: 订单号列名
Returns:
{
'matched_main': 在主表中匹配的订单,
'matched_pending': 在待结算表中匹配的订单,
'not_found': 未找到的订单,
'summary': 汇总统计
}
"""
# 加载主表
df_main = pd.read_excel(excel_path, sheet_name=main_sheet)
# 处理表头(如果第一行是表头)
if df_main.iloc[0].astype(str).str.contains(order_column).any():
df_main.columns = df_main.iloc[0]
df_main = df_main.iloc[1:].reset_index(drop=True)
# 提取基础订单号
main_orders = set(df_main[order_column].apply(extract_base_order_no).dropna())
# 加载待结算表(如有)
pending_orders = set()
df_pending = None
if pending_sheet:
try:
df_pending = pd.read_excel(excel_path, sheet_name=pending_sheet)
pending_orders = set(df_pending[order_column].apply(extract_base_order_no).dropna())
except:
pass
all_excel_orders = main_orders | pending_orders
# 扫描件订单号
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
# 匹配分析
matched_main = scan_orders & main_orders
matched_pending = scan_orders & pending_orders
not_found = scan_orders - all_excel_orders
# 汇总
summary = {
'scan_total': len(scan_orders),
'matched_main_count': len(matched_main),
'matched_pending_count': len(matched_pending),
'not_found_count': len(not_found),
'match_rate': (len(matched_main) + len(matched_pending)) / len(scan_orders) * 100 if scan_orders else 0
}
return {
'matched_main': matched_main,
'matched_pending': matched_pending,
'not_found': not_found,
'summary': summary,
'df_main': df_main,
'df_pending': df_pending
}
def generate_litigation_reconciliation_report(
result: dict,
scanned_df: pd.DataFrame,
output_path: str
):
"""
生成诉讼对账分析报告
"""
summary = result['summary']
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
# Sheet1: 汇总
summary_df = pd.DataFrame({
'项目': [
'扫描件订单数',
'在主对账单中找到',
'在待结算表中找到',
'完全未找到',
'匹配率'
],
'数值': [
summary['scan_total'],
summary['matched_main_count'],
summary['matched_pending_count'],
summary['not_found_count'],
f"{summary['match_rate']:.1f}%"
]
})
summary_df.to_excel(writer, sheet_name='对账汇总', index=False)
# Sheet2: 未找到的订单
if result['not_found']:
not_found_df = scanned_df[
scanned_df['order_no'].apply(extract_base_order_no).isin(result['not_found'])
][['order_no', 'page', 'tc_no', 'date']]
not_found_df.columns = ['订单号', 'PDF页码', '运单号', '日期']
not_found_df.to_excel(writer, sheet_name='未找到订单', index=False)
# Sheet3: 待结算订单详情
if result['matched_pending'] and result['df_pending'] is not None:
pending_detail = result['df_pending'][
result['df_pending']['华住/锦江订单号'].apply(extract_base_order_no).isin(result['matched_pending'])
]
pending_detail.to_excel(writer, sheet_name='待结算订单', index=False)
print(f"报告已保存: {output_path}")
```
#### 使用示例
```python
# 1. 从 PDF 扫描件提取订单
pdf_path = "承运商证据_6月业务.pdf"
scanned_df = ocr_pdf_extract_orders(pdf_path, dpi=150)
# 2. 与 Excel 对账单匹配
result = reconcile_scanned_with_excel(
scanned_df=scanned_df,
excel_path="对账单_2025年6月.xlsx",
main_sheet="2025.6",
pending_sheet="次月结",
order_column="华住/锦江订单号"
)
# 3. 查看匹配结果
print(f"匹配率: {result['summary']['match_rate']:.1f}%")
print(f"未找到: {result['summary']['not_found_count']} 条")
if result['not_found']:
print("\n未找到的订单:")
for order in result['not_found']:
print(f" {order}")
# 4. 生成报告
generate_litigation_reconciliation_report(
result=result,
scanned_df=scanned_df,
output_path="诉讼对账分析报告.xlsx"
)
```
#### OCR 识别注意事项
1. **环境依赖**:
```bash
# macOS
brew install tesseract tesseract-lang poppler
pip install pdf2image pytesseract pandas openpyxl
```
2. **识别精度**:
- DPI 150 适合快速扫描,DPI 200-250 适合精确识别
- 中文识别需要 `chi_sim` 语言包
3. **订单号格式差异**:
- PDF 中可能是 `HM202505227320`
- Excel 中可能是 `HM202505227320-1`、`HM202505227320-2`(带后缀)
- 匹配时应使用基础订单号
4. **常见问题**:
- 空白页(签收单背面)会被自动跳过
- OCR 可能误识别字符(如 5→S, 0→O),建议人工抽查
- 大 PDF 处理较慢,可先用低 DPI 预览
5. **"次月结"/"待结算" 订单**:
- 这类订单通常标记为"没有报价"、"未建单"等
- 虽然在系统中有记录,但可能未计入正式结算金额
- 诉讼中需特别关注这类订单的状态
6. **多承运商订单归属校验(重要风险点)**:
- 承运商可能将其他公司的订单混入证据,企图多收运费
- 必须检查PDF订单是否出现在其他承运商的对账单中
- 如果订单同时出现在多个承运商对账单中,则为异常订单
#### 多承运商订单归属校验
```python
def check_order_ownership(
scanned_df: pd.DataFrame,
my_company_excel: str,
other_carrier_excels: list[str],
order_column: str = '华住/锦江订单号'
) -> dict:
"""
检查订单归属,识别是否有其他承运商的订单被混入
Args:
scanned_df: OCR 提取的订单 DataFrame
my_company_excel: 我司对账单路径
other_carrier_excels: 其他承运商对账单路径列表
order_column: 订单号列名
Returns:
{
'my_orders': 属于我司的订单,
'other_carrier_orders': 属于其他承运商的订单(异常),
'disputed_orders': 同时出现在多处的争议订单,
'summary': 汇总
}
"""
# 提取PDF订单
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
# 加载我司对账单订单
my_orders = set()
try:
xl = pd.ExcelFile(my_company_excel)
for sheet in xl.sheet_names:
df = pd.read_excel(my_company_excel, sheet_name=sheet)
for col in df.columns:
if '订单' in str(col):
orders = df[col].apply(extract_base_order_no).dropna()
my_orders.update(orders)
except Exception as e:
print(f"加载我司对账单失败: {e}")
# 加载其他承运商对账单订单
other_orders = {} # {carrier_name: set of orders}
for excel_path in other_carrier_excels:
carrier_name = excel_path.split('/')[-1]
carrier_orders = set()
try:
xl = pd.ExcelFile(excel_path)
for sheet in xl.sheet_names:
df = pd.read_excel(excel_path, sheet_name=sheet)
for col in df.columns:
if '订单' in str(col):
orders = df[col].apply(extract_base_order_no).dropna()
carrier_orders.update(orders)
other_orders[carrier_name] = carrier_orders
except:
pass
# 分析订单归属
all_other_orders = set()
for orders in other_orders.values():
all_other_orders.update(orders)
# 分类
only_mine = scan_orders & my_orders - all_other_orders # 仅在我司
only_others = scan_orders & all_other_orders - my_orders # 仅在其他承运商(异常!)
in_both = scan_orders & my_orders & all_other_orders # 同时出现(争议)
nowhere = scan_orders - my_orders - all_other_orders # 哪里都没有
# 识别异常订单属于哪个承运商
abnormal_details = []
for order in only_others:
for carrier, orders in other_orders.items():
if order in orders:
page = scanned_df[scanned_df['order_no'].apply(extract_base_order_no) == order]['page'].values
abnormal_details.append({
'order_no': order,
'belongs_to': carrier,
'pdf_page': page[0] if len(page) > 0 else None
})
summary = {
'scan_total': len(scan_orders),
'my_orders_count': len(only_mine),
'other_carrier_count': len(only_others), # 异常订单数
'disputed_count': len(in_both),
'not_found_count': len(nowhere)
}
return {
'my_orders': only_mine,
'other_carrier_orders': only_others,
'abnormal_details': abnormal_details,
'disputed_orders': in_both,
'not_found': nowhere,
'summary': summary
}
```
#### 使用示例(多承运商校验)
```python
# 检查订单归属
result = check_order_ownership(
scanned_df=scanned_df,
my_company_excel="智慧云彩对账单_2025年6月.xlsx",
other_carrier_excels=[
"友利速运对账单_2025年6月.xlsx",
"其他承运商对账单.xlsx"
]
)
# 检查是否有异常订单
if result['summary']['other_carrier_count'] > 0:
print(f"⚠️ 发现 {result['summary']['other_carrier_count']} 个异常订单!")
print("这些订单属于其他承运商,不应由我司支付:")
for item in result['abnormal_details']:
print(f" {item['order_no']} -> 属于 {item['belongs_to']} (PDF第{item['pdf_page']}页)")
else:
print("✓ 未发现异常订单,所有订单归属正确")
```
---
## 注意事项 ## 注意事项
1. **数据格式统一**: 确保日期、金额格式一致后再对账 1. **数据格式统一**: 确保日期、金额格式一致后再对账
@@ -946,3 +495,9 @@ else:
- **data-excel**: 处理复杂的 Excel 数据转换 - **data-excel**: 处理复杂的 Excel 数据转换
- **feishu**: 将对账报告保存到飞书云文档 - **feishu**: 将对账报告保存到飞书云文档
- **siyuan**: 记录对账工作日志到思源笔记 - **siyuan**: 记录对账工作日志到思源笔记
## 详细内容
以下章节已移到 [references/details.md](references/details.md):
- 常见对账场景
@@ -0,0 +1,453 @@
# skills 详细内容
## 常见对账场景
### 场景1: 月末银行对账
```python
# 1. 加载数据
bank_df = load_bank_statement('银行流水_202601.xlsx')
system_df = load_system_records('收付款记录_202601.xlsx')
# 2. 执行对账
result = reconcile(bank_df, system_df)
# 3. 查看汇总
print(f"已匹配: {result['summary']['matched_count']} 笔")
print(f"银行未达: {result['summary']['bank_only_count']} 笔, 金额 {result['summary']['bank_only_amount']:,.2f}")
print(f"企业未达: {result['summary']['system_only_count']} 笔, 金额 {result['summary']['system_only_amount']:,.2f}")
# 4. 生成调节表
report = generate_bank_reconciliation_report(
bank_balance=150000.00, # 银行对账单余额
book_balance=145000.00, # 企业账面余额
bank_only=result['bank_only'],
system_only=result['system_only'],
as_of_date='2026-01-31'
)
print(report)
# 5. 导出 Excel
export_reconciliation_to_excel(result, '银行对账_202601.xlsx', '2026-01-31')
```
### 场景2: 客户对账
```python
# 加载客户交易明细
ar_transactions = pd.read_excel('应收明细.xlsx')
# 筛选特定客户
customer_data = ar_transactions[ar_transactions['customer'] == '深圳XX公司']
# 生成对账单
statement = generate_ar_statement(
customer_name='深圳XX公司',
transactions=customer_data,
as_of_date='2026-01-31'
)
print(statement)
```
### 场景3: 账龄分析
```python
# 加载未收款数据
receivables = pd.read_excel('应收账款.xlsx')
# 账龄分析
aging = aging_analysis(
receivables=receivables,
as_of_date='2026-01-31',
aging_buckets=[30, 60, 90, 180, 365]
)
print(aging)
# 导出
aging.to_excel('账龄分析_202601.xlsx')
```
### 场景4: 扫描单据OCR识别与对账匹配(诉讼证据核对)
适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。
```python
from pdf2image import convert_from_path
import pytesseract
import pandas as pd
import re
def ocr_pdf_extract_orders(pdf_path: str, dpi: int = 150) -> pd.DataFrame:
"""
从 PDF 扫描件中 OCR 提取订单信息
Args:
pdf_path: PDF 文件路径
dpi: 图像分辨率,越高识别越准但速度越慢
Returns:
DataFrame 包含 order_no, page, tc_no 等字段
"""
print(f"正在转换 PDF...")
images = convert_from_path(pdf_path, dpi=dpi)
print(f"共 {len(images)} 页,开始 OCR 识别...")
all_records = []
for i, img in enumerate(images, 1):
if i % 10 == 0:
print(f" 处理进度: {i}/{len(images)}")
text = pytesseract.image_to_string(img, lang='chi_sim+eng')
# 提取多种订单号格式(根据实际业务调整)
hm_orders = re.findall(r'(HM\d{12,15})(?:-\d+)?', text) # 华住订单
hpo_orders = re.findall(r'(HPO-\d{8}-\d+)', text) # 华住采购订单
fy_orders = re.findall(r'(FY\d{9,})', text) # 凤悦订单
phgco_orders = re.findall(r'(PHGCO\d{9,})', text) # 锦江订单
tc_orders = re.findall(r'(TC\d{8,})', text) # 运单号
# 提取日期
dates = re.findall(r'(\d{4}-\d{2}-\d{2})', text)
all_orders = hm_orders + hpo_orders + fy_orders + phgco_orders
for order in all_orders:
all_records.append({
'order_no': order,
'page': i,
'tc_no': tc_orders[0] if tc_orders else None,
'date': dates[0] if dates else None,
'raw_text': text[:500] # 保留部分原文用于核查
})
df = pd.DataFrame(all_records)
df = df.drop_duplicates(subset=['order_no'])
print(f"提取完成,共 {len(df)} 条唯一订单")
return df
def extract_base_order_no(order_no: str) -> str:
"""
提取基础订单号(移除后缀如 -1, -2)
例如: HM202505227320-1 -> HM202505227320
"""
if pd.isna(order_no):
return None
order_str = str(order_no)
match = re.match(r'(HM\d{12,15}|HPO-\d{8}-\d+|FY\d{9,}|PHGCO\d{9,})', order_str)
return match.group(1) if match else order_str
def reconcile_scanned_with_excel(
scanned_df: pd.DataFrame,
excel_path: str,
main_sheet: str,
pending_sheet: str = None,
order_column: str = '华住/锦江订单号'
) -> dict:
"""
扫描单据与 Excel 对账单匹配
Args:
scanned_df: OCR 提取的订单 DataFrame
excel_path: Excel 对账单路径
main_sheet: 主数据 Sheet 名称
pending_sheet: 待结算/次月结 Sheet 名称(可选)
order_column: 订单号列名
Returns:
{
'matched_main': 在主表中匹配的订单,
'matched_pending': 在待结算表中匹配的订单,
'not_found': 未找到的订单,
'summary': 汇总统计
}
"""
# 加载主表
df_main = pd.read_excel(excel_path, sheet_name=main_sheet)
# 处理表头(如果第一行是表头)
if df_main.iloc[0].astype(str).str.contains(order_column).any():
df_main.columns = df_main.iloc[0]
df_main = df_main.iloc[1:].reset_index(drop=True)
# 提取基础订单号
main_orders = set(df_main[order_column].apply(extract_base_order_no).dropna())
# 加载待结算表(如有)
pending_orders = set()
df_pending = None
if pending_sheet:
try:
df_pending = pd.read_excel(excel_path, sheet_name=pending_sheet)
pending_orders = set(df_pending[order_column].apply(extract_base_order_no).dropna())
except:
pass
all_excel_orders = main_orders | pending_orders
# 扫描件订单号
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
# 匹配分析
matched_main = scan_orders & main_orders
matched_pending = scan_orders & pending_orders
not_found = scan_orders - all_excel_orders
# 汇总
summary = {
'scan_total': len(scan_orders),
'matched_main_count': len(matched_main),
'matched_pending_count': len(matched_pending),
'not_found_count': len(not_found),
'match_rate': (len(matched_main) + len(matched_pending)) / len(scan_orders) * 100 if scan_orders else 0
}
return {
'matched_main': matched_main,
'matched_pending': matched_pending,
'not_found': not_found,
'summary': summary,
'df_main': df_main,
'df_pending': df_pending
}
def generate_litigation_reconciliation_report(
result: dict,
scanned_df: pd.DataFrame,
output_path: str
):
"""
生成诉讼对账分析报告
"""
summary = result['summary']
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
# Sheet1: 汇总
summary_df = pd.DataFrame({
'项目': [
'扫描件订单数',
'在主对账单中找到',
'在待结算表中找到',
'完全未找到',
'匹配率'
],
'数值': [
summary['scan_total'],
summary['matched_main_count'],
summary['matched_pending_count'],
summary['not_found_count'],
f"{summary['match_rate']:.1f}%"
]
})
summary_df.to_excel(writer, sheet_name='对账汇总', index=False)
# Sheet2: 未找到的订单
if result['not_found']:
not_found_df = scanned_df[
scanned_df['order_no'].apply(extract_base_order_no).isin(result['not_found'])
][['order_no', 'page', 'tc_no', 'date']]
not_found_df.columns = ['订单号', 'PDF页码', '运单号', '日期']
not_found_df.to_excel(writer, sheet_name='未找到订单', index=False)
# Sheet3: 待结算订单详情
if result['matched_pending'] and result['df_pending'] is not None:
pending_detail = result['df_pending'][
result['df_pending']['华住/锦江订单号'].apply(extract_base_order_no).isin(result['matched_pending'])
]
pending_detail.to_excel(writer, sheet_name='待结算订单', index=False)
print(f"报告已保存: {output_path}")
```
#### 使用示例
```python
# 1. 从 PDF 扫描件提取订单
pdf_path = "承运商证据_6月业务.pdf"
scanned_df = ocr_pdf_extract_orders(pdf_path, dpi=150)
# 2. 与 Excel 对账单匹配
result = reconcile_scanned_with_excel(
scanned_df=scanned_df,
excel_path="对账单_2025年6月.xlsx",
main_sheet="2025.6",
pending_sheet="次月结",
order_column="华住/锦江订单号"
)
# 3. 查看匹配结果
print(f"匹配率: {result['summary']['match_rate']:.1f}%")
print(f"未找到: {result['summary']['not_found_count']} 条")
if result['not_found']:
print("\n未找到的订单:")
for order in result['not_found']:
print(f" {order}")
# 4. 生成报告
generate_litigation_reconciliation_report(
result=result,
scanned_df=scanned_df,
output_path="诉讼对账分析报告.xlsx"
)
```
#### OCR 识别注意事项
1. **环境依赖**:
```bash
# macOS
brew install tesseract tesseract-lang poppler
pip install pdf2image pytesseract pandas openpyxl
```
2. **识别精度**:
- DPI 150 适合快速扫描,DPI 200-250 适合精确识别
- 中文识别需要 `chi_sim` 语言包
3. **订单号格式差异**:
- PDF 中可能是 `HM202505227320`
- Excel 中可能是 `HM202505227320-1`、`HM202505227320-2`(带后缀)
- 匹配时应使用基础订单号
4. **常见问题**:
- 空白页(签收单背面)会被自动跳过
- OCR 可能误识别字符(如 5→S, 0→O),建议人工抽查
- 大 PDF 处理较慢,可先用低 DPI 预览
5. **"次月结"/"待结算" 订单**:
- 这类订单通常标记为"没有报价"、"未建单"等
- 虽然在系统中有记录,但可能未计入正式结算金额
- 诉讼中需特别关注这类订单的状态
6. **多承运商订单归属校验(重要风险点)**:
- 承运商可能将其他公司的订单混入证据,企图多收运费
- 必须检查PDF订单是否出现在其他承运商的对账单中
- 如果订单同时出现在多个承运商对账单中,则为异常订单
#### 多承运商订单归属校验
```python
def check_order_ownership(
scanned_df: pd.DataFrame,
my_company_excel: str,
other_carrier_excels: list[str],
order_column: str = '华住/锦江订单号'
) -> dict:
"""
检查订单归属,识别是否有其他承运商的订单被混入
Args:
scanned_df: OCR 提取的订单 DataFrame
my_company_excel: 我司对账单路径
other_carrier_excels: 其他承运商对账单路径列表
order_column: 订单号列名
Returns:
{
'my_orders': 属于我司的订单,
'other_carrier_orders': 属于其他承运商的订单(异常),
'disputed_orders': 同时出现在多处的争议订单,
'summary': 汇总
}
"""
# 提取PDF订单
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
# 加载我司对账单订单
my_orders = set()
try:
xl = pd.ExcelFile(my_company_excel)
for sheet in xl.sheet_names:
df = pd.read_excel(my_company_excel, sheet_name=sheet)
for col in df.columns:
if '订单' in str(col):
orders = df[col].apply(extract_base_order_no).dropna()
my_orders.update(orders)
except Exception as e:
print(f"加载我司对账单失败: {e}")
# 加载其他承运商对账单订单
other_orders = {} # {carrier_name: set of orders}
for excel_path in other_carrier_excels:
carrier_name = excel_path.split('/')[-1]
carrier_orders = set()
try:
xl = pd.ExcelFile(excel_path)
for sheet in xl.sheet_names:
df = pd.read_excel(excel_path, sheet_name=sheet)
for col in df.columns:
if '订单' in str(col):
orders = df[col].apply(extract_base_order_no).dropna()
carrier_orders.update(orders)
other_orders[carrier_name] = carrier_orders
except:
pass
# 分析订单归属
all_other_orders = set()
for orders in other_orders.values():
all_other_orders.update(orders)
# 分类
only_mine = scan_orders & my_orders - all_other_orders # 仅在我司
only_others = scan_orders & all_other_orders - my_orders # 仅在其他承运商(异常!)
in_both = scan_orders & my_orders & all_other_orders # 同时出现(争议)
nowhere = scan_orders - my_orders - all_other_orders # 哪里都没有
# 识别异常订单属于哪个承运商
abnormal_details = []
for order in only_others:
for carrier, orders in other_orders.items():
if order in orders:
page = scanned_df[scanned_df['order_no'].apply(extract_base_order_no) == order]['page'].values
abnormal_details.append({
'order_no': order,
'belongs_to': carrier,
'pdf_page': page[0] if len(page) > 0 else None
})
summary = {
'scan_total': len(scan_orders),
'my_orders_count': len(only_mine),
'other_carrier_count': len(only_others), # 异常订单数
'disputed_count': len(in_both),
'not_found_count': len(nowhere)
}
return {
'my_orders': only_mine,
'other_carrier_orders': only_others,
'abnormal_details': abnormal_details,
'disputed_orders': in_both,
'not_found': nowhere,
'summary': summary
}
```
#### 使用示例(多承运商校验)
```python
# 检查订单归属
result = check_order_ownership(
scanned_df=scanned_df,
my_company_excel="智慧云彩对账单_2025年6月.xlsx",
other_carrier_excels=[
"友利速运对账单_2025年6月.xlsx",
"其他承运商对账单.xlsx"
]
)
# 检查是否有异常订单
if result['summary']['other_carrier_count'] > 0:
print(f"⚠️ 发现 {result['summary']['other_carrier_count']} 个异常订单!")
print("这些订单属于其他承运商,不应由我司支付:")
for item in result['abnormal_details']:
print(f" {item['order_no']} -> 属于 {item['belongs_to']} (PDF第{item['pdf_page']}页)")
else:
print("✓ 未发现异常订单,所有订单归属正确")
```
---
@@ -0,0 +1,13 @@
{
"name": "ai-proj-helper-entry-plugin",
"description": "技能库路由:列出技能、什么任务用哪个、交付终点口径。由人主动调用,不占模型目录 token。",
"version": "0.1.0",
"author": {
"name": "qiudl"
},
"install_name": "ai-proj-helper-entry",
"install_type": "skill",
"dir_category": "core",
"status": "deprecated",
"invocation": "user-invoked"
}
@@ -0,0 +1,77 @@
---
name: ai-proj-helper-entry
description: ai-proj-helper 技能库路由:告诉你有哪些技能、什么任务用哪个、以及「REQ released + PDV 通过才算交付」的强制口径。人在开始需求/开发/评审/发布任务前主动调用。
metadata:
invocation: user-invoked
owner: qiudl
disable-model-invocation: true
---
# ai-proj-helper 入口(路由器)
## 0. 这个技能是什么
一个**纯人触发**的路由器:人问「该用哪个技能 / 从哪开始」,就读这里。它不进模型目录,所以不占每次会话的目录 token;代价是必须由人主动喊。
> 路由规则:**任何技能增、删、改名、改行为后,必须回改本文件**。一个没提到新技能、或还指向已删技能的路由器,就是假的路由器(见 `skill-creator` §9)。
## 1. 交付终点(强制,优先于路由)
开发任务的交付终点是:需求按 REQ 流程推进到具备发布条件并完成上线(`delivery_stage=released` + PDV 验收通过)。
- 写代码、过单测、提 PR、过 internal gate、合并代码,都是过程节点,**不得当作交付终点**;
- 任务 `completed` ≠ 需求完成;REQ 未 `released` 不得报完成;
- 汇报格式:**当前 REQ 阶段 + 下一个要做的节点**;
- 只有必须由人拍板/授权的阻塞(生产发布授权、密钥权限、需求变更、无法自动修复的失败)才停下来问,并写清卡点与下一步。
## 2. 主流程:需求 → 上线
```
讨论(req) → PRD(req-prd) → 评审(req-review) → 设计(req-design)
→ 开发(dev-coding / karpathy-guidelines) → 测试(dev-test + req-test-gate)
→ 代码评审(dev-review) → 发布(publish / slark-cicd / slark-dsh-cicd) → PDV 验收 → released
```
| 阶段 | 技能 |
| --- | --- |
| 讨论/调研 | `req`、`req-research`、`req-compare` |
| PRD | `req-prd`、`req-prototype` |
| 评审 | `req-review` |
| 设计/拆任务 | `req-design`、`dev-arch`、`db-migration` |
| 编码 | `dev-coding`、`karpathy-guidelines` |
| 测试 | `dev-test`、`req-test-gate` |
| 代码评审 | `dev-review`、`review-checklist` |
| 发布 | `publish`、`slark-cicd`、`slark-dsh-cicd`、`deploy-rollback` |
| 部署后 | `req-audit`、`req-lookback` |
| 复盘 | `req-retro` |
| 项目管理 | `project-management`、`pm-risk`、`pm-ask` |
| 建/改技能 | `skill-creator` |
| 汇报完成前 | `verification-before-completion` |
## 3. 按任务类型的旁路
- 「这个 bug 很难查」→ `diagnosing-bugs`(先建红循环再推理)
- 「帮我审这个 PR」→ `dev-review`
- 「发版」→ 对应 `*-cicd` runbook,不是通用 `dev-deploy`
- 「不确定该用哪个」→ 读到这里就够了,别猜
## 4. 红线
- 不得自审批(提交人不能批自己的 REQ)
- 不得绕过门禁(测试 Gate、发布 Gate、归档 Gate)
- 不得把「某步骤完成」当结束
- 不得无证据声称完成(见 `verification-before-completion`)
- 不得把凭据/内网地址/绝对路径写进技能或文档
## 5. 技能从哪来
- 用户级:`~/.agents/skills/`(Codex)、`$DSH_HOME/skills/`(DSH)
- 项目级:项目根 `.agents/skills/`
- **真相源**:`ai-proj-helper` 仓库;本机目录是安装产物,不要直接改
- 个人技能:`skills-personal/`(不入库、不发布)
## 6. 维护检查
- `./install-skills.sh --audit` 漂移数 > 0 → 先同步再干活
- 技能有增删改 → 本文件 + `catalog.json` 必须同 PR 回改
- 每季度人工过一遍本路由,删掉不再用的技能引用
@@ -0,0 +1,24 @@
{
"skill_name": "ai-proj-helper-entry",
"skill_version": "0.1.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "我要给 qiu-slark 做个新功能,从哪开始",
"expected_output": "给出 req → req-prd → req-review → req-design 的路径,并声明交付终点是 released + PDV,不是写完代码"
},
{
"id": 2,
"kind": "edge_case",
"prompt": "我已经把单测跑过了,这个需求可以算完成了吗",
"expected_output": "否定:单测只是过程节点,REQ 未 released 不算完成;给出当前阶段与下一节点"
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "你说的那个技能我机器上没装,怎么办",
"expected_output": "说明技能真相源在 ai-proj-helper 仓库、本机目录是安装产物;给出 install-skills.sh 安装/审计命令"
}
]
}
+9 -664
View File
@@ -2,11 +2,6 @@
name: ai-proj name: ai-proj
description: ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。 description: ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。
--- ---
# ai-proj MCP 技能
通过 MCP (Model Context Protocol) 直接调用 ai-proj 服务,管理任务、需求和文档。
## 功能概述 ## 功能概述
| 模块 | 功能 | | 模块 | 功能 |
@@ -572,102 +567,6 @@ approved → [archive] → archived
--- ---
## 六、每日聚焦
### 6.1 查看今日任务
**MCP 函数**: `mcp__ai-proj__get_daily_focus_tasks`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| date | string | ❌ | 日期 (YYYY-MM-DD) |
| status | string | ❌ | 状态筛选: active/completed/removed |
| priority | string | ❌ | 优先级筛选 |
| include_suggestions | boolean | ❌ | 是否包含智能推荐 |
### 6.2 添加任务到今日聚焦
**单个添加**: `mcp__ai-proj__add_daily_focus_task`
```
参数:
- task_id (number) - 任务ID
- priority (string) - 优先级: critical/high/medium/low
- estimated_duration_minutes (number) - 预估时长
- notes (string) - 备注
- focus_date (string) - 聚焦日期
```
**批量添加**: `mcp__ai-proj__batch_add_daily_focus_tasks`
```
参数:
- task_ids (array) - 任务ID列表
- priority (string) - 统一优先级
- notes (string) - 统一备注
- focus_date (string)
```
**快速添加当前任务**: `mcp__ai-proj__quick_add_current_task`
```
参数:
- priority (string) - 优先级,默认high
- notes (string)
功能: 将当前正在进行的任务添加到今日聚焦
```
### 6.3 管理今日任务
**更新**: `mcp__ai-proj__update_daily_focus_task`
```
参数:
- id (number) - Daily Focus任务ID
- priority, estimated_duration_minutes, notes
```
**移除**: `mcp__ai-proj__remove_daily_focus_task`
```
参数: id (number)
```
**完成**: `mcp__ai-proj__complete_daily_focus_task`
```
参数: id (number)
```
**聚焦并计时**: `mcp__ai-proj__focus_task_with_timer`
```
参数:
- daily_focus_task_id (number)
- timer_description (string)
```
**清理已完成**: `mcp__ai-proj__clear_completed_tasks`
```
参数:
- date (string) - 日期
- confirm (boolean) - 必须设为true
```
### 6.4 统计与推荐
**获取统计**: `mcp__ai-proj__get_daily_focus_stats`
```
参数:
- date (string)
- period (string) - 统计周期: daily/weekly/monthly
```
**获取任务推荐**: `mcp__ai-proj__get_task_recommendations`
```
参数:
- date (string)
- limit (number) - 推荐数量
- exclude_existing (boolean) - 排除已存在任务
返回: 基于优先级、截止日期等的智能推荐
```
---
## 七、项目管理 ## 七、项目管理
### 7.1 查看项目列表 ### 7.1 查看项目列表
@@ -696,90 +595,6 @@ approved → [archive] → archived
--- ---
## 八、项目手册
### 8.1 查看手册列表
**MCP 函数**: `mcp__ai-proj__list_manuals`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| projectId | number | ✅ | 项目ID |
| status | string | ❌ | 状态筛选: draft/published/archived |
| parentId | number | ❌ | 父级手册ID(获取子级列表) |
| search | string | ❌ | 搜索关键词(标题或描述) |
**自然语言示例**:
- "查看项目手册列表"
- "列出已发布的手册"
- "查看手册 5 的子章节"
### 8.2 创建手册
**MCP 函数**: `mcp__ai-proj__create_manual`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| projectId | number | ✅ | 项目ID |
| title | string | ✅ | 手册标题 |
| content | string | ❌ | 手册内容(Markdown格式) |
| description | string | ❌ | 手册简介 |
| parentId | number | ❌ | 父级手册ID(用于创建子章节) |
| status | string | ❌ | 状态: draft/published,默认draft |
**自然语言示例**:
- "创建项目手册: API 开发规范"
- "在手册 5 下创建子章节: 接口设计规范"
- "创建并发布手册: 前端开发规范"
### 8.3 获取手册详情
**MCP 函数**: `mcp__ai-proj__get_manual`
**参数**: `id` (number) - 手册ID
**返回**: 完整的手册内容(Markdown格式)
**自然语言示例**:
- "查看手册 5 的内容"
- "显示 API 开发规范手册"
### 8.4 更新手册
**MCP 函数**: `mcp__ai-proj__update_manual`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | ✅ | 手册ID |
| title | string | ❌ | 新标题 |
| content | string | ❌ | 新内容(Markdown格式) |
| description | string | ❌ | 新简介 |
| status | string | ❌ | 新状态: draft/published/archived |
**自然语言示例**:
- "更新手册 5 的内容"
- "发布手册 5"
- "归档手册 5"
### 8.5 搜索手册
**MCP 函数**: `mcp__ai-proj__search_manuals`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| projectId | number | ✅ | 项目ID |
| query | string | ✅ | 搜索关键词 |
**自然语言示例**:
- "搜索项目手册中包含 API 的文档"
- "查找数据库相关的手册"
---
## 九、报告与时间线 ## 九、报告与时间线
### 9.1 获取日报 ### 9.1 获取日报
@@ -830,83 +645,6 @@ approved → [archive] → archived
--- ---
## 十一、远程同步
### 11.1 任务同步
**同步任务**: `mcp__ai-proj__sync_task`
```
参数:
- taskId (number)
- direction (string) - to_remote/from_remote
- includeDocument (boolean) - 是否同步文档
- includeChildren (boolean) - 是否同步子任务
```
**批量同步到远程**: `mcp__ai-proj__batch_sync_tasks_to_remote`
```
参数:
- taskIds (array)
- includeDocuments (boolean)
- includeChildren (boolean)
```
**获取同步状态**: `mcp__ai-proj__get_task_sync_status`
```
参数: taskId (number)
```
**获取远程同步状态**: `mcp__ai-proj__get_remote_sync_status`
```
参数: taskId (number)
返回: 比较本地和远程版本差异
```
**通过UUID获取任务**: `mcp__ai-proj__get_task_by_uuid`
```
参数: uuid (string)
用途: 跨库同步场景
```
### 11.2 需求同步
**同步需求到远程**: `mcp__ai-proj__sync_requirement_to_remote`
```
参数:
- requirementId (number)
- includeHistory (boolean)
- includeComments (boolean)
```
**从远程同步需求**: `mcp__ai-proj__sync_requirement_from_remote`
```
参数:
- remoteRequirementId (number)
- includeHistory (boolean)
- includeComments (boolean)
```
**批量同步需求**: `mcp__ai-proj__batch_sync_requirements_to_remote`
```
参数:
- requirementIds (array)
- includeComments (boolean)
```
**同步需求-任务关联**: `mcp__ai-proj__sync_requirement_task_links`
```
参数:
- linkUUIDs (array) - 关联关系的UUID列表
- forceUpdate (boolean)
```
**同步任务的需求关联**: `mcp__ai-proj__sync_task_links_to_remote`
```
参数: taskId (number)
```
---
## 十二、开发工具 ## 十二、开发工具
### 12.1 快速登录 ### 12.1 快速登录
@@ -919,52 +657,6 @@ approved → [archive] → archived
--- ---
## 自然语言操作示例
### 任务管理
| 用户说 | 调用函数 |
|--------|----------|
| "创建一个任务叫做登录功能开发" | `create_task` |
| "查看所有进行中的任务" | `list_tasks` |
| "开始任务 5069" | `start_task` |
| "完成任务 5069" | `complete_task` |
| "暂停任务 5069" | `pause_task` |
| "把任务 5069 移到项目2" | `move_task` |
| "给任务 5069 创建子任务: 数据库设计" | `create_subtask` |
### 需求管理
| 用户说 | 调用函数 |
|--------|----------|
| "创建需求: 用户权限管理" | `create_requirement` |
| "查看待审核的需求" | `list_requirements` |
| "提交需求 123 评审" | `requirement_action (submit)` |
| "批准需求 123" | `requirement_action (approve)` |
| "拒绝需求 123,原因是需求不完整" | `requirement_action (reject)` |
| "将任务 5069 关联到需求 123" | `link_tasks_to_requirement` |
### 文档操作
| 用户说 | 调用函数 |
|--------|----------|
| "为任务 5069 创建PRD文档" | `create-and-attach` |
| "查看任务 5069 的文档" | `get_task_document` |
| "更新任务 5069 的文档" | `update_task_document` |
| "导出任务 5069 的文档到文件" | `export_task_document_to_file` |
### 计时与聚焦
| 用户说 | 调用函数 |
|--------|----------|
| "开始计时任务 5069" | `start_task_with_timer` |
| "停止计时" | `stop_timer` |
| "查看今日任务" | `get_daily_focus_tasks` |
| "把任务 5069 添加到今日聚焦" | `add_daily_focus_task` |
| "生成今日工作报告" | `get_daily_work_report` |
---
## 注意事项 ## 注意事项
1. **项目ID默认值**: 大多数函数的 `projectId` 默认为 1 1. **项目ID默认值**: 大多数函数的 `projectId` 默认为 1
@@ -1013,229 +705,6 @@ go run /tmp/genhash.go
--- ---
## Google 日历集成配置
### 环境变量
生产环境需要配置以下环境变量(`.env` 文件):
```bash
GOOGLE_CLIENT_ID=<你的Client ID>.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-<你的Client Secret>
GOOGLE_PROJECT_ID=<你的项目ID>
GOOGLE_REDIRECT_URL=https://ai.pipexerp.com/api/v1/auth/google/callback
GOOGLE_CALENDAR_SCOPES=https://www.googleapis.com/auth/calendar
```
### 获取 Google OAuth 凭据
#### 1. 创建 Google Cloud 项目
1. 访问 [Google Cloud Console](https://console.cloud.google.com/)
2. 创建新项目或选择现有项目
3. 记录项目 ID
#### 2. 启用 API
1. 进入 **APIs & Services** → **Library**
2. 搜索并启用 **Google Calendar API**
#### 3. 配置 OAuth 同意屏幕
1. 进入 **APIs & Services** → **OAuth consent screen**
2. 选择 **External** → 创建
3. 填写应用名称、支持邮箱
4. 添加 Scopes:
- `https://www.googleapis.com/auth/calendar`
- `https://www.googleapis.com/auth/calendar.events`
5. 添加测试用户(开发阶段)
#### 4. 创建 OAuth 凭据
1. 进入 **APIs & Services** → **Credentials**
2. 点击 **+ CREATE CREDENTIALS** → **OAuth client ID**
3. 选择 **Web application**
4. 配置:
- **Name**: `AI-Proj Web Client`
- **Authorized JavaScript origins**: `https://ai.pipexerp.com`
- **Authorized redirect URIs**: `https://ai.pipexerp.com/api/v1/auth/google/callback`
5. 复制 Client ID 和 Client Secret
### 部署配置
```bash
# 更新生产环境配置
ssh tools_ai_proj "cd /home/ubuntu/apps/new-ai-proj && \
sed -i 's|GOOGLE_CLIENT_ID=.*|GOOGLE_CLIENT_ID=<CLIENT_ID>|' .env && \
sed -i 's|GOOGLE_CLIENT_SECRET=.*|GOOGLE_CLIENT_SECRET=<CLIENT_SECRET>|' .env && \
sed -i 's|GOOGLE_PROJECT_ID=.*|GOOGLE_PROJECT_ID=<PROJECT_ID>|' .env && \
docker restart ai_backend_prod"
```
### 数据库迁移
Google 日历集成需要以下表:
```sql
-- OAuth 状态表(CSRF 保护)
CREATE TABLE oauth_states (
id SERIAL PRIMARY KEY,
state VARCHAR(255) NOT NULL UNIQUE,
user_id INTEGER NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 项目日历配置表
-- 见 migrations/20260210_01_project_calendar.sql
-- 日历 Webhook 表
-- 见 migrations/20260211_01_calendar_webhooks.sql
```
### 当前配置(2026-02)
| 配置项 | 值 |
|--------|-----|
| Project ID | `project-bfa64769-1595-4c41-964` |
| Client ID | `78721641513-...apps.googleusercontent.com` |
| Redirect URL | `https://ai.pipexerp.com/api/v1/auth/google/callback` |
> **注意**: Client Secret 不在文档中记录,存储在生产环境 `.env` 文件中
---
## 十三、最佳实践与故障排查
### 13.1 创建需求的最佳实践
#### 问题:description 字段"请求数据格式错误"
**症状**:
```json
{
"code": "BAD_REQUEST",
"message": "请求数据格式错误"
}
```
**常见原因**:
| 原因 | 解决方案 |
|------|----------|
| **description 内容过长** | API 可能有字段长度限制,逐步测试找到上限 |
| **Markdown 特殊字符** | 检查是否有未转义的特殊字符(如反引号、引号) |
| **JSON 转义问题** | 确保 JSON 字符串正确转义(尤其是换行符、引号) |
| **多层嵌套的代码块** | Markdown 代码块中的反引号可能导致解析失败 |
**正确的调试流程**:
```python
# ❌ 错误做法:直接简化内容
"创建失败 → 简化 description → 成功"
问题:丢失了重要的需求细节
# ✅ 正确做法:找到根本原因
步骤 1: 测试空描述
create_requirement(description="")
→ 是否成功?
步骤 2: 测试纯文本
create_requirement(description="简单文本")
→ 是否成功?
步骤 3: 测试简单 Markdown
create_requirement(description="# 标题\n\n内容")
→ 是否成功?
步骤 4: 逐步增加复杂度
create_requirement(description="包含代码块的Markdown")
create_requirement(description="包含表格的Markdown")
create_requirement(description="完整的技术方案")
→ 找到临界点
步骤 5: 根据根因选择方案
- 如果是长度限制 → 分段或使用附件
- 如果是特殊字符 → 转义或替换
- 如果是 Markdown → 简化格式或转纯文本
```
**推荐方案**:
**方案 A:完整的需求 + 关联文档**
```typescript
// 1. 创建需求(简洁描述)
const req = await create_requirement({
title: "思源笔记增加 MCP 协议层支持",
description: "核心方案:创建独立的 siyuan-mcp-bridge 服务...",
projectId: 162
});
// 2. 创建详细的技术方案文档
const doc = await create_requirement_document({
requirementId: req.id,
title: "技术方案详细设计",
content: `
# 需求背景
...(完整的技术方案,3000+ 字)
# 架构设计
...
# 实施路线图
...
`
});
```
**方案 B:分段创建需求描述**
```typescript
// 1. 创建需求(基本信息)
const req = await create_requirement({
title: "思源笔记增加 MCP 协议层支持",
description: "简短描述",
projectId: 162
});
// 2. 更新需求(逐步添加内容)
await update_requirement({
id: req.id,
updates: {
description: description_part1 // 添加背景
}
});
await update_requirement({
id: req.id,
updates: {
description: description_part1 + description_part2 // 添加架构
}
});
```
#### 经验总结
**原则 1:业务优先于技术**
- ❌ 为了创建成功而牺牲需求完整性
- ✅ 找到技术方案来满足业务需求
**原则 2:追根溯源**
- ❌ 基于假设快速妥协("可能是太长了")
- ✅ 通过调试找到真正的错误原因
**原则 3:给用户选择权**
- ❌ 自作主张简化内容
- ✅ 询问用户的偏好和优先级
**原则 4:保留完整信息**
- ❌ 简化后的需求缺少关键信息
- ✅ 通过关联文档保留详细内容
### 13.2 需求描述的格式建议
**推荐的需求描述结构**(简洁版):
```markdown
## 核心方案 ## 核心方案
1. 关键点 1(一句话) 1. 关键点 1(一句话)
2. 关键点 2(一句话) 2. 关键点 2(一句话)
@@ -1246,139 +715,15 @@ await update_requirement({
- 第2层:组件名(职责) - 第2层:组件名(职责)
- 第3层:组件名(职责) - 第3层:组件名(职责)
## 实施周期
X周(阶段1 + 阶段2 + 阶段3)
```
**详细内容放在关联文档**: ## 详细内容
- 需求背景与业务价值
- 详细的技术方案
- 实施路线图与时间规划
- 风险评估与应对措施
- 验收标准
**Markdown 格式注意事项**: 以下章节已移到 [references/details.md](references/details.md):
```markdown - 六、每日聚焦
✅ 安全的格式: - 八、项目手册
- 简单的标题(# ## ###) - 十一、远程同步
- 无序列表(-) - 自然语言操作示例
- 有序列表(1. 2. 3.) - Google 日历集成配置
- 简单的表格 - 十三、最佳实践与故障排查
- 加粗(**text**) - 实施周期
⚠️ 可能有问题的格式:
- 三重反引号代码块(```)
- HTML 标签
- 复杂的嵌套列表
- 大量的特殊字符
- 超长的单行文本
```
### 13.3 更新需求的最佳实践
**使用 update_requirement 补充详细内容**:
```typescript
// 创建需求后,逐步更新描述
await update_requirement({
id: 857, // REQ-20260217-0001
updates: {
description: `
## 需求背景
### 当前问题
思源笔记仅提供 REST API 接口,AI 操作复杂...
### 业务价值
为思源笔记添加 MCP 协议层支持,使其能够...
## 技术方案
### 架构设计
...(详细内容)
`
}
});
```
**测试长度限制**:
```bash
# 逐步增加内容,找到上限
第1次: 200字 → 成功
第2次: 500字 → 成功
第3次: 1000字 → 成功
第4次: 2000字 → 成功
第5次: 5000字 → 失败 ← 找到上限
结论: description 字段最大约 3000-4000 字
```
### 13.4 故障排查清单
遇到 API 错误时,按以下步骤排查:
**步骤 1:分析错误信息**
```json
{
"code": "BAD_REQUEST",
"message": "请求数据格式错误"
}
```
- [ ] 错误码是什么?(BAD_REQUEST / VALIDATION_ERROR / AUTH_FAILED)
- [ ] 错误信息是否有具体提示?
- [ ] 是哪个字段导致的错误?
**步骤 2:检查参数格式**
- [ ] 必填参数是否都提供了?
- [ ] 参数类型是否正确?(number vs string)
- [ ] 枚举值是否在允许范围内?
- [ ] JSON 格式是否正确?
**步骤 3:简化测试**
- [ ] 尝试最小化的参数集
- [ ] 逐个添加可选参数
- [ ] 找到导致错误的具体参数
**步骤 4:查看后端日志**(如有权限)
```bash
# 生产环境日志
ssh tools_ai_proj "docker logs ai_backend_prod --tail 100"
# 开发环境日志
cd ~/coding/new-ai-proj/backend
tail -f logs/app.log
```
**步骤 5:参考类似的成功案例**
- [ ] 查看 SKILL.md 中的示例
- [ ] 查看之前成功的调用记录
- [ ] 对比参数差异
### 13.5 常见错误与解决方案
| 错误 | 可能原因 | 解决方案 |
|------|----------|----------|
| `BAD_REQUEST` | 参数格式错误 | 检查参数类型和 JSON 格式 |
| `VALIDATION_ERROR` | 参数验证失败 | 检查必填字段和枚举值 |
| `AUTH_FAILED` | 认证失败 | 检查 Token 是否过期 |
| `PERMISSION_DENIED` | 权限不足 | 检查用户角色和权限 |
| `NOT_FOUND` | 资源不存在 | 检查 ID 是否正确 |
| `INTERNAL_ERROR` | 服务器错误 | 查看后端日志排查 |
### 13.6 需求 description 长度测试记录
**测试日期**: 2026-02-17
| 长度 | 格式 | 结果 | 备注 |
|------|------|------|------|
| ~200字 | 简单文本 | ✅ 成功 | 核心方案3点 |
| ~3000字 | 完整 Markdown | ❌ 失败 | "请求数据格式错误" |
| 待测试 | 1000字 Markdown | ? | 需要进一步测试 |
| 待测试 | 2000字 Markdown | ? | 需要进一步测试 |
**建议**:
- 需求描述控制在 1000 字以内
- 详细内容使用关联文档
- 复杂格式改为纯文本
@@ -0,0 +1,664 @@
# skills 详细内容
## 六、每日聚焦
### 6.1 查看今日任务
**MCP 函数**: `mcp__ai-proj__get_daily_focus_tasks`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| date | string | ❌ | 日期 (YYYY-MM-DD) |
| status | string | ❌ | 状态筛选: active/completed/removed |
| priority | string | ❌ | 优先级筛选 |
| include_suggestions | boolean | ❌ | 是否包含智能推荐 |
### 6.2 添加任务到今日聚焦
**单个添加**: `mcp__ai-proj__add_daily_focus_task`
```
参数:
- task_id (number) - 任务ID
- priority (string) - 优先级: critical/high/medium/low
- estimated_duration_minutes (number) - 预估时长
- notes (string) - 备注
- focus_date (string) - 聚焦日期
```
**批量添加**: `mcp__ai-proj__batch_add_daily_focus_tasks`
```
参数:
- task_ids (array) - 任务ID列表
- priority (string) - 统一优先级
- notes (string) - 统一备注
- focus_date (string)
```
**快速添加当前任务**: `mcp__ai-proj__quick_add_current_task`
```
参数:
- priority (string) - 优先级,默认high
- notes (string)
功能: 将当前正在进行的任务添加到今日聚焦
```
### 6.3 管理今日任务
**更新**: `mcp__ai-proj__update_daily_focus_task`
```
参数:
- id (number) - Daily Focus任务ID
- priority, estimated_duration_minutes, notes
```
**移除**: `mcp__ai-proj__remove_daily_focus_task`
```
参数: id (number)
```
**完成**: `mcp__ai-proj__complete_daily_focus_task`
```
参数: id (number)
```
**聚焦并计时**: `mcp__ai-proj__focus_task_with_timer`
```
参数:
- daily_focus_task_id (number)
- timer_description (string)
```
**清理已完成**: `mcp__ai-proj__clear_completed_tasks`
```
参数:
- date (string) - 日期
- confirm (boolean) - 必须设为true
```
### 6.4 统计与推荐
**获取统计**: `mcp__ai-proj__get_daily_focus_stats`
```
参数:
- date (string)
- period (string) - 统计周期: daily/weekly/monthly
```
**获取任务推荐**: `mcp__ai-proj__get_task_recommendations`
```
参数:
- date (string)
- limit (number) - 推荐数量
- exclude_existing (boolean) - 排除已存在任务
返回: 基于优先级、截止日期等的智能推荐
```
---
## 八、项目手册
### 8.1 查看手册列表
**MCP 函数**: `mcp__ai-proj__list_manuals`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| projectId | number | ✅ | 项目ID |
| status | string | ❌ | 状态筛选: draft/published/archived |
| parentId | number | ❌ | 父级手册ID(获取子级列表) |
| search | string | ❌ | 搜索关键词(标题或描述) |
**自然语言示例**:
- "查看项目手册列表"
- "列出已发布的手册"
- "查看手册 5 的子章节"
### 8.2 创建手册
**MCP 函数**: `mcp__ai-proj__create_manual`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| projectId | number | ✅ | 项目ID |
| title | string | ✅ | 手册标题 |
| content | string | ❌ | 手册内容(Markdown格式) |
| description | string | ❌ | 手册简介 |
| parentId | number | ❌ | 父级手册ID(用于创建子章节) |
| status | string | ❌ | 状态: draft/published,默认draft |
**自然语言示例**:
- "创建项目手册: API 开发规范"
- "在手册 5 下创建子章节: 接口设计规范"
- "创建并发布手册: 前端开发规范"
### 8.3 获取手册详情
**MCP 函数**: `mcp__ai-proj__get_manual`
**参数**: `id` (number) - 手册ID
**返回**: 完整的手册内容(Markdown格式)
**自然语言示例**:
- "查看手册 5 的内容"
- "显示 API 开发规范手册"
### 8.4 更新手册
**MCP 函数**: `mcp__ai-proj__update_manual`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | ✅ | 手册ID |
| title | string | ❌ | 新标题 |
| content | string | ❌ | 新内容(Markdown格式) |
| description | string | ❌ | 新简介 |
| status | string | ❌ | 新状态: draft/published/archived |
**自然语言示例**:
- "更新手册 5 的内容"
- "发布手册 5"
- "归档手册 5"
### 8.5 搜索手册
**MCP 函数**: `mcp__ai-proj__search_manuals`
**参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| projectId | number | ✅ | 项目ID |
| query | string | ✅ | 搜索关键词 |
**自然语言示例**:
- "搜索项目手册中包含 API 的文档"
- "查找数据库相关的手册"
---
## 十一、远程同步
### 11.1 任务同步
**同步任务**: `mcp__ai-proj__sync_task`
```
参数:
- taskId (number)
- direction (string) - to_remote/from_remote
- includeDocument (boolean) - 是否同步文档
- includeChildren (boolean) - 是否同步子任务
```
**批量同步到远程**: `mcp__ai-proj__batch_sync_tasks_to_remote`
```
参数:
- taskIds (array)
- includeDocuments (boolean)
- includeChildren (boolean)
```
**获取同步状态**: `mcp__ai-proj__get_task_sync_status`
```
参数: taskId (number)
```
**获取远程同步状态**: `mcp__ai-proj__get_remote_sync_status`
```
参数: taskId (number)
返回: 比较本地和远程版本差异
```
**通过UUID获取任务**: `mcp__ai-proj__get_task_by_uuid`
```
参数: uuid (string)
用途: 跨库同步场景
```
### 11.2 需求同步
**同步需求到远程**: `mcp__ai-proj__sync_requirement_to_remote`
```
参数:
- requirementId (number)
- includeHistory (boolean)
- includeComments (boolean)
```
**从远程同步需求**: `mcp__ai-proj__sync_requirement_from_remote`
```
参数:
- remoteRequirementId (number)
- includeHistory (boolean)
- includeComments (boolean)
```
**批量同步需求**: `mcp__ai-proj__batch_sync_requirements_to_remote`
```
参数:
- requirementIds (array)
- includeComments (boolean)
```
**同步需求-任务关联**: `mcp__ai-proj__sync_requirement_task_links`
```
参数:
- linkUUIDs (array) - 关联关系的UUID列表
- forceUpdate (boolean)
```
**同步任务的需求关联**: `mcp__ai-proj__sync_task_links_to_remote`
```
参数: taskId (number)
```
---
## 自然语言操作示例
### 任务管理
| 用户说 | 调用函数 |
|--------|----------|
| "创建一个任务叫做登录功能开发" | `create_task` |
| "查看所有进行中的任务" | `list_tasks` |
| "开始任务 5069" | `start_task` |
| "完成任务 5069" | `complete_task` |
| "暂停任务 5069" | `pause_task` |
| "把任务 5069 移到项目2" | `move_task` |
| "给任务 5069 创建子任务: 数据库设计" | `create_subtask` |
### 需求管理
| 用户说 | 调用函数 |
|--------|----------|
| "创建需求: 用户权限管理" | `create_requirement` |
| "查看待审核的需求" | `list_requirements` |
| "提交需求 123 评审" | `requirement_action (submit)` |
| "批准需求 123" | `requirement_action (approve)` |
| "拒绝需求 123,原因是需求不完整" | `requirement_action (reject)` |
| "将任务 5069 关联到需求 123" | `link_tasks_to_requirement` |
### 文档操作
| 用户说 | 调用函数 |
|--------|----------|
| "为任务 5069 创建PRD文档" | `create-and-attach` |
| "查看任务 5069 的文档" | `get_task_document` |
| "更新任务 5069 的文档" | `update_task_document` |
| "导出任务 5069 的文档到文件" | `export_task_document_to_file` |
### 计时与聚焦
| 用户说 | 调用函数 |
|--------|----------|
| "开始计时任务 5069" | `start_task_with_timer` |
| "停止计时" | `stop_timer` |
| "查看今日任务" | `get_daily_focus_tasks` |
| "把任务 5069 添加到今日聚焦" | `add_daily_focus_task` |
| "生成今日工作报告" | `get_daily_work_report` |
---
## Google 日历集成配置
### 环境变量
生产环境需要配置以下环境变量(`.env` 文件):
```bash
GOOGLE_CLIENT_ID=<你的Client ID>.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-<你的Client Secret>
GOOGLE_PROJECT_ID=<你的项目ID>
GOOGLE_REDIRECT_URL=https://ai.pipexerp.com/api/v1/auth/google/callback
GOOGLE_CALENDAR_SCOPES=https://www.googleapis.com/auth/calendar
```
### 获取 Google OAuth 凭据
#### 1. 创建 Google Cloud 项目
1. 访问 [Google Cloud Console](https://console.cloud.google.com/)
2. 创建新项目或选择现有项目
3. 记录项目 ID
#### 2. 启用 API
1. 进入 **APIs & Services** → **Library**
2. 搜索并启用 **Google Calendar API**
#### 3. 配置 OAuth 同意屏幕
1. 进入 **APIs & Services** → **OAuth consent screen**
2. 选择 **External** → 创建
3. 填写应用名称、支持邮箱
4. 添加 Scopes:
- `https://www.googleapis.com/auth/calendar`
- `https://www.googleapis.com/auth/calendar.events`
5. 添加测试用户(开发阶段)
#### 4. 创建 OAuth 凭据
1. 进入 **APIs & Services** → **Credentials**
2. 点击 **+ CREATE CREDENTIALS** → **OAuth client ID**
3. 选择 **Web application**
4. 配置:
- **Name**: `AI-Proj Web Client`
- **Authorized JavaScript origins**: `https://ai.pipexerp.com`
- **Authorized redirect URIs**: `https://ai.pipexerp.com/api/v1/auth/google/callback`
5. 复制 Client ID 和 Client Secret
### 部署配置
```bash
# 更新生产环境配置
ssh tools_ai_proj "cd /home/ubuntu/apps/new-ai-proj && \
sed -i 's|GOOGLE_CLIENT_ID=.*|GOOGLE_CLIENT_ID=<CLIENT_ID>|' .env && \
sed -i 's|GOOGLE_CLIENT_SECRET=.*|GOOGLE_CLIENT_SECRET=<CLIENT_SECRET>|' .env && \
sed -i 's|GOOGLE_PROJECT_ID=.*|GOOGLE_PROJECT_ID=<PROJECT_ID>|' .env && \
docker restart ai_backend_prod"
```
### 数据库迁移
Google 日历集成需要以下表:
```sql
-- OAuth 状态表(CSRF 保护)
CREATE TABLE oauth_states (
id SERIAL PRIMARY KEY,
state VARCHAR(255) NOT NULL UNIQUE,
user_id INTEGER NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 项目日历配置表
-- 见 migrations/20260210_01_project_calendar.sql
-- 日历 Webhook 表
-- 见 migrations/20260211_01_calendar_webhooks.sql
```
### 当前配置(2026-02)
| 配置项 | 值 |
|--------|-----|
| Project ID | `project-bfa64769-1595-4c41-964` |
| Client ID | `78721641513-...apps.googleusercontent.com` |
| Redirect URL | `https://ai.pipexerp.com/api/v1/auth/google/callback` |
> **注意**: Client Secret 不在文档中记录,存储在生产环境 `.env` 文件中
---
## 十三、最佳实践与故障排查
### 13.1 创建需求的最佳实践
#### 问题:description 字段"请求数据格式错误"
**症状**:
```json
{
"code": "BAD_REQUEST",
"message": "请求数据格式错误"
}
```
**常见原因**:
| 原因 | 解决方案 |
|------|----------|
| **description 内容过长** | API 可能有字段长度限制,逐步测试找到上限 |
| **Markdown 特殊字符** | 检查是否有未转义的特殊字符(如反引号、引号) |
| **JSON 转义问题** | 确保 JSON 字符串正确转义(尤其是换行符、引号) |
| **多层嵌套的代码块** | Markdown 代码块中的反引号可能导致解析失败 |
**正确的调试流程**:
```python
# ❌ 错误做法:直接简化内容
"创建失败 → 简化 description → 成功"
问题:丢失了重要的需求细节
# ✅ 正确做法:找到根本原因
步骤 1: 测试空描述
create_requirement(description="")
→ 是否成功?
步骤 2: 测试纯文本
create_requirement(description="简单文本")
→ 是否成功?
步骤 3: 测试简单 Markdown
create_requirement(description="# 标题\n\n内容")
→ 是否成功?
步骤 4: 逐步增加复杂度
create_requirement(description="包含代码块的Markdown")
create_requirement(description="包含表格的Markdown")
create_requirement(description="完整的技术方案")
→ 找到临界点
步骤 5: 根据根因选择方案
- 如果是长度限制 → 分段或使用附件
- 如果是特殊字符 → 转义或替换
- 如果是 Markdown → 简化格式或转纯文本
```
**推荐方案**:
**方案 A:完整的需求 + 关联文档**
```typescript
// 1. 创建需求(简洁描述)
const req = await create_requirement({
title: "思源笔记增加 MCP 协议层支持",
description: "核心方案:创建独立的 siyuan-mcp-bridge 服务...",
projectId: 162
});
// 2. 创建详细的技术方案文档
const doc = await create_requirement_document({
requirementId: req.id,
title: "技术方案详细设计",
content: `
# 需求背景
...(完整的技术方案,3000+ 字)
# 架构设计
...
# 实施路线图
...
`
});
```
**方案 B:分段创建需求描述**
```typescript
// 1. 创建需求(基本信息)
const req = await create_requirement({
title: "思源笔记增加 MCP 协议层支持",
description: "简短描述",
projectId: 162
});
// 2. 更新需求(逐步添加内容)
await update_requirement({
id: req.id,
updates: {
description: description_part1 // 添加背景
}
});
await update_requirement({
id: req.id,
updates: {
description: description_part1 + description_part2 // 添加架构
}
});
```
#### 经验总结
**原则 1:业务优先于技术**
- ❌ 为了创建成功而牺牲需求完整性
- ✅ 找到技术方案来满足业务需求
**原则 2:追根溯源**
- ❌ 基于假设快速妥协("可能是太长了")
- ✅ 通过调试找到真正的错误原因
**原则 3:给用户选择权**
- ❌ 自作主张简化内容
- ✅ 询问用户的偏好和优先级
**原则 4:保留完整信息**
- ❌ 简化后的需求缺少关键信息
- ✅ 通过关联文档保留详细内容
### 13.2 需求描述的格式建议
**推荐的需求描述结构**(简洁版):
```markdown
## 实施周期
X周(阶段1 + 阶段2 + 阶段3)
```
**详细内容放在关联文档**:
- 需求背景与业务价值
- 详细的技术方案
- 实施路线图与时间规划
- 风险评估与应对措施
- 验收标准
**Markdown 格式注意事项**:
```markdown
✅ 安全的格式:
- 简单的标题(# ## ###)
- 无序列表(-)
- 有序列表(1. 2. 3.)
- 简单的表格
- 加粗(**text**)
⚠️ 可能有问题的格式:
- 三重反引号代码块(```)
- HTML 标签
- 复杂的嵌套列表
- 大量的特殊字符
- 超长的单行文本
```
### 13.3 更新需求的最佳实践
**使用 update_requirement 补充详细内容**:
```typescript
// 创建需求后,逐步更新描述
await update_requirement({
id: 857, // REQ-20260217-0001
updates: {
description: `
## 需求背景
### 当前问题
思源笔记仅提供 REST API 接口,AI 操作复杂...
### 业务价值
为思源笔记添加 MCP 协议层支持,使其能够...
## 技术方案
### 架构设计
...(详细内容)
`
}
});
```
**测试长度限制**:
```bash
# 逐步增加内容,找到上限
第1次: 200字 → 成功
第2次: 500字 → 成功
第3次: 1000字 → 成功
第4次: 2000字 → 成功
第5次: 5000字 → 失败 ← 找到上限
结论: description 字段最大约 3000-4000 字
```
### 13.4 故障排查清单
遇到 API 错误时,按以下步骤排查:
**步骤 1:分析错误信息**
```json
{
"code": "BAD_REQUEST",
"message": "请求数据格式错误"
}
```
- [ ] 错误码是什么?(BAD_REQUEST / VALIDATION_ERROR / AUTH_FAILED)
- [ ] 错误信息是否有具体提示?
- [ ] 是哪个字段导致的错误?
**步骤 2:检查参数格式**
- [ ] 必填参数是否都提供了?
- [ ] 参数类型是否正确?(number vs string)
- [ ] 枚举值是否在允许范围内?
- [ ] JSON 格式是否正确?
**步骤 3:简化测试**
- [ ] 尝试最小化的参数集
- [ ] 逐个添加可选参数
- [ ] 找到导致错误的具体参数
**步骤 4:查看后端日志**(如有权限)
```bash
# 生产环境日志
ssh tools_ai_proj "docker logs ai_backend_prod --tail 100"
# 开发环境日志
cd ~/coding/new-ai-proj/backend
tail -f logs/app.log
```
**步骤 5:参考类似的成功案例**
- [ ] 查看 SKILL.md 中的示例
- [ ] 查看之前成功的调用记录
- [ ] 对比参数差异
### 13.5 常见错误与解决方案
| 错误 | 可能原因 | 解决方案 |
|------|----------|----------|
| `BAD_REQUEST` | 参数格式错误 | 检查参数类型和 JSON 格式 |
| `VALIDATION_ERROR` | 参数验证失败 | 检查必填字段和枚举值 |
| `AUTH_FAILED` | 认证失败 | 检查 Token 是否过期 |
| `PERMISSION_DENIED` | 权限不足 | 检查用户角色和权限 |
| `NOT_FOUND` | 资源不存在 | 检查 ID 是否正确 |
| `INTERNAL_ERROR` | 服务器错误 | 查看后端日志排查 |
### 13.6 需求 description 长度测试记录
**测试日期**: 2026-02-17
| 长度 | 格式 | 结果 | 备注 |
|------|------|------|------|
| ~200字 | 简单文本 | ✅ 成功 | 核心方案3点 |
| ~3000字 | 完整 Markdown | ❌ 失败 | "请求数据格式错误" |
| 待测试 | 1000字 Markdown | ? | 需要进一步测试 |
| 待测试 | 2000字 Markdown | ? | 需要进一步测试 |
**建议**:
- 需求描述控制在 1000 字以内
- 详细内容使用关联文档
- 复杂格式改为纯文本
@@ -7,5 +7,6 @@
}, },
"install_name": "publish", "install_name": "publish",
"install_type": "skill", "install_type": "skill",
"dir_category": "core" "dir_category": "core",
"invocation": "user-invoked"
} }
+4 -1
View File
@@ -1,7 +1,10 @@
--- ---
name: publish name: publish
description: 发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。 description: 发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。
user_invocable: true metadata:
user_invocable: true
invocation: user-invoked
disable-model-invocation: true
--- ---
# publish - 技能市场发布与通知 # publish - 技能市场发布与通知
@@ -0,0 +1,13 @@
{
"name": "skill-creator-plugin",
"description": "团队技能创建与维护的标准流程:收录门槛、分桶生命周期、调用模式、evals、四段式文档、路由回改。",
"version": "0.1.0",
"author": {
"name": "qiudl"
},
"install_name": "skill-creator",
"install_type": "skill",
"dir_category": "core",
"status": "beta",
"invocation": "user-invoked"
}
@@ -0,0 +1,127 @@
---
name: skill-creator
description: 团队技能的标准流程:收录门槛、分桶生命周期、调用模式、SKILL.md 规范、evals 全量、四段式文档、路由回改。当要新建技能、大改现有技能、或评审一个技能是否合格时使用。
metadata:
invocation: user-invoked
owner: qiudl
disable-model-invocation: true
---
# 技能创建(团队标准流程)
## 0. 一句话
先有真实失败,再写技能;没有 evals 的技能不算完成;**技能目录是预算,不是收藏夹**。
## 1. 何时用
- 新建技能
- 大改现有技能(改 description、改核心流程、改门禁)
- 评审别人提上来的技能
- 判断某个想法「该不该做成技能」
何时不用:只改错别字/格式 → 直接改;一次性个人偏好 → 放个人技能,不进团队。
## 2. 收录门槛(先过这一关,再谈怎么写)
新技能**默认拒绝**,只有同时满足两条才立项:
1. **真实失败**:能说出在哪个真实任务/会话里出了问题——跑了什么、期望什么、实际什么。假想改进(「这样会更好」)不算。
2. **组合不出来**:用现有技能组合做不到;能组合的,就改现有技能,不新增。
被拒的想法记进仓库 `.out-of-scope/<概念>.md`,写清理由,避免下次重复讨论。
## 3. 分桶与生命周期
| 桶 | 含义 | 是否安装 | 是否进 README / plugin.json |
| --- | --- | --- | --- |
| `promoted` | 已验证、日常在用 | 默认安装 | 必须出现 |
| `beta` | 公开试跑,收反馈 | 需 `--include-beta` | 不进默认清单 |
| `deprecated` | 不再用 | 不安装 | 留文档,标弃用 |
- 新技能一律先进 `beta`,跑够真实使用 + evals 通过才 `promoted`。
- 只有 `promoted` 能进模型目录;beta 不占目录预算。
## 4. 调用模式(省目录 token 的关键)
每个技能二选一,写进 frontmatter:
- **`model-invoked`**(默认):进模型目录,agent 自己判断何时用。
- **`user-invoked`**:只由人下命令触发,**不进模型目录**;必须同时写 `disable-model-invocation: true` 和 `metadata.invocation: user-invoked`。
判定口径:这件事是不是「人下命令才开始」?是 → user-invoked。典型:publish、retro、triage、发布 runbook、建技能。
## 5. SKILL.md 硬约束(Agent Skills 规范)
- 目录:`SKILL.md` + 可选 `scripts/`、`references/`、`assets/`
- frontmatter 必填:`name`(小写字母/数字/连字符,≤64)、`description`(≤1024,写清「做什么 + 何时用」)
- 可选:`license`、`compatibility`、`metadata`、`allowed-tools`
- **非标准字段**一律进 `metadata`
- 正文 ≤20KB(约 5000 token),细节拆 `references/`
- 只写 agent 不知道的东西:项目约定、边界、坑;不写通用常识
## 6. 正文结构模板
```
# 名称
## 0. 一句话
## 1. 何时用 / 何时不用
## 2. 步骤(可执行、有顺序)
## 3. 硬约束与红线
## 4. 常见坑(gotchas)
## 5. 验证清单
## 6. 参考(references/ 链接)
```
## 7. evals(强制、全量)
- 路径:`evals/evals.json`;至少 3 条:正常路径 / 边界输入 / 失败恢复
- 跑「有技能 / 无技能」对比,结果存 `iteration-<N>/`,按 rubric 打分,PR 附结果
## 8. 四段式文档(每个 promoted 技能一篇)
`docs/skills/<name>.md`,四节固定顺序:
1. **What it does**
2. **When to reach for it**
3. **Common questions**
4. **It's working if**(必须给可验证证据,不许写「感觉对了」)
beta 不写文档;弃用保留文档并标 archived。
## 9. 路由回改(硬规则)
所有技能增、删、改名、改行为后,**必须同步回改入口技能 `ai-proj-helper-entry` 和 `catalog.json`**。一个没提到新技能、或还在路由到已删技能的路由器,就是假的路由器。
## 10. 质量门禁(提 PR 前逐条打勾)
- [ ] 过了收录门槛(真实失败 + 组合不出来)
- [ ] 桶与调用模式已标,`beta` 不得出现在默认清单
- [ ] `name`/`description` 合规,description 含「何时用」
- [ ] 正文 ≤20KB,细节已拆 references
- [ ] evals ≥3 条,有「有/无技能」对比
- [ ] 四段式文档已写(promoted 才需要)
- [ ] 入口路由与 `catalog.json` 已回改
- [ ] `plugin.json` 已登记 `install_name`/`install_type`/`dir_category`/`status`/版本号
- [ ] `python3 scripts/validate-skills.py` 通过
- [ ] `./scripts/scan-skill.sh` 无高危项
- [ ] 版本号与 CHANGELOG 已更新
- [ ] 无绝对路径、无凭据、无内网地址
## 11. 反模式
- 一次写 800 行「大全」,什么都想覆盖
- description 写成「各种任务都能用」
- 没有 evals 就提 PR;没有四段式文档就 promoted
- 直接复制外部技能,不改写成我们的 REQ/ai-proj 语境
- 把临时排障步骤固化成团队技能(这类放 personal)
- 技能改名后忘了回改路由
## 12. 评审别人技能时看什么
1. 收录门槛过没过(有没有真实失败)
2. description 能不能让模型判断「何时用」
3. 桶与调用模式是否正确(该 user-invoked 的别占目录)
4. 和现有技能是否重复(先查 `catalog.json`)
5. evals 是否覆盖失败路径、文档是否有「It's working if」
6. 是否触碰红线(自审批、绕过门禁、绝对路径、凭据)
@@ -0,0 +1,24 @@
{
"skill_name": "skill-creator",
"skill_version": "0.1.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "我想加一个新技能,用来在每次部署后自动跑一遍冒烟",
"expected_output": "先问真实失败场景(哪次部署出过问题、跑了什么、期望什么),再判断现有 req-lookback/req-audit 能否组合;给出 bucket/调用模式/evals 计划"
},
{
"id": 2,
"kind": "edge_case",
"prompt": "直接帮我把这个想法写成 SKILL.md:一个能自动优化代码风格的技能",
"expected_output": "拒绝直接写,指出没有真实失败证据,要求先给案例;若无证据则建议放个人技能"
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "技能有 evals 但 validate-skills.py 报了 name 不合规,怎么处理",
"expected_output": "说明 name 规则(小写/数字/连字符、≤64),给出改名步骤,并提示要同步 install_name、catalog、入口路由"
}
]
}
@@ -404,3 +404,14 @@ ssh melbourne "cd /Users/coolbuy-dev/coding/new-ai-proj && \
- [OpenAI Swarm 文档](https://github.com/openai/swarm) - [OpenAI Swarm 文档](https://github.com/openai/swarm)
- [Multi-Agent Systems 设计模式](https://arxiv.org/abs/2308.00352) - [Multi-Agent Systems 设计模式](https://arxiv.org/abs/2308.00352)
- [Claude Code Skills 文档](https://docs.anthropic.com/claude-code/skills) - [Claude Code Skills 文档](https://docs.anthropic.com/claude-code/skills)
## 并行编排流程
把大任务拆给多个子 agent 时按这个顺序:
1. **先划写入边界**:每个子 agent 拥有互不重叠的目录/文件;共享文件(锁文件、入口文件)由编排者最后统一改。
2. **子 agent 必须新上下文**:不要 fork 父上下文,否则它继承了本该甩掉的包袱。
3. **每个子任务给验收标准 + 回报格式**:三句话(结果 / 证据路径 / 下一步)。
4. **子 agent 不做合并与发布**:只交分支或补丁,合并由编排者做。
5. **汇总时先跑集成验证**,再回报;冲突由编排者解,不让子 agent 互改。
6. 两个子 agent 不得写同一文件;不确定时先串行。
+2 -1
View File
@@ -1,7 +1,8 @@
--- ---
name: ai-chat name: ai-chat
description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。 description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。
arguments: <subcommand> [args] metadata:
arguments: <subcommand> [args]
--- ---
# AI Chat Skill # AI Chat Skill
@@ -7,5 +7,6 @@
}, },
"install_name": "ai-proj-cicd-release", "install_name": "ai-proj-cicd-release",
"install_type": "skill", "install_type": "skill",
"dir_category": "dev" "dir_category": "dev",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: ai-proj-cicd-release name: ai-proj-cicd-release
description: Execute and audit the AI-Proj service CI/CD flow across Gitea gates, immutable image builds, staging verification, approved production release, rollback, and evidence capture. Use for AI-Proj CI status, release PRs, staging or production deployments, failed-release diagnosis, deployed commit/schema verification, and delivery-chain repair. Do not use for unrelated repositories or macOS application packaging. description: Execute and audit the AI-Proj service CI/CD flow across Gitea gates, immutable image builds, staging verification, approved production release, rollback, and evidence capture. Use for AI-Proj CI status, release PRs, staging or production deployments, failed-release diagnosis, deployed commit/schema verification, and delivery-chain repair. Do not use for unrelated repositories or macOS application packaging.
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# AI-Proj CI/CD release # AI-Proj CI/CD release
@@ -7,5 +7,6 @@
}, },
"install_name": "ai-proj-macos-release", "install_name": "ai-proj-macos-release",
"install_type": "skill", "install_type": "skill",
"dir_category": "dev" "dir_category": "dev",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: ai-proj-macos-release name: ai-proj-macos-release
description: Build, sign, notarize, publish, and verify the AI-Proj macOS Apple Silicon package through the repository's release chain. Use when asked to release, republish, update, or repair the downloadable macOS application, including Gatekeeper failures and download-manifest updates. Do not use for service deployments or unrelated applications. description: Build, sign, notarize, publish, and verify the AI-Proj macOS Apple Silicon package through the repository's release chain. Use when asked to release, republish, update, or repair the downloadable macOS application, including Gatekeeper failures and download-manifest updates. Do not use for service deployments or unrelated applications.
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# AI-Proj macOS release # AI-Proj macOS release
@@ -7,5 +7,6 @@
}, },
"install_name": "dev-android", "install_name": "dev-android",
"install_type": "skill", "install_type": "skill",
"dir_category": "dev" "dir_category": "dev",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: dev-android name: dev-android
description: Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。 description: Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# Android 开发插件 (dev-android) # Android 开发插件 (dev-android)
@@ -608,3 +608,55 @@ fi
### 非阻断原则 ### 非阻断原则
架构信息缺失时**仅警告不阻止**。用户仍可继续,但会被告知"生成的建议可能不够准确"。 架构信息缺失时**仅警告不阻止**。用户仍可继续,但会被告知"生成的建议可能不够准确"。
---
## Step 0.5:Shell 环境自检(node/npm command not found 先看这里)
**在任何 `npm` / `pnpm` / `node` 命令之前,先确认解释器可解析。** 报 `command not found` 时
**不要**下结论"当前环境没有 shell",也**不要**去找"给 agent 配 shell"的开关(DSH 没有这个开关)。
### 根因(不要重复侦察;已在 macOS + Slark/DSH 上定位到代码)
| # | 事实 |
|---|------|
| 1 | DSH 的 bash 工具(`@deepseek-ai/dsh-bash-local`)一律以 **`bash -c`(非登录、非交互)** 执行 → **不读** `~/.zprofile` / `~/.zshrc` / `~/.profile` |
| 2 | 子进程环境 = **宿主进程环境** − 凭据类变量 − `DSH_*`(`@deepseek-ai/dsh-subprocess`)→ **PATH 不被清洗**,它等于启动宿主那个进程的 PATH |
| 3 | 宿主由 GUI 应用(Slark.app)拉起 → 继承 macOS GUI 的最小 PATH(`/usr/local/bin:/bin:/usr/bin:...`)→ node 不在里面 |
| 4 | `HOME` **未设置**(不是空值);`USER` / `TMPDIR` / `LANG` 同样未设置 |
| 5 | `@deepseek-ai/dsh-shell-env` 只注册/注入 **`DSH_*`** 变量,`PATH` 是保留键 → 改 profile/`cordis.patch.yml` 加 PATH **无效** |
### 正确做法(两行 export,别多一步)
```bash
export HOME="/Users/johnq" # 必须补:git 等直接读 $HOME
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:$PATH" # node/npm/pnpm
cd <项目> && npm install && npm run build
```
**为什么 `HOME` 必须补**(实测):
- `~` 在 bash 里靠 passwd 查表展开,所以 `ls ~`、`cd ~/x` 仍能用 → **容易误判环境正常**;
- 直接读 `$HOME` 的会崩:`git` → `fatal: $HOME not set`(`status` / `commit` / `config --global` 全废);
带 `set -u` 的脚本 → `HOME: unbound variable`(例:ai-proj-helper 的 `install-skills.sh`);
- node 侧多数无事(`os.homedir()` 走 passwd 兜底,只有 `process.env.HOME` 是 `undefined`)
→ **「node 能跑」≠ 环境完整**,别据此跳过 `HOME`。
- **本机 node 必须用这一支**(定义在 `~/.zprofile`,对齐项目 `engines.node`):
`/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin/node` = v22.22.3,npm 10.9.8。
- `/opt/homebrew/bin/node` 是**另一支 v26**,不要混用;`/usr/local/bin/node` 不存在。
- 备选①:`/bin/zsh -lic '<命令>'`(登录 zsh 会读 `~/.zprofile`)。
- 备选②:绝对路径直调 `/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin/node build.mjs`。
- 路径变了先 `ls -d /Users/johnq/.local/node-v*-darwin-arm64/bin` 重新取**真实版本号**,不要猜。
### 交付纪律(配合「Step 0:验证优先」)
- **禁止**把"没有 shell / 不能构建"写成交付结论:bash 工具是好的,缺的只是 `HOME` + `PATH`。
正确表述是"我未执行构建(缺 node PATH,已给用户补 PATH 后的构建命令)"。
- 构建/测试没跑就说没跑(`未经构建与实机验证`),**不要**用"构建配置已就绪"之类中间信号冒充验收。
- 校验环境的最快方式(输出直接贴进交付):
```bash
export HOME="/Users/johnq"; export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:$PATH"
node -v && npm -v && git status --short | head -3
```
+7 -183
View File
@@ -2,9 +2,6 @@
name: dev-deploy name: dev-deploy
description: 应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。 description: 应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。
--- ---
# 应用部署 Skill (dev-deploy)
## 概述 ## 概述
管理应用从构建到发布的完整部署流程,支持多平台: 管理应用从构建到发布的完整部署流程,支持多平台:
@@ -558,186 +555,6 @@ preflight_docker() {
--- ---
## 回滚策略
### iOS TestFlight 回滚
TestFlight **无法真正回滚**已安装的版本,但有以下应急手段:
| 手段 | 说明 | API |
|------|------|-----|
| 停止分发 | 将 build 从测试中移除,用户不再收到更新 | `PATCH /v1/builds/{id}` 设置 `expired: true` |
| 过期 build | 强制过期有问题的 build | 同上 |
| 紧急热修 | 构建新版本覆盖上线 | 常规部署流程 |
```bash
# 通过 ASC API 停止分发某个 build
curl -X PATCH "https://api.appstoreconnect.apple.com/v1/builds/$BUILD_ID" \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"data":{"type":"builds","id":"'$BUILD_ID'","attributes":{"expired":true}}}'
```
### Docker 回滚
Docker 回滚相对简单,拉取上一个正常版本的镜像重新部署即可:
```bash
# 1. 确定上一个正常的 tag
PREVIOUS_TAG=<previous-good-tag>
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
# 2. 在服务器上回滚
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
cd /opt/xiaoqu/production # 或 /opt/xiaoqu/staging
export IMAGE_TAG=$PREVIOUS_TAG
docker compose pull
docker compose up -d
"
# 3. 验证回滚成功
curl -sf http://39.104.87.246/health && echo 'Rollback OK'
```
### 数据库回滚注意事项
| 场景 | 策略 |
|------|------|
| 可逆 migration(加列、加表) | 部署回滚后数据库无需回滚,旧代码忽略新列 |
| 不可逆 migration(删列、改类型) | **必须先回滚 migration 再回滚代码**,否则旧代码报错 |
| 数据 migration | 评估是否需要补偿脚本,建议 migration 前做备份快照 |
```bash
# 数据库 migration 回滚示例(如果使用 golang-migrate)
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
docker compose exec gateway migrate -path /migrations -database \$DATABASE_URL down 1
"
```
---
## 部署监控
### Post-deploy 健康检查模式
```bash
# 通用部署后验证函数
post_deploy_verify() {
local url=$1
local max_retries=${2:-5}
local interval=${3:-10}
echo "Verifying deployment at $url ..."
for i in $(seq 1 $max_retries); do
local status
status=$(curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
if [ "$status" = "200" ]; then
echo "Health check passed (attempt $i/$max_retries)"
return 0
fi
echo "Attempt $i/$max_retries: status=$status, retrying in ${interval}s..."
sleep $interval
done
echo "Health check FAILED after $max_retries attempts"
return 1
}
# 使用示例
post_deploy_verify "http://39.104.87.246/health" 5 10
```
### 飞书通知模板
部署完成后通过飞书 Webhook 发送通知:
```bash
# 部署成功通知
send_feishu_deploy_notification() {
local env=$1 # staging / production
local version=$2 # 版本号或 tag
local status=$3 # success / failure
local detail=$4 # 额外说明
local WEBHOOK_URL="<飞书群 Webhook 地址>"
if [ "$status" = "success" ]; then
local color="green"
local emoji="✅"
else
local color="red"
local emoji="❌"
fi
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "interactive",
"card": {
"header": {
"title": {"tag": "plain_text", "content": "'"$emoji"' 部署通知 - '"$env"'"},
"template": "'"$color"'"
},
"elements": [
{"tag": "div", "text": {"tag": "lark_md", "content": "**环境**: '"$env"'\n**版本**: '"$version"'\n**状态**: '"$status"'\n**时间**: '"$(date '+%Y-%m-%d %H:%M:%S')"'\n**详情**: '"$detail"'"}}
]
}
}'
}
# 使用示例
send_feishu_deploy_notification "production" "v1.2.3" "success" "Gateway + Web 部署完成"
send_feishu_deploy_notification "staging" "abc12345" "failure" "Health check 超时"
```
### iOS TestFlight 构建状态监控
通过 ASC API 持续监控 build 处理状态:
```bash
# 监控 TestFlight build 处理状态
monitor_testflight_build() {
local build_id=$1
local jwt_token=$2
local max_wait=600 # 最长等待 10 分钟
local elapsed=0
while [ $elapsed -lt $max_wait ]; do
local response
response=$(curl -s "https://api.appstoreconnect.apple.com/v1/builds/$build_id" \
-H "Authorization: Bearer $jwt_token")
local state
state=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['attributes']['processingState'])" 2>/dev/null)
echo "[$(date '+%H:%M:%S')] Build $build_id: $state"
case "$state" in
VALID)
echo "Build 处理完成,可用于测试"
return 0
;;
FAILED|INVALID)
echo "Build 处理失败: $state"
return 1
;;
PROCESSING)
sleep 30
((elapsed+=30))
;;
*)
sleep 15
((elapsed+=15))
;;
esac
done
echo "Build 处理超时(${max_wait}s)"
return 1
}
```
---
## 与需求工作流集成 ## 与需求工作流集成
部署完成后更新需求状态: 部署完成后更新需求状态:
@@ -774,3 +591,10 @@ ai-proj task append-doc --id <task_id> --content "部署记录..."
| 10 | App Store 图标为空 | 需将 build 关联到 App Store 版本(PATCH relationships/build) | | 10 | App Store 图标为空 | 需将 build 关联到 App Store 版本(PATCH relationships/build) |
| 11 | SSH 长连接断开 | xcodebuild 3-4 分钟无输出,Tailscale 断连。用 nohup 后台执行 | | 11 | SSH 长连接断开 | xcodebuild 3-4 分钟无输出,Tailscale 断连。用 nohup 后台执行 |
| 12 | xcodegen 后 cwd 错乱 | `cd ios && xcodegen && cd ..` 失败时不回退。用 subshell `(cd ios && xcodegen)` | | 12 | xcodegen 后 cwd 错乱 | `cd ios && xcodegen && cd ..` 失败时不回退。用 subshell `(cd ios && xcodegen)` |
## 详细内容
以下章节已移到 [references/details.md](references/details.md):
- 回滚策略
- 部署监控
@@ -0,0 +1,182 @@
# skills 详细内容
## 回滚策略
### iOS TestFlight 回滚
TestFlight **无法真正回滚**已安装的版本,但有以下应急手段:
| 手段 | 说明 | API |
|------|------|-----|
| 停止分发 | 将 build 从测试中移除,用户不再收到更新 | `PATCH /v1/builds/{id}` 设置 `expired: true` |
| 过期 build | 强制过期有问题的 build | 同上 |
| 紧急热修 | 构建新版本覆盖上线 | 常规部署流程 |
```bash
# 通过 ASC API 停止分发某个 build
curl -X PATCH "https://api.appstoreconnect.apple.com/v1/builds/$BUILD_ID" \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"data":{"type":"builds","id":"'$BUILD_ID'","attributes":{"expired":true}}}'
```
### Docker 回滚
Docker 回滚相对简单,拉取上一个正常版本的镜像重新部署即可:
```bash
# 1. 确定上一个正常的 tag
PREVIOUS_TAG=<previous-good-tag>
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
# 2. 在服务器上回滚
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
cd /opt/xiaoqu/production # 或 /opt/xiaoqu/staging
export IMAGE_TAG=$PREVIOUS_TAG
docker compose pull
docker compose up -d
"
# 3. 验证回滚成功
curl -sf http://39.104.87.246/health && echo 'Rollback OK'
```
### 数据库回滚注意事项
| 场景 | 策略 |
|------|------|
| 可逆 migration(加列、加表) | 部署回滚后数据库无需回滚,旧代码忽略新列 |
| 不可逆 migration(删列、改类型) | **必须先回滚 migration 再回滚代码**,否则旧代码报错 |
| 数据 migration | 评估是否需要补偿脚本,建议 migration 前做备份快照 |
```bash
# 数据库 migration 回滚示例(如果使用 golang-migrate)
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
docker compose exec gateway migrate -path /migrations -database \$DATABASE_URL down 1
"
```
---
## 部署监控
### Post-deploy 健康检查模式
```bash
# 通用部署后验证函数
post_deploy_verify() {
local url=$1
local max_retries=${2:-5}
local interval=${3:-10}
echo "Verifying deployment at $url ..."
for i in $(seq 1 $max_retries); do
local status
status=$(curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
if [ "$status" = "200" ]; then
echo "Health check passed (attempt $i/$max_retries)"
return 0
fi
echo "Attempt $i/$max_retries: status=$status, retrying in ${interval}s..."
sleep $interval
done
echo "Health check FAILED after $max_retries attempts"
return 1
}
# 使用示例
post_deploy_verify "http://39.104.87.246/health" 5 10
```
### 飞书通知模板
部署完成后通过飞书 Webhook 发送通知:
```bash
# 部署成功通知
send_feishu_deploy_notification() {
local env=$1 # staging / production
local version=$2 # 版本号或 tag
local status=$3 # success / failure
local detail=$4 # 额外说明
local WEBHOOK_URL="<飞书群 Webhook 地址>"
if [ "$status" = "success" ]; then
local color="green"
local emoji="✅"
else
local color="red"
local emoji="❌"
fi
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"msg_type": "interactive",
"card": {
"header": {
"title": {"tag": "plain_text", "content": "'"$emoji"' 部署通知 - '"$env"'"},
"template": "'"$color"'"
},
"elements": [
{"tag": "div", "text": {"tag": "lark_md", "content": "**环境**: '"$env"'\n**版本**: '"$version"'\n**状态**: '"$status"'\n**时间**: '"$(date '+%Y-%m-%d %H:%M:%S')"'\n**详情**: '"$detail"'"}}
]
}
}'
}
# 使用示例
send_feishu_deploy_notification "production" "v1.2.3" "success" "Gateway + Web 部署完成"
send_feishu_deploy_notification "staging" "abc12345" "failure" "Health check 超时"
```
### iOS TestFlight 构建状态监控
通过 ASC API 持续监控 build 处理状态:
```bash
# 监控 TestFlight build 处理状态
monitor_testflight_build() {
local build_id=$1
local jwt_token=$2
local max_wait=600 # 最长等待 10 分钟
local elapsed=0
while [ $elapsed -lt $max_wait ]; do
local response
response=$(curl -s "https://api.appstoreconnect.apple.com/v1/builds/$build_id" \
-H "Authorization: Bearer $jwt_token")
local state
state=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['attributes']['processingState'])" 2>/dev/null)
echo "[$(date '+%H:%M:%S')] Build $build_id: $state"
case "$state" in
VALID)
echo "Build 处理完成,可用于测试"
return 0
;;
FAILED|INVALID)
echo "Build 处理失败: $state"
return 1
;;
PROCESSING)
sleep 30
((elapsed+=30))
;;
*)
sleep 15
((elapsed+=15))
;;
esac
done
echo "Build 处理超时(${max_wait}s)"
return 1
}
```
---
@@ -7,5 +7,6 @@
}, },
"install_name": "dev-ios", "install_name": "dev-ios",
"install_type": "skill", "install_type": "skill",
"dir_category": "dev" "dir_category": "dev",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: dev-ios name: dev-ios
description: iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。 description: iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# iOS 开发插件 (dev-ios) # iOS 开发插件 (dev-ios)
@@ -7,5 +7,6 @@
}, },
"install_name": "dev-pda", "install_name": "dev-pda",
"install_type": "skill", "install_type": "skill",
"dir_category": "dev" "dir_category": "dev",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: dev-pda name: dev-pda
description: PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。 description: PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# PDA 应用开发插件 (dev-pda) # PDA 应用开发插件 (dev-pda)
@@ -303,3 +303,13 @@ mcp__ai-proj__link_tasks_to_requirement({
4. **严重度准确** — 不要所有问题都标 High,按实际影响分级 4. **严重度准确** — 不要所有问题都标 High,按实际影响分级
5. **建议可操作** — 每个发现必须附带具体修复建议 5. **建议可操作** — 每个发现必须附带具体修复建议
6. **关注变更** — 评审范围是 diff,不要对未变更的代码提意见(除非变更引入了对旧代码的新风险) 6. **关注变更** — 评审范围是 diff,不要对未变更的代码提意见(除非变更引入了对旧代码的新风险)
## 接收与响应评审意见
评审不是单向输出,收到意见后按这套来:
1. **先分类**:阻断项(必须修)/ 建议项(可讨论)/ 澄清项(问题理解不一致)。
2. **先复述再回应**:用自己的话重述意见,确认理解一致,再给证据或修改。
3. **不辩解**:用证据说话(代码、测试、日志),不用语气词反驳。
4. **逐条回评**:每条写「已修(commit)/ 不修(理由)/ 需澄清(具体问题)」,不得默默跳过。
5. **阻断项没修完不得合并**;建议项不修要记录原因,进 PR 描述。
@@ -215,3 +215,11 @@ AI 根据需求变更范围动态生成 Playwright spec,模板见 `templates/p
若 git diff 显示同一功能的 `handlers/`、`middleware/`、`routes/` 文件均有变更(通过命名模式识别,如 `impersonation_handler.go` + `impersonation_middleware.go` + `impersonation_routes.go`),则除单元测试外**额外生成** `backend/tests/` 下的集成测试。 若 git diff 显示同一功能的 `handlers/`、`middleware/`、`routes/` 文件均有变更(通过命名模式识别,如 `impersonation_handler.go` + `impersonation_middleware.go` + `impersonation_routes.go`),则除单元测试外**额外生成** `backend/tests/` 下的集成测试。
识别方式:提取文件名中的功能前缀(如 `impersonation`),若在三个目录中均出现,则触发集成测试生成。 识别方式:提取文件名中的功能前缀(如 `impersonation`),若在三个目录中均出现,则触发集成测试生成。
## TDD 循环(红 → 绿 → 重构)
1. **一次只做一个行为**:写一个小测试,先确认它**失败**,并确认失败原因就是你要修的那件事(不是编译错误/环境问题)。
2. **最小实现让它变绿**,不做顺手重构。
3. **绿了再重构**,重构期间测试保持绿。
4. 禁止先写实现再补测试;补的测试要能证明它真的会红(临时改坏一次验证)。
5. 交付时附:测试命令、通过/失败数、以及「曾经红过」的证据(日志或截图记录)。
@@ -1,7 +1,8 @@
--- ---
name: frontend-design name: frontend-design
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics. description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
arguments: "[component|page|storybook] <description>" metadata:
arguments: "[component|page|storybook] <description>"
--- ---
# Frontend Design 前端设计技能 # Frontend Design 前端设计技能
@@ -0,0 +1,11 @@
{
"name": "slark-dsh-cicd-plugin",
"description": "DSH 插件包与 Slark 宿主补丁的发布实战技能:npm run pack 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、draft Release、发布后 SHA 核对、asar 完整性与 ai-proj 状态对齐。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "slark-dsh-cicd",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,213 @@
---
name: slark-dsh-cicd
description: DSH 插件包与 Slark 宿主补丁的发布 runbook。覆盖 npm run pack 双 tgz 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、改指向后必须重新打包、draft→publish 节奏、GitHub HTTPS/SSH 与 fetch 卡死处置、发布后 SHA 核对、app.asar 完整性约束、ai-proj 发布状态对齐。当用户要发布或升级 DSH 插件(dsh-worktable 等)、发布/更新 Slark 宿主补丁、push 或下载 GitHub 失败、插件「检查更新」看不到新版本、需要 fork 发布、或排查 asar 完整性/签名问题时使用。
---
# DSH 插件与 Slark 宿主发布 runbook (slark-dsh-cicd)
面向「把插件包/宿主补丁真正发出去」的可执行清单。默认在**发布机**执行;命令按阶段照做,
每阶段结束先核验再进入下一阶段。
## 适用与分工
两条发布线,**互不依赖**,不要串流程:
| 发布线 | 产物 | 入口 | 权威源 |
| --- | --- | --- | --- |
| A · DSH 插件 | `dist/v<ver>/dsh-worktable.tgz` + `dsh-worktable-<ver>.tgz`(同一验收包) | `<pkg>/npm run pack` | GitHub Release(tag = `v<包内版本>`) |
| B · Slark 宿主补丁 | 重打包 + 签名的 Slark.app(含内嵌 DSH runtime) | 宿主自身打包/签名链 | 宿主发布链,不经 GitHub Release |
与同市场技能的分工,避免重复:
- **本技能**:DSH 插件包发布、宿主补丁打包侧的完整性约束、发布链本身(权限、tag、Release、SHA 核对)。
- `slark-cicd`:Slark 仓库 staging 验收、生产发布、Desktop 安装包上传 OSS、PR/CI 门禁——服务器与包分发细节以它为准。
- `dev-cicd`:通用流水线设计与排查(Gitea Actions/缓存/并发)。
- `ai-proj-cicd-release`:AI-Proj 服务自身的门禁与回滚。
## 铁律(先记死)
1. **打包只走唯一入口**:`npm run pack`(或仓库自带的等价脚本)。手写 `tar`/`npm pack` 会绕过结构断言与安装导入验证。
脚本自己会做:版本一致性 → 兼容回归 → 构建 → 语法检查 → 结构断言(精确文件清单、无 `src/`、无 `.map`)→
独立临时目录真实安装 + `import()` 断言 → 客户端/宿主门禁 → 双资产同源 → SHA-256。
2. **`package.json` 与 `dsh.plugin.json` 的版本必须相等**,否则构建与打包脚本 fail-closed。发版只升这一处对,不要只改一边。
3. **产物出了包就不再改**:任何源码/常量/文档改动都可能改变 bundle → 旧 `.tgz` 立即作废、sha 必变。**改完必须重新 pack 并重新记录 sha**。
4. **对上游仓库零改动是硬指标**(尤其是没有写权限的外部作者仓库):不 push、不打 tag、不建 Release/PR,一次都不做。
5. **公开发布必须是 draft 起步**,对外动作经用户过目后再 `--draft=false`。
6. **force push 只允许发生在自己 fork 的分支/tag 上**;绝不对上游使用 `-f`。
7. **发布用的分支必须是干净单提交**,不要为了省事把本地历史推上去(本地历史可能含个人路径、凭据、临时调试 commit)。
8. **"下载通了" ≠ "hash 对了"**:发布完成的判据是「远端资产 sha == 本地验收 sha」,不是页面能打开。
## 阶段 0:身份与权限三连(先判定能力,再决定链路)
```bash
gh auth status # 登录账号是否符合预期
ssh -T git@github.com 2>&1 | head -3 # SSH 身份(回显 Hi <user>! 即通)
gh api repos/<owner>/<repo> --jq '.permissions' # 关键:{"pull":true,"push":<bool>}
```
- **只有 `push:true` 才能走「上游直发」**;`push:false` 时不要硬撞,直接走阶段 2 的 fork 发布。
- **`git ls-remote` 通 ≠ 能 push**:读权限随时可通,写权限被拒要到 push 才暴露。不要用 ls-remote 成功推断可写。
- 同时确认远端协议:`git remote -v`。HTTPS 到 GitHub 可能整体不通(见「网络与链路故障处置」),
必要时改用 SSH:`git remote set-url origin git@github.com:<owner>/<repo>.git`。
## 阶段 1:发布线 A — DSH 插件(有上游写权限)
```bash
export HOME="/Users/johnq"
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
cd <repo>/<pkg>
npm run check && npm run test:gate && npm run pack
shasum -a 256 dist/v<ver>/*.tgz # 两个文件必须同 sha
tar -xzf dist/v<ver>/dsh-worktable.tgz -C "$(mktemp -d)" && grep -rn "<旧仓库名>" <解包目录> # 期望无输出
git add -A && git commit -m "release: <name> v<ver>(REQ-…)…"
git tag -f v<ver> && git push origin HEAD v<ver>
gh release create v<ver> dist/v<ver>/dsh-worktable.tgz dist/v<ver>/dsh-worktable-<ver>.tgz \
--draft --title "<name> v<ver>" --notes-file <notes.md>
```
- tag **必须等于包内版本**(`v` + `package.json#version`),否则下游按 tag 拼版本化资产名会 404。
- Release 附**双资产**:固定名(供 `releases/latest/download/<fixed>.tgz` 长期入口)+ 版本化名(可追责)。
## 阶段 2:发布线 B — 无上游写权限 → fork 发布
```bash
gh repo fork <upstream-owner>/<repo> --clone=false # 已存在则复用
git remote add fork git@github.com:<me>/<repo>.git # 已存在则 git remote set-url fork …
git fetch --no-tags origin main
```
### 2.1 干净单提交 + 树一致性 gate(必做,不可省)
```bash
OURS=$(git rev-parse <本地完成发布内容的 HEAD>)
git checkout -B release/v<ver> origin/main
git rm -rq --cached .
git checkout "$OURS" -- .
git add -A
git commit -m "release: <name> v<ver>(REQ-…)…"
git diff --stat "$OURS" # 必须是空输出
```
- `git diff --stat "$OURS"` **非空即 abort**:切回原分支、删掉该发布分支、报告差异,不要「差一点也推」。
- 该 gate 的真实作用是:让公开发布分支的**树**等于本地验收树,同时**不携带本地历史提交**(个人路径、调试、凭据)。
- `git rm -rq --cached .` 只清索引,随后 `git checkout "$OURS" -- .` 覆盖工作树;若上游 `main` 存在本地树里**已删除**的
文件,残留文件会被 `git add -A` 重新加回导致 gate 失败——先确认 `git diff --name-status origin/main <OURS>` 无 `D`/`R`。
- 提交前可顺手确认没有敏感路径混入:`git log -S'<本机绝对路径前缀>' --oneline`。
### 2.2 推分支 + tag + 附双资产的 draft Release
```bash
git push -u fork release/v<ver>
git tag -f v<ver> && git push -f fork v<ver> # -f 只作用于 fork 的 tag
gh release create v<ver> dist/v<ver>/dsh-worktable.tgz dist/v<ver>/dsh-worktable-<ver>.tgz \
--repo <me>/<repo> --draft --title "<name> v<ver>" --notes-file <notes.md>
```
- 推完复核「原仓库零改动」:`git ls-remote <origin-url>`(应无新分支/tag)+ `gh release list --repo <upstream>`(列表应与之前一致)。
- Release notes 至少写:新增 / 修复 / 清理 / 验证(实际跑过的测试与数字)/ 安装命令(指向 fork)/ 包 SHA-256 / 已知边界。
## 阶段 3:改指向(换仓库、换发布方时)
只要「发布方」变了,插件里所有指向旧仓库的常量都要一次改全,否则**更新检查读的还是旧仓库 → 永远发现不了新版**:
- 更新检查仓库常量(如 `UPDATE_REPO`,可能有多份:入口 + 独立模块 + 设置面板各自实现);
- 升级命令/提示词里的安装 URL(可能拼在字符串里);
- `package.json#repository.url`;
- 发布后核对脚本里硬编码的 REPO;
- README/AGENTS/安装说明里的一行命令、`git clone` 地址、文档链接;
- 任何 `grep -rn "<旧 owner>"` 的残留。
规则:
- **只改指向,不改版本号与逻辑**;逐处 `git diff` 复核。
- 「相关项目/其它仓库」的外链如果指向**另一位开发者的独立仓库**,不要顺手改指(目标仓库不存在会变成死链)——保留并在交付里说明。
- **改完必须重新 pack**(铁律 3),解包 `grep` 确认产物内无旧仓库名,sha 与 Release 全部按新值重来。
## 阶段 4:draft → publish
```bash
gh release edit v<ver> --repo <me>/<repo> --draft=false
```
- **draft 期间 `releases/latest` 仍指向上一个已发布版本** → 插件内「检查更新」看不到新版本;
draft 资产的 tag 路径也不公开可下(需鉴权)。这是「更新检查不生效」最常见的**非 bug 原因**,先查 Release 是否还是 draft。
- 发布前用用户过目确认;发布后再跑阶段 5。
## 阶段 5:发布后核对
优先用仓库自带的核对脚本,但**平台适配要先判定**:`verify-remote.mjs` 之类脚本若用
`powershell Invoke-WebRequest` 下载,则**只在 Windows 可用**,在 macOS 会以 `FAIL: download fixed-name asset` 收场
(它的 GET URL 仍能证明改指生效,不代表脚本思路错)。
macOS/无 PowerShell 时的等价核对(全部要过):
```bash
gh release view v<ver> --repo <me>/<repo> --json assets # 两个资产的 digest 必须都等于本地 sha
gh release download v<ver> --repo <me>/<repo> --dir "$TMP" # draft 也可下载(已鉴权)
shasum -a 256 "$TMP"/*.tgz # 与本地验收 sha 逐字一致
cmp "$TMP/dsh-worktable.tgz" dist/v<ver>/dsh-worktable.tgz # byte-identical
tar -xzf "$TMP/dsh-worktable.tgz" -C "$TMP/ext" && grep -rn "<旧仓库名>" "$TMP/ext" # 无输出
curl -sL -o /dev/null -w '%{http_code}\n' <版本化资产 URL> # 发布后 200/302;draft 阶段 404 属预期
```
结论口径:**文件名 + 结构(精确文件清单)+ 包内版本 + 双资产同源 + 远端=本地验收 SHA + 真实安装导入** 全过才算发布验收。
draft 阶段只能做「远端资产 = 本地验收包」的比对,匿名可下载性要等 publish 后再验。
## 阶段 6:Slark 宿主补丁(宿主侧特有)
- **改 `app.asar` 无效**:Electron 用 `ElectronAsarIntegrity` 记录 asar 头哈希并有 fuse 完整性校验,直接热改会让
哈希失配,破坏签名,应用拒绝启动或行为异常。**必须重打包 + 重新安装**,不要在安装目录里就地改文件。
- 宿主补丁落在 fork/分支上时:rebase 到发布基线后**必须重跑全量测试**(类型检查 + 全量单测),
例如 `pnpm -r typecheck` 与 `node --import tsx --test "src/**/*.test.ts"`;rebase 不产生冲突**不代表语义没变**。
- 宿主发布与插件发布解耦:宿主要用新插件版本,通常靠宿主内嵌 runtime 的升级,而不是「插件先发、宿主自动跟进」。
## 网络与链路故障处置
| 现象 | 判定 | 处置 |
| --- | --- | --- |
| `curl` 访问 GitHub 返回 HTTP `000`(HTTPS 整体不通) | 出口到 github.com:443 被掐断,与凭据无关 | 改走 SSH:`git remote set-url origin git@github.com:<owner>/<repo>.git`,并确认 SSH 身份 |
| `git fetch` 长时间无输出/0 字节、卡十几分钟 | 链路假死,不是"慢" | 判死并清理后重试(见下方循环模板),不要原地等 |
| 30 秒内报 `fatal: early EOF` | 链路被中途掐断(代理/MTU/网络策略) | 记录原文与时间点;换协议/换网络重试;不要反复 force |
| macOS 没有 `timeout` 命令 | BSD 无 GNU timeout | 用后台 + 轮询模板 |
| `git push` 报无权限但 ls-remote 正常 | 只读凭据 | 直接走 fork 发布(阶段 2),不要重试上游 |
macOS 无 `timeout` 时的判死模板:
```bash
( git fetch --no-tags origin main ) & PID=$!
for i in $(seq 1 30); do sleep 1; kill -0 "$PID" 2>/dev/null || break; done
if kill -0 "$PID" 2>/dev/null; then kill -9 "$PID"; pkill -9 -f git-upload-pack; echo "TIMEOUT(30s) — 链路假死"; fi
```
- 认证/网络失败时**不做破坏性重试**(尤其不要为了"绕过"而 force 到上游);把失败原文、已完成的步骤、当前指针状态写清楚上报。
- 推送成功但平台上看不到:查 `git config --get-regexp '^url\.'` 是否有 `pushInsteadOf` 静默改写了目标。
## ai-proj 对齐
- 需求阶段:动手前立项(`create_requirement`,写清需求描述 / 预期结果 / 验收标准三段式 + AC),commit/PR 引用 `REQ-…`。
纯文档/工具/只读排查可例外。
- 交付阶段:**真实发布之后**才推进 `staging → released`;只有计划、没有发布产物或验收证据时不要推进。
- 归档阶段:`/req done` 归档前要**逐条 AC 证据**(命令、输出、资产 sha、URL),不能只写"已完成"。
- **文档接口故障绕过**:若 `append-document-content` / `create-and-attach` 持续报
`document_versions_document_id_version_number_key` 唯一约束错误(同一 `(document_id, version_number)` 冲突),
改用 `PUT /api/v1/requirements/{id}` 把说明写进**需求描述**——`requirements` 表不经过 `document_versions`,可绕开该约束;
不要伪造版本号或删除已有文档版本。
## 故障分流
| 现象 | 处置 |
| --- | --- |
| 插件「检查更新」永远看不到新版 | 依次查:Release 是否还是 draft;`releases/latest` 指向;插件内 `UPDATE_REPO` 是否仍指旧仓库;`latest` 资产名是否与代码拼的一致 |
| pack 报结构断言失败 | 包内混入了 `src/`、`.map` 或多余文件;检查 `files` 字段与构建产物位置,不要手改 tgz |
| 两个 tgz 的 sha 不一致 | 打包脚本的"同源复制"被绕过(手工 pack/改名);重新走唯一入口 |
| `git diff --stat <OURS>` 非空 | 上游 `main` 有本地已删除/重命名文件残留 → 先 `git rm --cached` 清理并确认无 `D`/`R`,gate 不过不上推 |
| 发布分支含个人路径/凭据 commit | 不要 push 该历史;改用干净单提交分支(阶段 2.1) |
| `gh release create` 报 tag 不存在/被占用 | tag 必须已存在或由 `create` 指定;draft 会以 `untagged-…` 预览 URL 呈现,属正常,publish 后即 `.../tags/v<ver>` |
| draft 资产匿名下载 404 | 预期行为(draft 需鉴权);发布后用 `curl -sL -o /dev/null -w '%{http_code}'` 复验 |
| 宿主改完不生效 | 是否只改了 `app.asar`(被完整性校验挡住);必须重打包重装;再查是否加载了另一份 runtime |
## 参考
- `references/fork-release-and-post-checks.md` —— fork 发布可复制命令序列、Release notes 模板、发布后核对矩阵。
- 同市场:`slark-cicd`(Slark staging/生产/Desktop)、`dev-cicd`(流水线设计)、`ai-proj-cicd-release`(AI-Proj 发布链)。
- 权威流程以目标仓库自带的发布脚本与文档为准;本技能是清单,不替代仓库脚本的断言。
@@ -0,0 +1,134 @@
# Fork 发布与发布后核对
本文件是 `slark-dsh-cicd` 的操作附件:需要**无上游写权限发布**,或需要**逐项核对远端资产**时读取。
把它当模板:替换 `<...>` 占位符,按阶段执行,每阶段的核验不通过就停下,不要"带病"推进。
## 0. 变量与前置
```bash
export HOME="/Users/johnq"
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
UP=<upstream-owner>/<repo> # 例如 Aisland-SJL/dsh-worktable(外部作者,无写权限)
ME=<your-account> # 例如 qiudl
PKG=<repo>/01_content # 打包脚本所在目录
VER=0.4.1 # = package.json#version
```
```bash
gh auth status
ssh -T git@github.com 2>&1 | head -3
gh api "repos/$UP" --jq '.permissions' # push:false → 必须走 fork 发布
git remote -v
```
## 1. 改指向 → 重新打包(顺序不可颠倒)
```bash
cd "$PKG"
grep -rn "<upstream-owner>" .. --include='*.json' --include='*.ts' --include='*.tsx' --include='*.md' --include='*.mjs'
# 逐处改完之后再打包:
npm run check && npm run test:gate && npm run pack
shasum -a 256 dist/v$VER/*.tgz
TMP=$(mktemp -d) && tar -xzf dist/v$VER/dsh-worktable.tgz -C "$TMP"
grep -rn "<upstream-owner>" "$TMP" ; echo "grep exit=$? (1 = 无残留,正确)"
```
判据:`check`/`test:gate`/`pack` 全部 exit 0;两个 tgz 同 sha;解包无旧仓库名。
## 2. fork 与干净发布分支
```bash
cd <repo-root>
gh repo fork "$UP" --clone=false # 已存在会提示 already exists,可复用
git remote add fork "git@github.com:$ME/<repo>.git" # 已存在:git remote set-url fork …
git fetch --no-tags origin main
OURS=$(git rev-parse HEAD) # 本地"发布内容已就绪"的 HEAD
git diff --name-status origin/main "$OURS" | awk '$1 ~ /^[DR]/' # 期望空;有 D/R 先处理再用 gate
git checkout -B release/v$VER origin/main
git rm -rq --cached .
git checkout "$OURS" -- .
git add -A
git commit -m "release: <name> v$VER(REQ-…)<一句话说明>"
DIFFOUT="$(git diff --stat "$OURS")"
if [ -n "$DIFFOUT" ]; then
echo "GATE FAILED:"; echo "$DIFFOUT"
git checkout <原分支> && git branch -D release/v$VER # abort:不留半成品分支
exit 1
fi
echo "GATE PASSED: release tree == OURS tree"
```
## 3. 推送(只对 fork)与 draft Release
```bash
git push -u fork release/v$VER
git tag -f v$VER && git push -f fork v$VER # -f 仅限自己 fork 的 tag
gh release create v$VER \
dist/v$VER/dsh-worktable.tgz dist/v$VER/dsh-worktable-$VER.tgz \
--repo "$ME/<repo>" --draft --title "<name> v$VER" --notes-file /tmp/rel-notes.md
```
原仓库零改动复核:
```bash
git ls-remote "git@github.com:$UP.git" # 无 release/v… 分支、无新 tag
gh release list --repo "$UP" # 与发布前一致
```
### Release notes 模板(draft 阶段就写全新值)
```markdown
# <name> v<ver>
> 发布方:fork <ME>/<repo>(原仓库 <UP> 为上游来源)。插件内更新检查已指向本 fork。
## 新增 / 修复 / 清理
## 验证
- check / gate / pack 实际输出(测试条数、断言项)
- 双资产同 SHA:<sha256>
- 解包 grep 无旧仓库名
- 发布分支为干净单提交(父提交 = 上游 main),`git diff --stat <OURS>` 为空
## 安装
网页端 / 桌面端一行命令(指向 fork)
## 已知边界
- draft 期间 releases/latest 仍指旧版 → 更新检查看不到新版
- 保留的第三方外链、平台未验证范围
```
## 4. publish 与发布后核对矩阵
```bash
gh release edit v$VER --repo "$ME/<repo>" --draft=false
```
| 核对项 | 命令 | 通过判据 |
| --- | --- | --- |
| 资产列表与 digest | `gh release view v$VER --repo $ME/<repo> --json assets` | 两个资产 `digest` = 本地 sha |
| 下载后 sha | `gh release download v$VER --repo $ME/<repo> --dir "$D"` + `shasum -a 256 "$D"/*.tgz` | 与本地逐字一致 |
| 与本地验收件一致 | `cmp "$D/dsh-worktable.tgz" dist/v$VER/dsh-worktable.tgz` | 无差异(byte-identical) |
| 包内无旧仓库名 | 解包后 `grep -rn "<upstream-owner>"` | 无输出(exit 1) |
| 包内版本 = tag | 解包读 `package.json#version` | `v$VER` |
| 匿名可下载 | `curl -sL -o /dev/null -w '%{http_code}\n' <资产 URL>` | publish 后 200/302;draft 期 404 属预期 |
| 安装可用 | 临时目录 `npm install <tgz>` + `import()` 断言 | 导出/版本断言通过 |
- **`verify-remote.mjs` 的平台限制**:其下载走 `powershell Invoke-WebRequest`,只在 Windows 可用;
在 macOS 会 `FAIL: download fixed-name asset`。此时用上表的 `gh release view/download` + `shasum` + `cmp` 等价替代,
并说明该脚本的 GET URL 已证明改指生效。
- **"检查更新"看不到新版**的排查顺序:Release 是否 draft → `releases/latest` 指向 → 插件内 `UPDATE_REPO` 指向 →
`latest` 资产名与代码拼法一致。
## 5. 链路判死模板(macOS 无 `timeout`)
```bash
( git fetch --no-tags origin main ) & PID=$!
for i in $(seq 1 30); do sleep 1; kill -0 "$PID" 2>/dev/null || break; done
if kill -0 "$PID" 2>/dev/null; then kill -9 "$PID"; pkill -9 -f git-upload-pack; echo "TIMEOUT(30s)"; fi
```
- HTTPS 到 GitHub `curl` 返回 `000` → 整体不通,改 SSH(`git remote set-url`)。
- 30 秒内 `fatal: early EOF` → 链路被掐断;记录原文与时间,换网络/协议重试。
- 认证或网络失败时不做破坏性重试,也不要把 force 用到上游;保留本地提交并上报失败原文。
@@ -7,5 +7,6 @@
}, },
"install_name": "data-excel", "install_name": "data-excel",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: data-excel name: data-excel
description: Excel 数据处理与 BI 集成。通过自然语言操作 Excel 文件的读取、编辑、转换,并支持导入到 BI 系统(Metabase)进行可视化分析。当用户提到 Excel、表格处理、数据导入、BI 分析相关任务时自动激活。 description: Excel 数据处理与 BI 集成。通过自然语言操作 Excel 文件的读取、编辑、转换,并支持导入到 BI 系统(Metabase)进行可视化分析。当用户提到 Excel、表格处理、数据导入、BI 分析相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# Excel 数据处理与 BI 集成 Skill # Excel 数据处理与 BI 集成 Skill
@@ -7,5 +7,6 @@
}, },
"install_name": "doubao-voice", "install_name": "doubao-voice",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: doubao-voice name: doubao-voice
description: 豆包语音API调用。支持语音合成(TTS)和唱歌。当用户提到语音合成、文字转语音、唱歌、豆包语音相关任务时自动激活。 description: 豆包语音API调用。支持语音合成(TTS)和唱歌。当用户提到语音合成、文字转语音、唱歌、豆包语音相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 豆包语音API技能 # 豆包语音API技能
@@ -7,5 +7,6 @@
}, },
"install_name": "feishu-bitable", "install_name": "feishu-bitable",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: feishu-bitable name: feishu-bitable
description: 飞书多维表格操作。用于记录增删改查、批量操作、筛选排序、数据同步。当需要操作飞书多维表格时使用。 description: 飞书多维表格操作。用于记录增删改查、批量操作、筛选排序、数据同步。当需要操作飞书多维表格时使用。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 飞书多维表格 (Bitable) # 飞书多维表格 (Bitable)
@@ -7,5 +7,6 @@
}, },
"install_name": "feishu-docx", "install_name": "feishu-docx",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: feishu-docx name: feishu-docx
description: 飞书云文档操作。用于创建、编辑云文档,插入内容块,会议纪要生成。当需要操作飞书云文档时使用。 description: 飞书云文档操作。用于创建、编辑云文档,插入内容块,会议纪要生成。当需要操作飞书云文档时使用。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 飞书云文档 (Docx) # 飞书云文档 (Docx)
@@ -7,5 +7,6 @@
}, },
"install_name": "feishu", "install_name": "feishu",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: feishu name: feishu
description: 飞书文档与多维表格操作入口。当用户提到飞书、云文档、多维表格、Bitable 相关任务时自动激活。 description: 飞书文档与多维表格操作入口。当用户提到飞书、云文档、多维表格、Bitable 相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 飞书集成 # 飞书集成
@@ -7,5 +7,6 @@
}, },
"install_name": "siyuan", "install_name": "siyuan",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
@@ -1,10 +1,10 @@
--- ---
name: siyuan name: siyuan
description: 思源笔记 API 集成。通过自然语言创建、编辑、搜索笔记,管理笔记本和文档树。当用户提到思源笔记、SiYuan、笔记管理、知识库相关任务时自动激活。 description: 思源笔记 API 集成。通过自然语言创建、编辑、搜索笔记,管理笔记本和文档树。当用户提到思源笔记、SiYuan、笔记管理、知识库相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 思源笔记 API 集成 Skill
## 环境变量配置(必须) ## 环境变量配置(必须)
本 skill 通过环境变量读取思源笔记连接信息,不同用户/组织配置不同的值。 本 skill 通过环境变量读取思源笔记连接信息,不同用户/组织配置不同的值。
@@ -517,127 +517,6 @@ markdown = f"""# {today} 工作日志
## 会议记录 ## 会议记录
## 备注
"""
# 获取或创建日记笔记本
notebooks = api.list_notebooks()
diary_nb = next((nb for nb in notebooks if nb["name"] == "日记"), None)
if not diary_nb:
diary_nb = api.create_notebook("日记")["notebook"]
# 使用 upsert_doc 避免重复创建
doc_id = api.upsert_doc(diary_nb["id"], f"/{today}", markdown)
print(f"每日笔记: {doc_id}")
```
### 场景 2: 批量导入 Markdown 文件
```python
from pathlib import Path
api = SiYuanAPI()
notebook_id = "目标笔记本ID"
md_folder = "/path/to/markdown/files"
for md_file in Path(md_folder).glob("*.md"):
with open(md_file, "r", encoding="utf-8") as f:
content = f.read()
doc_name = md_file.stem
# 使用 upsert_doc 支持增量导入,避免重复
doc_id = api.upsert_doc(notebook_id, f"/{doc_name}", content)
print(f"导入: {doc_name} -> {doc_id}")
```
### 场景 3: 搜索并汇总
```python
api = SiYuanAPI()
# 搜索所有包含"会议"的内容
results = api.search("会议")
blocks = results.get("blocks", [])
print(f"找到 {len(blocks)} 条相关内容:")
for block in blocks[:10]:
print(f"- {block.get('content', '')[:50]}...")
```
### 场景 4: 发布合同到飞书云文档
```python
from siyuan import SiYuanAPI
api = SiYuanAPI()
# 1. 从思源笔记导出合同内容
contract_doc_id = "20260202080355-2c0vgac" # 合同文档 ID
markdown_content = api.export_md(contract_doc_id)
# 2. 使用飞书 MCP 创建云文档
# 调用 feishu-doc MCP 的 create_and_write_document 工具
# - title: "物流服务合同-妗晨与名风"
# - content: markdown_content
# - folder_token: (可选,指定目标文件夹)
# 3. 完整的发布函数
def publish_to_feishu(doc_id: str, doc_title: str = None) -> dict:
"""
将思源笔记文档发布到飞书云文档
Args:
doc_id: 思源笔记文档 ID
doc_title: 飞书文档标题(可选,默认使用原文档标题)
Returns:
dict: {"siyuan_doc_id": str, "content": str, "feishu_url": str}
"""
api = SiYuanAPI()
# 导出 Markdown 内容
content = api.export_md(doc_id)
# 获取原文档标题(如果未指定)
if not doc_title:
doc_info = api.get_block(doc_id)
doc_title = doc_info.get("content", "未命名文档")
# 返回准备好的内容,由调用者使用 feishu-doc MCP 创建
return {
"siyuan_doc_id": doc_id,
"doc_title": doc_title,
"content": content
}
# 使用示例
result = publish_to_feishu("20260202080355-2c0vgac", "物流服务合同-妗晨与名风-2026")
print(f"准备发布: {result['doc_title']}")
print(f"内容长度: {len(result['content'])} 字符")
# 然后调用 feishu-doc MCP:
# mcp__feishu-doc__create_and_write_document(
# title=result["doc_title"],
# content=result["content"]
# )
```
**工作流说明**:
1. **创建合同** (biz-contract 技能) → 生成合同 Markdown 文本
2. **存储到思源** (siyuan 技能) → 保存到"商务合同"笔记本,便于预览和修改
3. **发布到飞书** (feishu-doc MCP) → 导出并创建飞书云文档
**商务合同笔记本配置**:
| 配置项 | 值 |
|--------|-----|
| 笔记本名称 | 商务合同 |
| 笔记本 ID | `20260202080313-kjtgg1j` |
| 路径规范 | `/{合同类型}/{甲方简称}-{乙方简称}-{年份}` |
---
## 注意事项 ## 注意事项
- API Token 需要保密,不要提交到代码仓库 - API Token 需要保密,不要提交到代码仓库
@@ -665,72 +544,6 @@ print(f"内容长度: {len(result['content'])} 字符")
- **最后更新**: 2026-01-15 07:35:00 ACDT - **最后更新**: 2026-01-15 07:35:00 ACDT
- **验证时间**: 2026-01-15 08:00:00 CST - **验证时间**: 2026-01-15 08:00:00 CST
## 配置变更
**变更时间**: 2026-01-15 01:12:00 CST
**生效时间**: 2026-01-15 01:15:00 CST
```
#### ❌ 错误示例
```markdown
- **发生时间**: 2026-01-15
- **创建时间**: 2026-01-15
- **最后更新**: 今天
- **验证时间**: 早上
```
#### 时间格式规范
| 场景 | 格式 | 示例 |
|------|------|------|
| 精确时间点 | `YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 02:00:01 CST` |
| 时间范围 | `YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ` | `2026-01-15 00:00:00 - 00:46:00 CST` |
| 跨天时间范围 | `YYYY-MM-DD HH:MM:SS - YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 23:00:00 - 2026-01-16 01:00:00 CST` |
**时区标注**:
- CST: 中国标准时间 (服务器时间、生产环境)
- ACDT: 澳大利亚中部夏令时 (au-dev 本地开发时间)
- UTC: 协调世界时 (国际标准)
### 文档元信息要求
所有运维、技术类笔记文档必须在开头包含元信息:
```markdown
# 文档标题
**创建时间**: YYYY-MM-DD HH:MM:SS TZ
**创建人**: qiudl / Claude
**最后更新**: YYYY-MM-DD HH:MM:SS TZ
**文档状态**: ✅ 已完成 / 🔄 进行中 / ⚠️ 待验证 / ❌ 已废弃
**相关项目**: 项目名称
**标签**: #运维 #数据库 #备份
---
[正文内容...]
```
### 事件记录规范
记录重大事件或故障时必须包含:
1. **事件时间**: 完整的开始和结束时间(精确到秒)
2. **严重程度**: P0/P1/P2/P3
3. **影响范围**: 具体说明受影响的系统和用户
4. **事件描述**: 清晰简洁的事件说明
5. **恢复过程**: 详细的恢复步骤和时间点
6. **恢复完成时间**: 精确的恢复完成时间
7. **根本原因**: RCA (Root Cause Analysis)
8. **预防措施**: 包含完成时间的预防措施
9. **相关文档**: 链接到相关技术文档或代码仓库
**模板**:
```markdown
# YYYY-MM-DD 事件标题
## 事件概述 ## 事件概述
- **发生时间**: YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ - **发生时间**: YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ
@@ -890,83 +703,11 @@ YYYY-MM-DD HH:MM:SS - 验证通过
记录手动运维操作时应包含: 记录手动运维操作时应包含:
```markdown ```markdown
## YYYY-MM-DD HH:MM 操作记录
**操作时间**: YYYY-MM-DD HH:MM:SS TZ ## 详细内容
**操作人**: qiudl / Claude
**操作类型**: 数据库操作 / 服务重启 / 配置变更 / 部署发布
**操作内容**: 简短描述
### 详细步骤 以下章节已移到 [references/details.md](references/details.md):
1. **步骤 1** (HH:MM:SS) - 备注
```bash - 配置变更
具体命令 - YYYY-MM-DD HH:MM 操作记录
```
结果: [...]
2. **步骤 2** (HH:MM:SS)
```bash
具体命令
```
结果: [...]
### 验证结果
**验证时间**: YYYY-MM-DD HH:MM:SS TZ
**验证结果**: ✅ 成功 / ❌ 失败 / ⚠️ 部分成功
[验证详情...]
### 回滚方案
如需回滚,执行以下步骤:
```bash
回滚命令
```
---
**记录时间**: YYYY-MM-DD HH:MM:SS TZ
```
### 检查清单
在创建或更新思源笔记文档时,请检查:
- [ ] 所有时间记录都包含完整时间戳(精确到秒)
- [ ] 所有时间戳都标注了时区(CST/ACDT/UTC)
- [ ] 文档包含元信息(创建时间、创建人、状态)
- [ ] 事件记录包含完整的时间范围和时间线
- [ ] 技术方案包含各阶段的时间规划
- [ ] 会议记录包含会议时间和记录完成时间
- [ ] 操作日志包含操作时间、验证时间
- [ ] 所有时间使用 24 小时制
- [ ] 时间格式一致(YYYY-MM-DD HH:MM:SS TZ)
- [ ] 跨天操作明确标注日期变化
### 为什么需要完整时间戳
1. **故障排查**: 精确时间有助于关联服务器日志和追踪问题根源
2. **时间顺序**: 明确事件的先后关系,构建完整的时间线
3. **性能分析**: 精确计算操作耗时,识别性能瓶颈
4. **审计追踪**: 完整的操作记录,满足合规要求
5. **跨时区协作**: 明确时区避免混淆(澳洲团队 vs 中国服务器)
6. **自动化集成**: 便于后续工具提取时间信息进行分析
7. **知识沉淀**: 便于后续查阅和学习,理解决策的时间背景
### 常见错误和修正
| 错误写法 | 正确写法 | 说明 |
|---------|---------|------|
| 今天下午 | 2026-01-15 14:30:00 CST | 避免模糊表述 |
| 凌晨 | 2026-01-15 02:00:00 CST | 明确具体时间 |
| 2026-01-15 | 2026-01-15 14:30:00 CST | 补充时分秒 |
| 14:30 | 2026-01-15 14:30:00 CST | 补充日期和时区 |
| 昨天 | 2026-01-14 HH:MM:SS CST | 使用具体日期 |
| 刚才 | 2026-01-15 14:28:00 CST | 记录精确时间 |
---
**规范创建时间**: 2026-01-15 07:45:00 ACDT
**最后更新时间**: 2026-01-15 07:45:00 ACDT
@@ -0,0 +1,269 @@
# skills 详细内容
## 备注
"""
# 获取或创建日记笔记本
notebooks = api.list_notebooks()
diary_nb = next((nb for nb in notebooks if nb["name"] == "日记"), None)
if not diary_nb:
diary_nb = api.create_notebook("日记")["notebook"]
# 使用 upsert_doc 避免重复创建
doc_id = api.upsert_doc(diary_nb["id"], f"/{today}", markdown)
print(f"每日笔记: {doc_id}")
```
### 场景 2: 批量导入 Markdown 文件
```python
from pathlib import Path
api = SiYuanAPI()
notebook_id = "目标笔记本ID"
md_folder = "/path/to/markdown/files"
for md_file in Path(md_folder).glob("*.md"):
with open(md_file, "r", encoding="utf-8") as f:
content = f.read()
doc_name = md_file.stem
# 使用 upsert_doc 支持增量导入,避免重复
doc_id = api.upsert_doc(notebook_id, f"/{doc_name}", content)
print(f"导入: {doc_name} -> {doc_id}")
```
### 场景 3: 搜索并汇总
```python
api = SiYuanAPI()
# 搜索所有包含"会议"的内容
results = api.search("会议")
blocks = results.get("blocks", [])
print(f"找到 {len(blocks)} 条相关内容:")
for block in blocks[:10]:
print(f"- {block.get('content', '')[:50]}...")
```
### 场景 4: 发布合同到飞书云文档
```python
from siyuan import SiYuanAPI
api = SiYuanAPI()
# 1. 从思源笔记导出合同内容
contract_doc_id = "20260202080355-2c0vgac" # 合同文档 ID
markdown_content = api.export_md(contract_doc_id)
# 2. 使用飞书 MCP 创建云文档
# 调用 feishu-doc MCP 的 create_and_write_document 工具
# - title: "物流服务合同-妗晨与名风"
# - content: markdown_content
# - folder_token: (可选,指定目标文件夹)
# 3. 完整的发布函数
def publish_to_feishu(doc_id: str, doc_title: str = None) -> dict:
"""
将思源笔记文档发布到飞书云文档
Args:
doc_id: 思源笔记文档 ID
doc_title: 飞书文档标题(可选,默认使用原文档标题)
Returns:
dict: {"siyuan_doc_id": str, "content": str, "feishu_url": str}
"""
api = SiYuanAPI()
# 导出 Markdown 内容
content = api.export_md(doc_id)
# 获取原文档标题(如果未指定)
if not doc_title:
doc_info = api.get_block(doc_id)
doc_title = doc_info.get("content", "未命名文档")
# 返回准备好的内容,由调用者使用 feishu-doc MCP 创建
return {
"siyuan_doc_id": doc_id,
"doc_title": doc_title,
"content": content
}
# 使用示例
result = publish_to_feishu("20260202080355-2c0vgac", "物流服务合同-妗晨与名风-2026")
print(f"准备发布: {result['doc_title']}")
print(f"内容长度: {len(result['content'])} 字符")
# 然后调用 feishu-doc MCP:
# mcp__feishu-doc__create_and_write_document(
# title=result["doc_title"],
# content=result["content"]
# )
```
**工作流说明**:
1. **创建合同** (biz-contract 技能) → 生成合同 Markdown 文本
2. **存储到思源** (siyuan 技能) → 保存到"商务合同"笔记本,便于预览和修改
3. **发布到飞书** (feishu-doc MCP) → 导出并创建飞书云文档
**商务合同笔记本配置**:
| 配置项 | 值 |
|--------|-----|
| 笔记本名称 | 商务合同 |
| 笔记本 ID | `20260202080313-kjtgg1j` |
| 路径规范 | `/{合同类型}/{甲方简称}-{乙方简称}-{年份}` |
---
## 配置变更
**变更时间**: 2026-01-15 01:12:00 CST
**生效时间**: 2026-01-15 01:15:00 CST
```
#### ❌ 错误示例
```markdown
- **发生时间**: 2026-01-15
- **创建时间**: 2026-01-15
- **最后更新**: 今天
- **验证时间**: 早上
```
#### 时间格式规范
| 场景 | 格式 | 示例 |
|------|------|------|
| 精确时间点 | `YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 02:00:01 CST` |
| 时间范围 | `YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ` | `2026-01-15 00:00:00 - 00:46:00 CST` |
| 跨天时间范围 | `YYYY-MM-DD HH:MM:SS - YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 23:00:00 - 2026-01-16 01:00:00 CST` |
**时区标注**:
- CST: 中国标准时间 (服务器时间、生产环境)
- ACDT: 澳大利亚中部夏令时 (au-dev 本地开发时间)
- UTC: 协调世界时 (国际标准)
### 文档元信息要求
所有运维、技术类笔记文档必须在开头包含元信息:
```markdown
# 文档标题
**创建时间**: YYYY-MM-DD HH:MM:SS TZ
**创建人**: qiudl / Claude
**最后更新**: YYYY-MM-DD HH:MM:SS TZ
**文档状态**: ✅ 已完成 / 🔄 进行中 / ⚠️ 待验证 / ❌ 已废弃
**相关项目**: 项目名称
**标签**: #运维 #数据库 #备份
---
[正文内容...]
```
### 事件记录规范
记录重大事件或故障时必须包含:
1. **事件时间**: 完整的开始和结束时间(精确到秒)
2. **严重程度**: P0/P1/P2/P3
3. **影响范围**: 具体说明受影响的系统和用户
4. **事件描述**: 清晰简洁的事件说明
5. **恢复过程**: 详细的恢复步骤和时间点
6. **恢复完成时间**: 精确的恢复完成时间
7. **根本原因**: RCA (Root Cause Analysis)
8. **预防措施**: 包含完成时间的预防措施
9. **相关文档**: 链接到相关技术文档或代码仓库
**模板**:
```markdown
# YYYY-MM-DD 事件标题
## YYYY-MM-DD HH:MM 操作记录
**操作时间**: YYYY-MM-DD HH:MM:SS TZ
**操作人**: qiudl / Claude
**操作类型**: 数据库操作 / 服务重启 / 配置变更 / 部署发布
**操作内容**: 简短描述
### 详细步骤
1. **步骤 1** (HH:MM:SS)
```bash
具体命令
```
结果: [...]
2. **步骤 2** (HH:MM:SS)
```bash
具体命令
```
结果: [...]
### 验证结果
**验证时间**: YYYY-MM-DD HH:MM:SS TZ
**验证结果**: ✅ 成功 / ❌ 失败 / ⚠️ 部分成功
[验证详情...]
### 回滚方案
如需回滚,执行以下步骤:
```bash
回滚命令
```
---
**记录时间**: YYYY-MM-DD HH:MM:SS TZ
```
### 检查清单
在创建或更新思源笔记文档时,请检查:
- [ ] 所有时间记录都包含完整时间戳(精确到秒)
- [ ] 所有时间戳都标注了时区(CST/ACDT/UTC)
- [ ] 文档包含元信息(创建时间、创建人、状态)
- [ ] 事件记录包含完整的时间范围和时间线
- [ ] 技术方案包含各阶段的时间规划
- [ ] 会议记录包含会议时间和记录完成时间
- [ ] 操作日志包含操作时间、验证时间
- [ ] 所有时间使用 24 小时制
- [ ] 时间格式一致(YYYY-MM-DD HH:MM:SS TZ)
- [ ] 跨天操作明确标注日期变化
### 为什么需要完整时间戳
1. **故障排查**: 精确时间有助于关联服务器日志和追踪问题根源
2. **时间顺序**: 明确事件的先后关系,构建完整的时间线
3. **性能分析**: 精确计算操作耗时,识别性能瓶颈
4. **审计追踪**: 完整的操作记录,满足合规要求
5. **跨时区协作**: 明确时区避免混淆(澳洲团队 vs 中国服务器)
6. **自动化集成**: 便于后续工具提取时间信息进行分析
7. **知识沉淀**: 便于后续查阅和学习,理解决策的时间背景
### 常见错误和修正
| 错误写法 | 正确写法 | 说明 |
|---------|---------|------|
| 今天下午 | 2026-01-15 14:30:00 CST | 避免模糊表述 |
| 凌晨 | 2026-01-15 02:00:00 CST | 明确具体时间 |
| 2026-01-15 | 2026-01-15 14:30:00 CST | 补充时分秒 |
| 14:30 | 2026-01-15 14:30:00 CST | 补充日期和时区 |
| 昨天 | 2026-01-14 HH:MM:SS CST | 使用具体日期 |
| 刚才 | 2026-01-15 14:28:00 CST | 记录精确时间 |
---
**规范创建时间**: 2026-01-15 07:45:00 ACDT
**最后更新时间**: 2026-01-15 07:45:00 ACDT
@@ -7,5 +7,6 @@
}, },
"install_name": "siyuan-to-feishu", "install_name": "siyuan-to-feishu",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
@@ -1,6 +1,9 @@
--- ---
name: siyuan-to-feishu name: siyuan-to-feishu
description: 将思源笔记导出为 PDF 并发送到飞书群。当用户提到"发送笔记"、"导出PDF发飞书"、"/siyuan send"、"分享到飞书"相关任务时自动激活。 description: 将思源笔记导出为 PDF 并发送到飞书群。当用户提到"发送笔记"、"导出PDF发飞书"、"/siyuan send"、"分享到飞书"相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# siyuan-to-feishu Skill # siyuan-to-feishu Skill
@@ -7,5 +7,6 @@
}, },
"install_name": "wecom", "install_name": "wecom",
"install_type": "skill", "install_type": "skill",
"dir_category": "integration" "dir_category": "integration",
"invocation": "user-invoked"
} }
+9 -304
View File
@@ -1,10 +1,10 @@
--- ---
name: wecom name: wecom
description: 企业微信集成。通过自然语言发送消息、管理群机器人、操作审批流程、管理通讯录。当用户提到企业微信、微信工作、群机器人、企业号、wecom相关任务时自动激活。 description: 企业微信集成。通过自然语言发送消息、管理群机器人、操作审批流程、管理通讯录。当用户提到企业微信、微信工作、群机器人、企业号、wecom相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
--- ---
# 企业微信集成技能
## 功能概述 ## 功能概述
### 消息推送 ### 消息推送
@@ -481,307 +481,6 @@ screenshot_and_insert("https://www.baidu.com", "YOUR_DOCID", "百度首页截图
--- ---
## 通用函数封装
### 简单文本文档
适用于报告、记录、会议纪要等。使用符号标记章节。
```python
def create_simple_doc(doc_name: str, content: str) -> dict:
"""
创建简单文本文档
Args:
doc_name: 文档名称
content: 文档内容(使用【】标记章节)
Returns:
dict: {"docid": "xxx", "url": "https://..."}
Example:
content = '''项目周报
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【本周完成】
1. 完成用户模块开发
2. 修复登录bug
【下周计划】
1. 开始订单模块
2. 编写测试用例
'''
result = create_simple_doc("2026年第5周周报", content)
"""
token = get_access_token()
# 1. 创建文档
create_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={token}"
create_resp = requests.post(create_url, json={
"doc_type": 3,
"doc_name": doc_name
}).json()
if create_resp.get("errcode") != 0:
return create_resp
docid = create_resp["docid"]
doc_url = create_resp["url"]
# 2. 写入内容
update_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"
update_resp = requests.post(update_url, json={
"docid": docid,
"requests": [{
"insert_text": {
"text": content,
"location": {"index": 0}
}
}]
}).json()
return {
"errcode": update_resp.get("errcode", 0),
"docid": docid,
"url": doc_url
}
```
### 合同类文档(带标题样式)
适用于合同、协议等需要标题层级的正式文档。
```python
import time
def create_contract_doc(doc_name: str, sections: list) -> dict:
"""
创建带标题样式的合同文档
Args:
doc_name: 文档名称
sections: 内容列表,格式 [(文本, 标题级别), ...]
标题级别: 1=主标题, 2=章节标题, 0=正文
Returns:
dict: {"docid": "xxx", "url": "https://..."}
Example:
sections = [
("物流服务合同", 1),
("合同编号: WL-2026-0201", 0),
("", 0),
("甲方(托运方)", 2),
("公司名称: xxx公司", 0),
("", 0),
("第一条 服务内容", 2),
("1.1 服务类型:货物运输", 0),
("1.2 货物类型:xxx", 0),
]
result = create_contract_doc("物流服务合同-甲方与乙方", sections)
"""
token = get_access_token()
# 1. 创建文档
create_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={token}"
create_resp = requests.post(create_url, json={
"doc_type": 3,
"doc_name": doc_name
}).json()
if create_resp.get("errcode") != 0:
return create_resp
docid = create_resp["docid"]
doc_url = create_resp["url"]
# 2. 倒序内容(因为每次在位置0插入)
reversed_sections = list(reversed(sections))
# 3. 分批插入(每批最多25个,留余量)
update_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"
batch_size = 25
for i in range(0, len(reversed_sections), batch_size):
batch = reversed_sections[i:i+batch_size]
requests_list = []
for text, level in batch:
para = {"elements": [{"text_run": {"content": text}}]}
if level > 0:
para["paragraph_style"] = {"heading_level": level}
requests_list.append({
"insert_paragraph": {
"location": {"index": 0},
"paragraph": para
}
})
resp = requests.post(update_url, json={
"docid": docid,
"requests": requests_list
}).json()
if resp.get("errcode") != 0:
return {
"errcode": resp.get("errcode"),
"errmsg": resp.get("errmsg"),
"docid": docid,
"url": doc_url
}
time.sleep(0.3) # 避免频率限制
return {
"errcode": 0,
"docid": docid,
"url": doc_url
}
def build_contract_sections(
title: str,
party_a: dict,
party_b: dict,
clauses: list,
signature: bool = True
) -> list:
"""
构建合同内容结构
Args:
title: 合同标题
party_a: 甲方信息 {"name": "", "code": "", "address": "", "phone": "", "legal_rep": ""}
party_b: 乙方信息,格式同上
clauses: 条款列表 [{"title": "第一条 xxx", "items": ["1.1 xxx", "1.2 xxx"]}, ...]
signature: 是否包含签章栏
Returns:
list: 可直接传给 create_contract_doc 的 sections
"""
sections = []
# 标题
sections.append((title, 1))
sections.append(("", 0))
# 甲方
sections.append(("甲方", 2))
if party_a.get("name"):
sections.append((f"公司名称: {party_a['name']}", 0))
if party_a.get("code"):
sections.append((f"统一社会信用代码: {party_a['code']}", 0))
if party_a.get("address"):
sections.append((f"地址: {party_a['address']}", 0))
if party_a.get("phone"):
sections.append((f"联系电话: {party_a['phone']}", 0))
if party_a.get("legal_rep"):
sections.append((f"法定代表人: {party_a['legal_rep']}", 0))
sections.append(("", 0))
# 乙方
sections.append(("乙方", 2))
if party_b.get("name"):
sections.append((f"公司名称: {party_b['name']}", 0))
if party_b.get("code"):
sections.append((f"统一社会信用代码: {party_b['code']}", 0))
if party_b.get("address"):
sections.append((f"地址: {party_b['address']}", 0))
if party_b.get("phone"):
sections.append((f"联系电话: {party_b['phone']}", 0))
if party_b.get("legal_rep"):
sections.append((f"法定代表人: {party_b['legal_rep']}", 0))
sections.append(("", 0))
# 条款
for clause in clauses:
sections.append((clause["title"], 2))
for item in clause.get("items", []):
sections.append((item, 0))
sections.append(("", 0))
# 签章
if signature:
sections.append(("签章", 2))
sections.append(("", 0))
sections.append((f"甲方(盖章): {party_a.get('name', '')}", 0))
sections.append(("法定代表人/授权代表:________________", 0))
sections.append(("日期:________________", 0))
sections.append(("", 0))
sections.append((f"乙方(盖章): {party_b.get('name', '')}", 0))
sections.append(("法定代表人/授权代表:________________", 0))
sections.append(("日期:________________", 0))
return sections
```
### 使用示例
#### 简化示例
```python
# 简化示例:使用占位符
party_a = {"name": "甲方公司名称", "code": "甲方税号"}
party_b = {"name": "乙方公司名称", "code": "乙方税号"}
clauses = [
{"title": "第一条 服务内容", "items": ["1.1 xxx", "1.2 xxx"]},
{"title": "第二条 服务期限", "items": ["2.1 xxx"]},
]
sections = build_contract_sections("合同标题", party_a, party_b, clauses)
result = create_contract_doc("合同文档名称", sections)
```
#### 完整示例
```python
# 完整示例:创建物流合同
party_a = {
"name": "重庆妗晨工贸有限公司",
"code": "91500104MA7EJTPA6D",
"address": "重庆市大渡口区跳磴镇海康路106号1-1",
"phone": "15213397998"
}
party_b = {
"name": "北京名风新能源科技有限公司",
"code": "91110106092440790K",
"legal_rep": "魏小健"
}
clauses = [
{
"title": "第一条 服务内容",
"items": [
"1.1 服务类型:货物运输配送服务",
"1.2 货物类型:今麦郎系列产品",
]
},
{
"title": "第二条 服务期限",
"items": [
"2.1 合同有效期:自 2026年2月1日 至 2027年1月31日",
]
},
{
"title": "第三条 运费标准",
"items": [
"3.1 运费标准:今麦郎产品 2.50 元/件",
"3.2 结算周期:月结",
]
}
]
# 构建并创建文档
sections = build_contract_sections("物流服务合同", party_a, party_b, clauses)
result = create_contract_doc("物流服务合同-妗晨与名风", sections)
print(f"文档链接: {result['url']}")
```
---
## 常见错误码 ## 常见错误码
| 错误码 | 说明 | 解决方案 | | 错误码 | 说明 | 解决方案 |
@@ -806,3 +505,9 @@ print(f"文档链接: {result['url']}")
| API 文档 | https://developer.work.weixin.qq.com/document/ | | API 文档 | https://developer.work.weixin.qq.com/document/ |
| 调试工具 | https://developer.work.weixin.qq.com/devtool/interface | | 调试工具 | https://developer.work.weixin.qq.com/devtool/interface |
| 管理后台 | https://work.weixin.qq.com/wework_admin/ | | 管理后台 | https://work.weixin.qq.com/wework_admin/ |
## 详细内容
以下章节已移到 [references/details.md](references/details.md):
- 通用函数封装
@@ -0,0 +1,303 @@
# skills 详细内容
## 通用函数封装
### 简单文本文档
适用于报告、记录、会议纪要等。使用符号标记章节。
```python
def create_simple_doc(doc_name: str, content: str) -> dict:
"""
创建简单文本文档
Args:
doc_name: 文档名称
content: 文档内容(使用【】标记章节)
Returns:
dict: {"docid": "xxx", "url": "https://..."}
Example:
content = '''项目周报
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【本周完成】
1. 完成用户模块开发
2. 修复登录bug
【下周计划】
1. 开始订单模块
2. 编写测试用例
'''
result = create_simple_doc("2026年第5周周报", content)
"""
token = get_access_token()
# 1. 创建文档
create_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={token}"
create_resp = requests.post(create_url, json={
"doc_type": 3,
"doc_name": doc_name
}).json()
if create_resp.get("errcode") != 0:
return create_resp
docid = create_resp["docid"]
doc_url = create_resp["url"]
# 2. 写入内容
update_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"
update_resp = requests.post(update_url, json={
"docid": docid,
"requests": [{
"insert_text": {
"text": content,
"location": {"index": 0}
}
}]
}).json()
return {
"errcode": update_resp.get("errcode", 0),
"docid": docid,
"url": doc_url
}
```
### 合同类文档(带标题样式)
适用于合同、协议等需要标题层级的正式文档。
```python
import time
def create_contract_doc(doc_name: str, sections: list) -> dict:
"""
创建带标题样式的合同文档
Args:
doc_name: 文档名称
sections: 内容列表,格式 [(文本, 标题级别), ...]
标题级别: 1=主标题, 2=章节标题, 0=正文
Returns:
dict: {"docid": "xxx", "url": "https://..."}
Example:
sections = [
("物流服务合同", 1),
("合同编号: WL-2026-0201", 0),
("", 0),
("甲方(托运方)", 2),
("公司名称: xxx公司", 0),
("", 0),
("第一条 服务内容", 2),
("1.1 服务类型:货物运输", 0),
("1.2 货物类型:xxx", 0),
]
result = create_contract_doc("物流服务合同-甲方与乙方", sections)
"""
token = get_access_token()
# 1. 创建文档
create_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={token}"
create_resp = requests.post(create_url, json={
"doc_type": 3,
"doc_name": doc_name
}).json()
if create_resp.get("errcode") != 0:
return create_resp
docid = create_resp["docid"]
doc_url = create_resp["url"]
# 2. 倒序内容(因为每次在位置0插入)
reversed_sections = list(reversed(sections))
# 3. 分批插入(每批最多25个,留余量)
update_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"
batch_size = 25
for i in range(0, len(reversed_sections), batch_size):
batch = reversed_sections[i:i+batch_size]
requests_list = []
for text, level in batch:
para = {"elements": [{"text_run": {"content": text}}]}
if level > 0:
para["paragraph_style"] = {"heading_level": level}
requests_list.append({
"insert_paragraph": {
"location": {"index": 0},
"paragraph": para
}
})
resp = requests.post(update_url, json={
"docid": docid,
"requests": requests_list
}).json()
if resp.get("errcode") != 0:
return {
"errcode": resp.get("errcode"),
"errmsg": resp.get("errmsg"),
"docid": docid,
"url": doc_url
}
time.sleep(0.3) # 避免频率限制
return {
"errcode": 0,
"docid": docid,
"url": doc_url
}
def build_contract_sections(
title: str,
party_a: dict,
party_b: dict,
clauses: list,
signature: bool = True
) -> list:
"""
构建合同内容结构
Args:
title: 合同标题
party_a: 甲方信息 {"name": "", "code": "", "address": "", "phone": "", "legal_rep": ""}
party_b: 乙方信息,格式同上
clauses: 条款列表 [{"title": "第一条 xxx", "items": ["1.1 xxx", "1.2 xxx"]}, ...]
signature: 是否包含签章栏
Returns:
list: 可直接传给 create_contract_doc 的 sections
"""
sections = []
# 标题
sections.append((title, 1))
sections.append(("", 0))
# 甲方
sections.append(("甲方", 2))
if party_a.get("name"):
sections.append((f"公司名称: {party_a['name']}", 0))
if party_a.get("code"):
sections.append((f"统一社会信用代码: {party_a['code']}", 0))
if party_a.get("address"):
sections.append((f"地址: {party_a['address']}", 0))
if party_a.get("phone"):
sections.append((f"联系电话: {party_a['phone']}", 0))
if party_a.get("legal_rep"):
sections.append((f"法定代表人: {party_a['legal_rep']}", 0))
sections.append(("", 0))
# 乙方
sections.append(("乙方", 2))
if party_b.get("name"):
sections.append((f"公司名称: {party_b['name']}", 0))
if party_b.get("code"):
sections.append((f"统一社会信用代码: {party_b['code']}", 0))
if party_b.get("address"):
sections.append((f"地址: {party_b['address']}", 0))
if party_b.get("phone"):
sections.append((f"联系电话: {party_b['phone']}", 0))
if party_b.get("legal_rep"):
sections.append((f"法定代表人: {party_b['legal_rep']}", 0))
sections.append(("", 0))
# 条款
for clause in clauses:
sections.append((clause["title"], 2))
for item in clause.get("items", []):
sections.append((item, 0))
sections.append(("", 0))
# 签章
if signature:
sections.append(("签章", 2))
sections.append(("", 0))
sections.append((f"甲方(盖章): {party_a.get('name', '')}", 0))
sections.append(("法定代表人/授权代表:________________", 0))
sections.append(("日期:________________", 0))
sections.append(("", 0))
sections.append((f"乙方(盖章): {party_b.get('name', '')}", 0))
sections.append(("法定代表人/授权代表:________________", 0))
sections.append(("日期:________________", 0))
return sections
```
### 使用示例
#### 简化示例
```python
# 简化示例:使用占位符
party_a = {"name": "甲方公司名称", "code": "甲方税号"}
party_b = {"name": "乙方公司名称", "code": "乙方税号"}
clauses = [
{"title": "第一条 服务内容", "items": ["1.1 xxx", "1.2 xxx"]},
{"title": "第二条 服务期限", "items": ["2.1 xxx"]},
]
sections = build_contract_sections("合同标题", party_a, party_b, clauses)
result = create_contract_doc("合同文档名称", sections)
```
#### 完整示例
```python
# 完整示例:创建物流合同
party_a = {
"name": "重庆妗晨工贸有限公司",
"code": "91500104MA7EJTPA6D",
"address": "重庆市大渡口区跳磴镇海康路106号1-1",
"phone": "15213397998"
}
party_b = {
"name": "北京名风新能源科技有限公司",
"code": "91110106092440790K",
"legal_rep": "魏小健"
}
clauses = [
{
"title": "第一条 服务内容",
"items": [
"1.1 服务类型:货物运输配送服务",
"1.2 货物类型:今麦郎系列产品",
]
},
{
"title": "第二条 服务期限",
"items": [
"2.1 合同有效期:自 2026年2月1日 至 2027年1月31日",
]
},
{
"title": "第三条 运费标准",
"items": [
"3.1 运费标准:今麦郎产品 2.50 元/件",
"3.2 结算周期:月结",
]
}
]
# 构建并创建文档
sections = build_contract_sections("物流服务合同", party_a, party_b, clauses)
result = create_contract_doc("物流服务合同-妗晨与名风", sections)
print(f"文档链接: {result['url']}")
```
---
+12 -1
View File
@@ -1,7 +1,8 @@
--- ---
name: req-design name: req-design
description: 需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。 description: 需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。
arguments: <REQ-ID> metadata:
arguments: <REQ-ID>
--- ---
# 需求开发设计 Skill (req-design) # 需求开发设计 Skill (req-design)
@@ -474,3 +475,13 @@ PRD 说「做什么」(用户故事、业务规则、验收标准),设计
| V1.0 | 2026-01-26 | 初始版本(原名 req-dev) | | V1.0 | 2026-01-26 | 初始版本(原名 req-dev) |
| V2.0 | 2026-04-06 | 重构为 req-design:移除架构指南和编码规范,聚焦 API 契约 + 任务拆分 | | V2.0 | 2026-04-06 | 重构为 req-design:移除架构指南和编码规范,聚焦 API 契约 + 任务拆分 |
| V2.1 | 2026-04-16 | 新增 Memory 隔离规则(REQ-20260416-0017) | | V2.1 | 2026-04-16 | 新增 Memory 隔离规则(REQ-20260416-0017) |
## 任务依赖边(blocking edges)
拆子任务时,每个任务必须显式声明依赖,而不是只给层级:
- 每个任务写 `blockers: [任务编号...]`,没有依赖就写 `blockers: []`;
- **被阻塞的任务不得先开工**,即使它看起来更简单;
- 依赖图落进需求的任务描述,供 ai-proj / PM 检查;
- 出现循环依赖直接拒绝拆分方案,重新设计边界;
- 验收时检查:所有 blockers 是否 completed,再开自己。
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
{ {
"name": "req-plugin", "name": "req-plugin",
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。", "description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
"version": "2.0.0", "version": "2.1.0",
"author": { "author": {
"name": "qiudl" "name": "qiudl"
}, },
+13 -193
View File
@@ -1,13 +1,9 @@
--- ---
name: req name: req
description: 需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。 description: 需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。
arguments: <subcommand> [args] metadata:
arguments: <subcommand> [args]
--- ---
# req - 需求工作流管理
需求全流程工作流管理,与 ai-proj CLI 深度集成。
## 需求生命周期 ## 需求生命周期
**审批状态**(status): **审批状态**(status):
@@ -48,6 +44,7 @@ analysis → design → dev → review → testing → [待部署池] → releas
- **操作前先确认实际 ID** — 从 URL 提取 ID(如 `/requirements/897` → ID=897) - **操作前先确认实际 ID** — 从 URL 提取 ID(如 `/requirements/897` → ID=897)
- **务实路线关闭也必须补全关联任务** — 每个进度条阶段需创建关联任务 - **务实路线关闭也必须补全关联任务** — 每个进度条阶段需创建关联任务
- **阶段内容门禁(防空转)** — `/req next` 时检查关键阶段任务是否有实质内容 - **阶段内容门禁(防空转)** — `/req next` 时检查关键阶段任务是否有实质内容
- **复用盘点先于估工(Gate 5,强制)** — PRD 必须含「现有实现与复用盘点」章节:每个能力点给出**现有实现(file:line)+ 结论(复用 / 改造 / 新增)**;结论=复用/改造的只按「接入差异」估工,结论=新增的必须写「为什么现有实现不能满足」。没有 file:line 证据、或标「未排查」的,**禁止 submit、禁止按全新增估工**;独立复查由非 PRD 写作者用 grep 复核并在 note 留命令与命中结果
- **Harness 环境检测** — `/req dev` 启动开发前,快速检测项目基础设施(`.husky/` 存在?`scripts/check-*.sh` 存在?)。若两者都不存在(Level 0),输出一行提示:「项目无质量护栏,建议先运行 `/harness init`」。检测结果不阻塞开发,仅提示 - **Harness 环境检测** — `/req dev` 启动开发前,快速检测项目基础设施(`.husky/` 存在?`scripts/check-*.sh` 存在?)。若两者都不存在(Level 0),输出一行提示:「项目无质量护栏,建议先运行 `/harness init`」。检测结果不阻塞开发,仅提示
## 禁止直接调用(必须走命令流程) ## 禁止直接调用(必须走命令流程)
@@ -55,7 +52,7 @@ analysis → design → dev → review → testing → [待部署池] → releas
| 禁止直接调用 | 必须通过 | 原因 | | 禁止直接调用 | 必须通过 | 原因 |
|-------------|---------|------| |-------------|---------|------|
| `ai-proj req create` with allowSelfApprove | `/req new` | 禁止自动提交,必须创建为 draft | | `ai-proj req create` with allowSelfApprove | `/req new` | 禁止自动提交,必须创建为 draft |
| `ai-proj req submit` (直接调用) | `/req submit` | 必须先通过 4 道门禁检查 | | `ai-proj req submit` (直接调用) | `/req submit` | 必须先通过 5 道门禁检查 |
| `ai-proj req archive` | `/req done` | 必须先执行类型化门禁检查 | | `ai-proj req archive` | `/req done` | 必须先执行类型化门禁检查 |
| `ai-proj req approve` | `/req review pass` | 必须先展示 PRD 摘要并等待用户确认 | | `ai-proj req approve` | `/req review pass` | 必须先展示 PRD 摘要并等待用户确认 |
| `ai-proj req advance --to <stage>` (force) | `/req next` | 必须先 AskUserQuestion 确认 + 记录跳过原因 | | `ai-proj req advance --to <stage>` (force) | `/req next` | 必须先 AskUserQuestion 确认 + 记录跳过原因 |
@@ -73,13 +70,15 @@ ai-proj req create \
# → 结果必定是 draft 状态 # → 结果必定是 draft 状态
``` ```
### 从 draft 到 submit 的 4 道门禁 ### 从 draft 到 submit 的 5 道门禁
``` ```
Gate 1: 需求讨论 ── description 末尾有「## 讨论结论」章节 Gate 1: 需求讨论 ── description 末尾有「## 讨论结论」章节
Gate 2: 方案确认 ── AskUserQuestion 获得「方案确认通过」 Gate 2: 方案确认 ── AskUserQuestion 获得「方案确认通过」
Gate 3: PRD 文档 ── linkRole=prd 的任务存在且有文档 Gate 3: PRD 文档 ── linkRole=prd 的任务存在且有文档
Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD 存在 Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD 存在
Gate 5: 复用盘点 ── PRD 有「现有实现与复用盘点」章节;每条能力点有 file:line 证据
或「为什么不能复用」的理由;估工按 复用/改造/新增 分别计(无盘点 = 阻断)
``` ```
## 完整工作流 ## 完整工作流
@@ -251,191 +250,6 @@ Deploy Gate 3: 证据完整 ── 验收任务有文档,含检查项表格 +
{通过 / 有条件通过 / 需修改} {通过 / 有条件通过 / 需修改}
``` ```
## 命令参考
### 撰写命令
**`/req new [标题]`** — 对话式创建需求(仅 draft)
- 参数:`--priority=high|medium|low`,`--project=<name>`,`--category=feature|bug|improvement`
- 流程:描述→提取关键信息→用户故事→验收标准→`ai-proj req create`→ Gate 1 需求讨论
**`/req draft <描述>`** — 一句话快速创建 draft,AI 自动补全
**`/req edit [REQ-ID]`** — 编辑需求:`ai-proj req get` → 询问修改点 → `ai-proj req update` → 显示变更摘要
**`/req check [REQ-ID]`** — 完整性检查(Gate 4):
```
✅ 三段式结构完整 / ✅ 验收标准: 4 条 / ✅ 讨论结论已记录 / ❌ PRD 文档缺失
```
**`/req split [REQ-ID]`** — 拆分为开发任务(后端/前端/测试)并关联
### 原型设计
**`/req prototype [REQ-ID]`** — 基于 PRD 生成 Stitch 原型:
1. 读取 PRD 文档,提取 UI 相关描述
2. 创建 Stitch 项目 + 生成页面(调用 req-prototype 子技能)
3. 截图回填 PRD「4.2 界面原型」章节
- 参数:`--device [desktop|mobile|tablet]`,`--model [pro|flash]`
**`/req prototype edit [REQ-ID] --prompt "..."`** — 修改已有原型
**`/req prototype variant [REQ-ID]`** — 生成设计变体供选择
### 提交评审
**`/req submit [REQ-ID]`** ⭐ — 从 draft 到 pending 的唯一合法路径:
1. Gate 1: description 末尾有「## 讨论结论」
2. Gate 2: 讨论结论经过用户确认
3. Gate 3: 有 linkRole=prd 任务且附有文档
4. Gate 4: 执行 `/req check` 全部通过
5. → `ai-proj req submit --id <id>` → 展示 PRD 摘要 → 等待评审
**`/req review pass [REQ-ID]`** — 评审通过(必须用户明确确认后才调用 `ai-proj req approve --id <id>`)
**`/req review reject [REQ-ID] [原因]`** — 评审驳回,必须提供原因
### 阶段管理
**`/req` 或 `/req list`** — 按状态分组列出所有需求(`ai-proj req list`)
**`/req status [REQ-ID]`** — 查看需求详情(`ai-proj req get --id <id>`)、进度、关联任务
**`/req next [REQ-ID]`** — 推进到下一阶段:
1. 检查当前阶段任务完成度
2. 智能跳阶段检测(无 implementation 任务时建议跳过 review/testing)
3. `ai-proj req advance --id <id> --to <stage>`(门禁检查,未通过→AskUserQuestion,**禁止 AI 自动 force**)
4. ⭐ 内容门禁检查(review/testing/staging 阶段)
5. 自动创建目标阶段建议任务
6. 展示:「✅ 已创建: #XXXX {任务名}。如需撤销请说明」
**dev → review 门禁**:
- `pr_created`: PR 已创建
- `quality_gate`: CI 质量门禁通过记录(质量门禁未通过时提示:「CI 质量门禁未通过,请先运行 `/req ci`」)
**阶段顺序**:`analysis → design → dev → review → testing → [待部署池] → released`
**`/req resume [REQ-ID]`** — 会话断点恢复:`ai-proj req get --id <id>` 获取 delivery_stage + 任务状态 + 建议操作
### 开发命令
**`/req dev [REQ-ID]`** — 启动开发:
1. `ai-proj req tasks --id <id>` 找 implementation 任务
2. `ai-proj task start --id <task_id>` 更新任务状态为开发中
3. `/pr start` 从 origin/main 建分支
**`/req cr [REQ-ID]`** — 代码评审(前置:开发任务完成):
1. `ai-proj req tasks --id <id>` 确认所有 implementation 任务已完成
2. `git diff` / `find` 确定变更范围(文件数、行数)
3. 读取所有变更文件源码(非 test 文件)
4. **五视角扫描**:逐一用攻击者/泄露者/并发者/边界者/依赖者视角审查
5. **约定检查**:运行所有 `scripts/check-*.sh --ci`,将结果写入 CR 报告
6. **⭐ Bug 约定建议**(bug 类需求自动触发:category=bug,或标题含 fix/修复/bug,或描述含根因/复现步骤):
- 分析本次 bug 的根因模式
- 判断可检测性:能写 grep 找到同类问题?(规则见 convention-flow.md「可检测性判断」)
- 不可检测 → 仅在 CR 报告记录根因,跳过
- 可检测 → 检查已有 `scripts/check-*.sh` 是否已覆盖 → 已覆盖则跳过
- 扫描代码库统计同类模式数量 N
- AskUserQuestion(N=0: 防未来复发;N>0: 防恶化+逐步清理)
- 是 → 执行 convention flow → 产出物**单独 commit**(`chore: 建立 {name} 约定`)
7. `ai-proj task create` 创建【代码评审】任务并关联需求(linkRole=code_review)
8. `ai-proj create-and-attach` 附加 CR 报告文档(必须含五视角扫描结果 + 约定检查结果)
9. 展示发现摘要,AskUserQuestion 确认是否创建 bug 修复任务
10. 若有 High/Critical 发现 → `ai-proj task create` 创建关联修复任务
**`/req ci [REQ-ID]`** — CI 质量门禁检查与自动修复:
1. `check-ci-status.sh` 检查当前分支 CI 状态
- 通过 → 写入 `quality_gate` 门禁证据(`gate-callback.sh`)→ 完成
- 运行中 → `--wait` 等待最多 5 分钟
- 失败 → 进入自动修复循环
2. 自动修复循环(最多 3 轮):
a. `fetch-ci-logs.sh --failed-only` 获取失败 job 日志
b. AI 诊断根因(见错误模式目录)
c. 自动修复代码
d. 本地验证(`go vet ./...` / `npx tsc --noEmit`)
e. `git commit && git push`
f. `check-ci-status.sh --trigger --wait` 等待新 CI
g. 通过 → 写入门禁证据 → 完成
h. 仍失败 → 下一轮(超过 3 轮 → AskUserQuestion 是否继续)
- 参数:`--trigger` 手动触发 CI,`--wait` 等待完成,`--no-fix` 仅诊断不修复
**错误模式目录**:
| 失败 Job | 错误模式 | 修复策略 |
|----------|---------|---------|
| architecture-gate | 架构 ratchet 违规 | 读取违规文件,用 service 层替代直接 database import |
| backend-lint (go vet) | `file.go:42: unreachable code` | 读取文件,修复具体问题 |
| backend-lint (gofmt) | `Files need gofmt:` | 运行 `gofmt -s -w` |
| backend-lint (go test) | `--- FAIL: TestXxx` | 读取测试和实现,修复根因 |
| frontend-lint (ESLint) | `warnings N > 4600` | 对比 main,只修复新增警告 |
| frontend-lint (Prettier) | `unformatted N > 1000` | `npx prettier --write` 变更文件 |
| frontend-lint (TS) | `errors N > 60` | 对比 main,只修复新增类型错误 |
**`/req test [REQ-ID]`** — 测试(前置:代码评审通过),遵循 dev-test 技能的 5-Gate 流程
**`/req deploy [--project <name>] [--env production]`** — 项目级批量部署(Staging 已自动化,push develop 即触发):
1. 收集待部署需求(delivery_stage=testing + 全 test 任务 completed)
2. AskUserQuestion 确认范围
3. `ai-proj task create` 创建【部署】批次任务(linkRole=deploy)
4. 部署前检查(变更检测、数据库迁移)
5. 执行 `./scripts/build-and-push.sh prod --detect --deploy --wait --verify`
6. **Deploy Gate 1: 健康检查** — `/health` 返回 200
7. **Deploy Gate 2+3: PDV 验收**(为每个需求创建独立验收任务):
a. `ai-proj task create --title "【验收】PDV: {需求标题}"` 创建验收任务
b. `ai-proj req link --id <req_id> --task-ids <task_id> --link-role verification` 关联需求
c. 从需求关联任务中提取前端变更范围(页面路由、菜单项、关键 API)
d. 生成验收检查清单(页面可达 + 菜单可见 + 基础渲染 + API 连通)
e. 用 Playwright 对部署环境执行冒烟测试:`E2E_BASE_URL=<部署环境URL> npx playwright test e2e/pdv/ --project=chromium`
f. 收集截图证据
g. `ai-proj task append-doc --id <task_id>` 附加验收文档(检查项表格 + 截图 + 结论)
h. **全部通过** → `ai-proj task complete --id <task_id>` → Gate 2+3 放行
i. **任一失败** → 任务保持 in_progress,**阻断推进**,报告失败项
8. `ai-proj task append-doc` 记录【部署】任务文档(构建日志 + 健康检查 + PDV 结果摘要)
9. `ai-proj req advance --id <id> --to released` 批量推进(仅 Gate 1-3 全部通过的需求)
**`/req done [REQ-ID]`** — 类型化归档门禁 + git commit + push + `ai-proj req archive --id <id>`:
**Gate 0(强制):AC 验收逐条确认** — 在所有类型检查之前执行:
1. `ai-proj req get --id <id>` 获取需求描述
2. 提取 `## 验收标准` 章节下的所有 `- [ ]` / `- [x]` 条目
3. **无验收标准**:AskUserQuestion 警告并要求用户确认为何无 AC,必须说明跳过原因才能继续
4. **有未勾选项(`- [ ]`)**:逐条列出,AskUserQuestion 请用户对每条提供验证证据(截图/curl 输出/日志)或明确标记为 N/A + 说明原因
5. **全部已勾选(`- [x]`)**:展示 AC 摘要,直接继续
6. AC 确认通过后,将每条 AC 的验证结果追加到需求描述(`ai-proj req update`),格式:`- [x] {原AC内容}(验证于 YYYY-MM-DD)`
**类型检查(Gate 0 通过后)**:
- **推断类型**:有 implementation → code;无 implementation 有 prd/test → skill;仅 deploy → ops
- **code 检查**:delivery_stage=released + deploy 任务完成 + verification 任务完成(PDV 通过) + 部署文档 + 所有任务完成
- **skill 检查**:delivery_stage≥testing + 所有任务完成
- **ops 检查**:deploy 任务完成 + 所有任务完成
### PRD 文档创建(通过任务中转)
```bash
# create-and-attach 只支持任务 ID(task_documents 外键约束)
# 1. 创建任务
ai-proj task create --title "【评审】PRD: 用户认证功能"
# 2. 关联需求
ai-proj req link --id 711 --task-ids 5214
# 3. 附加文档到任务
ai-proj task append-doc --id 5214 --content "# PRD: 用户认证功能\n..."
```
### 阶段任务 Checklist
| 阶段 | 建议任务 | linkRole |
|------|----------|----------|
| analysis | 【评审】PRD: {需求标题} | prd |
| design | 【评审】技术设计: {需求标题} | design |
| dev | 【开发-后端】{需求标题}、【开发-前端】{需求标题} | implementation |
| review | 【代码评审】{需求标题} | code_review |
| review | 【约定】{约定名称}(bug 类需求,convention flow 自动创建) | documentation |
| testing | 【测试】集成测试: {需求标题} | test |
| deploy | 由 `/req deploy` 批量创建 | deploy |
| released | 【验收】PDV: {需求标题}(`/req deploy` 自动创建) | verification |
## 测试环境流程 ## 测试环境流程
``` ```
@@ -503,3 +317,9 @@ ai-proj req tasks --id <id> # 查看关联任务
| `req-prototype` | Stitch 原型生成 + 迭代 | | `req-prototype` | Stitch 原型生成 + 迭代 |
| `dev-test` | 测试 + 质量门禁 | | `dev-test` | 测试 + 质量门禁 |
| `req-test-gate` (harness) | 工程约束方法论。Gate 0-2 的约定检查由项目本地脚本定义,方法论见 `/harness` 命令 | | `req-test-gate` (harness) | 工程约束方法论。Gate 0-2 的约定检查由项目本地脚本定义,方法论见 `/harness` 命令 |
## 详细内容
以下章节已移到 [references/details.md](references/details.md):
- 命令参考
@@ -0,0 +1,187 @@
# skills 详细内容
## 命令参考
### 撰写命令
**`/req new [标题]`** — 对话式创建需求(仅 draft)
- 参数:`--priority=high|medium|low`,`--project=<name>`,`--category=feature|bug|improvement`
- 流程:描述→提取关键信息→用户故事→验收标准→`ai-proj req create`→ Gate 1 需求讨论
**`/req draft <描述>`** — 一句话快速创建 draft,AI 自动补全
**`/req edit [REQ-ID]`** — 编辑需求:`ai-proj req get` → 询问修改点 → `ai-proj req update` → 显示变更摘要
**`/req check [REQ-ID]`** — 完整性检查(Gate 4):
```
✅ 三段式结构完整 / ✅ 验收标准: 4 条 / ✅ 讨论结论已记录 / ❌ PRD 文档缺失
```
**`/req split [REQ-ID]`** — 拆分为开发任务(后端/前端/测试)并关联
### 原型设计
**`/req prototype [REQ-ID]`** — 基于 PRD 生成 Stitch 原型:
1. 读取 PRD 文档,提取 UI 相关描述
2. 创建 Stitch 项目 + 生成页面(调用 req-prototype 子技能)
3. 截图回填 PRD「4.2 界面原型」章节
- 参数:`--device [desktop|mobile|tablet]`,`--model [pro|flash]`
**`/req prototype edit [REQ-ID] --prompt "..."`** — 修改已有原型
**`/req prototype variant [REQ-ID]`** — 生成设计变体供选择
### 提交评审
**`/req submit [REQ-ID]`** ⭐ — 从 draft 到 pending 的唯一合法路径:
1. Gate 1: description 末尾有「## 讨论结论」
2. Gate 2: 讨论结论经过用户确认
3. Gate 3: 有 linkRole=prd 任务且附有文档
4. Gate 4: 执行 `/req check` 全部通过
5. → `ai-proj req submit --id <id>` → 展示 PRD 摘要 → 等待评审
**`/req review pass [REQ-ID]`** — 评审通过(必须用户明确确认后才调用 `ai-proj req approve --id <id>`)
**`/req review reject [REQ-ID] [原因]`** — 评审驳回,必须提供原因
### 阶段管理
**`/req` 或 `/req list`** — 按状态分组列出所有需求(`ai-proj req list`)
**`/req status [REQ-ID]`** — 查看需求详情(`ai-proj req get --id <id>`)、进度、关联任务
**`/req next [REQ-ID]`** — 推进到下一阶段:
1. 检查当前阶段任务完成度
2. 智能跳阶段检测(无 implementation 任务时建议跳过 review/testing)
3. `ai-proj req advance --id <id> --to <stage>`(门禁检查,未通过→AskUserQuestion,**禁止 AI 自动 force**)
4. ⭐ 内容门禁检查(review/testing/staging 阶段)
5. 自动创建目标阶段建议任务
6. 展示:「✅ 已创建: #XXXX {任务名}。如需撤销请说明」
**dev → review 门禁**:
- `pr_created`: PR 已创建
- `quality_gate`: CI 质量门禁通过记录(质量门禁未通过时提示:「CI 质量门禁未通过,请先运行 `/req ci`」)
**阶段顺序**:`analysis → design → dev → review → testing → [待部署池] → released`
**`/req resume [REQ-ID]`** — 会话断点恢复:`ai-proj req get --id <id>` 获取 delivery_stage + 任务状态 + 建议操作
### 开发命令
**`/req dev [REQ-ID]`** — 启动开发:
1. `ai-proj req tasks --id <id>` 找 implementation 任务
2. `ai-proj task start --id <task_id>` 更新任务状态为开发中
3. `/pr start` 从 origin/main 建分支
**`/req cr [REQ-ID]`** — 代码评审(前置:开发任务完成):
1. `ai-proj req tasks --id <id>` 确认所有 implementation 任务已完成
2. `git diff` / `find` 确定变更范围(文件数、行数)
3. 读取所有变更文件源码(非 test 文件)
4. **五视角扫描**:逐一用攻击者/泄露者/并发者/边界者/依赖者视角审查
5. **约定检查**:运行所有 `scripts/check-*.sh --ci`,将结果写入 CR 报告
6. **⭐ Bug 约定建议**(bug 类需求自动触发:category=bug,或标题含 fix/修复/bug,或描述含根因/复现步骤):
- 分析本次 bug 的根因模式
- 判断可检测性:能写 grep 找到同类问题?(规则见 convention-flow.md「可检测性判断」)
- 不可检测 → 仅在 CR 报告记录根因,跳过
- 可检测 → 检查已有 `scripts/check-*.sh` 是否已覆盖 → 已覆盖则跳过
- 扫描代码库统计同类模式数量 N
- AskUserQuestion(N=0: 防未来复发;N>0: 防恶化+逐步清理)
- 是 → 执行 convention flow → 产出物**单独 commit**(`chore: 建立 {name} 约定`)
7. `ai-proj task create` 创建【代码评审】任务并关联需求(linkRole=code_review)
8. `ai-proj create-and-attach` 附加 CR 报告文档(必须含五视角扫描结果 + 约定检查结果)
9. 展示发现摘要,AskUserQuestion 确认是否创建 bug 修复任务
10. 若有 High/Critical 发现 → `ai-proj task create` 创建关联修复任务
**`/req ci [REQ-ID]`** — CI 质量门禁检查与自动修复:
1. `check-ci-status.sh` 检查当前分支 CI 状态
- 通过 → 写入 `quality_gate` 门禁证据(`gate-callback.sh`)→ 完成
- 运行中 → `--wait` 等待最多 5 分钟
- 失败 → 进入自动修复循环
2. 自动修复循环(最多 3 轮):
a. `fetch-ci-logs.sh --failed-only` 获取失败 job 日志
b. AI 诊断根因(见错误模式目录)
c. 自动修复代码
d. 本地验证(`go vet ./...` / `npx tsc --noEmit`)
e. `git commit && git push`
f. `check-ci-status.sh --trigger --wait` 等待新 CI
g. 通过 → 写入门禁证据 → 完成
h. 仍失败 → 下一轮(超过 3 轮 → AskUserQuestion 是否继续)
- 参数:`--trigger` 手动触发 CI,`--wait` 等待完成,`--no-fix` 仅诊断不修复
**错误模式目录**:
| 失败 Job | 错误模式 | 修复策略 |
|----------|---------|---------|
| architecture-gate | 架构 ratchet 违规 | 读取违规文件,用 service 层替代直接 database import |
| backend-lint (go vet) | `file.go:42: unreachable code` | 读取文件,修复具体问题 |
| backend-lint (gofmt) | `Files need gofmt:` | 运行 `gofmt -s -w` |
| backend-lint (go test) | `--- FAIL: TestXxx` | 读取测试和实现,修复根因 |
| frontend-lint (ESLint) | `warnings N > 4600` | 对比 main,只修复新增警告 |
| frontend-lint (Prettier) | `unformatted N > 1000` | `npx prettier --write` 变更文件 |
| frontend-lint (TS) | `errors N > 60` | 对比 main,只修复新增类型错误 |
**`/req test [REQ-ID]`** — 测试(前置:代码评审通过),遵循 dev-test 技能的 5-Gate 流程
**`/req deploy [--project <name>] [--env production]`** — 项目级批量部署(Staging 已自动化,push develop 即触发):
1. 收集待部署需求(delivery_stage=testing + 全 test 任务 completed)
2. AskUserQuestion 确认范围
3. `ai-proj task create` 创建【部署】批次任务(linkRole=deploy)
4. 部署前检查(变更检测、数据库迁移)
5. 执行 `./scripts/build-and-push.sh prod --detect --deploy --wait --verify`
6. **Deploy Gate 1: 健康检查** — `/health` 返回 200
7. **Deploy Gate 2+3: PDV 验收**(为每个需求创建独立验收任务):
a. `ai-proj task create --title "【验收】PDV: {需求标题}"` 创建验收任务
b. `ai-proj req link --id <req_id> --task-ids <task_id> --link-role verification` 关联需求
c. 从需求关联任务中提取前端变更范围(页面路由、菜单项、关键 API)
d. 生成验收检查清单(页面可达 + 菜单可见 + 基础渲染 + API 连通)
e. 用 Playwright 对部署环境执行冒烟测试:`E2E_BASE_URL=<部署环境URL> npx playwright test e2e/pdv/ --project=chromium`
f. 收集截图证据
g. `ai-proj task append-doc --id <task_id>` 附加验收文档(检查项表格 + 截图 + 结论)
h. **全部通过** → `ai-proj task complete --id <task_id>` → Gate 2+3 放行
i. **任一失败** → 任务保持 in_progress,**阻断推进**,报告失败项
8. `ai-proj task append-doc` 记录【部署】任务文档(构建日志 + 健康检查 + PDV 结果摘要)
9. `ai-proj req advance --id <id> --to released` 批量推进(仅 Gate 1-3 全部通过的需求)
**`/req done [REQ-ID]`** — 类型化归档门禁 + git commit + push + `ai-proj req archive --id <id>`:
**Gate 0(强制):AC 验收逐条确认** — 在所有类型检查之前执行:
1. `ai-proj req get --id <id>` 获取需求描述
2. 提取 `## 验收标准` 章节下的所有 `- [ ]` / `- [x]` 条目
3. **无验收标准**:AskUserQuestion 警告并要求用户确认为何无 AC,必须说明跳过原因才能继续
4. **有未勾选项(`- [ ]`)**:逐条列出,AskUserQuestion 请用户对每条提供验证证据(截图/curl 输出/日志)或明确标记为 N/A + 说明原因
5. **全部已勾选(`- [x]`)**:展示 AC 摘要,直接继续
6. AC 确认通过后,将每条 AC 的验证结果追加到需求描述(`ai-proj req update`),格式:`- [x] {原AC内容}(验证于 YYYY-MM-DD)`
**类型检查(Gate 0 通过后)**:
- **推断类型**:有 implementation → code;无 implementation 有 prd/test → skill;仅 deploy → ops
- **code 检查**:delivery_stage=released + deploy 任务完成 + verification 任务完成(PDV 通过) + 部署文档 + 所有任务完成
- **skill 检查**:delivery_stage≥testing + 所有任务完成
- **ops 检查**:deploy 任务完成 + 所有任务完成
### PRD 文档创建(通过任务中转)
```bash
# create-and-attach 只支持任务 ID(task_documents 外键约束)
# 1. 创建任务
ai-proj task create --title "【评审】PRD: 用户认证功能"
# 2. 关联需求
ai-proj req link --id 711 --task-ids 5214
# 3. 附加文档到任务
ai-proj task append-doc --id 5214 --content "# PRD: 用户认证功能\n..."
```
### 阶段任务 Checklist
| 阶段 | 建议任务 | linkRole |
|------|----------|----------|
| analysis | 【评审】PRD: {需求标题} | prd |
| design | 【评审】技术设计: {需求标题} | design |
| dev | 【开发-后端】{需求标题}、【开发-前端】{需求标题} | implementation |
| review | 【代码评审】{需求标题} | code_review |
| review | 【约定】{约定名称}(bug 类需求,convention flow 自动创建) | documentation |
| testing | 【测试】集成测试: {需求标题} | test |
| deploy | 由 `/req deploy` 批量创建 | deploy |
| released | 【验收】PDV: {需求标题}(`/req deploy` 自动创建) | verification |
@@ -1,7 +1,7 @@
{ {
"name": "req-prd-plugin", "name": "req-prd-plugin",
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。", "description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
"version": "2.2.0", "version": "2.3.0",
"author": { "author": {
"name": "qiudl" "name": "qiudl"
}, },
+20 -171
View File
@@ -2,9 +2,6 @@
name: req-prd name: req-prd
description: 产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。 description: 产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。
--- ---
# 产品需求设计 Skill (req-prd)
## 概述 ## 概述
本技能用于辅助产品设计和需求管理工作,包括: 本技能用于辅助产品设计和需求管理工作,包括:
@@ -137,6 +134,21 @@ description: 产品设计与需求管理。用于 PRD、需求分析、用户故
- 会议/邮件/聊天记录链接 - 会议/邮件/聊天记录链接
- 客户特殊约束(如"必须在 Q2 前上线") - 客户特殊约束(如"必须在 Q2 前上线")
### 1.5 现有实现与复用盘点 ⭐ 强制
> **目的**:防止把「已经有的能力」当成新增来估工和开发(真实事故:DS Bot 早已有头像上传/裁剪功能,PRD 仍按新建估工)。
> **检索证据**:每条都要给 `file:line`(或 grep 命令 + 命中结果),不接受「凭印象认为没有」。
| 能力点 | 现有实现(file:line) | 结论 | 说明 |
|--------|----------------------|------|------|
| 头像上传/裁剪 | `ds-bot/src/client/bot-settings.js:xx` | 复用 | 只改存储落点与主体 |
| …(本次涉及的每个能力点都要有一行) | | | |
**硬规则**:
- 结论是 **复用 / 改造** 的,估工只算「接入差异」,不许按全新功能算。
- 结论是 **新增** 的,必须写「为什么现有实现不能满足」。
- 标「未排查」或没有 `file:line` 的 PRD **不得提交评审**;独立复查人(非 PRD 作者)要用 grep 复核并在 note 留证据。
## 2. 用户分析 ## 2. 用户分析
### 2.1 目标用户 ### 2.1 目标用户
[用户画像描述] [用户画像描述]
@@ -454,104 +466,6 @@ mcp__ai-proj__export_task_document_to_file
--- ---
## 功能设计流程
### 1. 需求收集
```
输入:
- 用户反馈
- 业务需求
- 数据分析
- 竞品分析
输出:
- 需求池(ai-proj 需求列表)
```
若命中“模块设计访谈模式”,本阶段改为执行访谈协议并持续写入 ai-proj 讨论文档;访谈未确认前不进入 PRD 定稿。
### 2. 需求分析
```
方法:
- 5W1H 分析法
- 用户访谈
- 数据验证
输出:
- 需求文档
- 优先级排序
```
### 3. 方案设计
```
内容:
- 功能架构
- 交互流程
- 界面原型
- 技术方案
输出:
- PRD 文档
- 可生成原型的界面状态与交互规格
```
### 4. 缺陷收敛
```
输入:
- 已确认讨论结论
- 最新版完整 PRD
执行:
- defect-analysis 全维度审计
- 接受项修订 PRD
- 对修订后的完整 PRD 重新审计,直至一轮 0 个新缺陷
输出:
- 已收敛 PRD
- 缺陷处置记录
```
### 5. HTML 原型与反馈闭环
```
适用:
- 所有包含界面、用户操作或可视状态的模块
执行:
- 调用 req-prototype 的 upload 模式生成独立 HTML
- 上传并关联 Requirement
- iframe 回填 PRD,验证访问和关键交互
- 请用户评审;行为性反馈回到问答 → PRD → defect-analysis → 新原型版本
输出:
- 已验证、已关联的 HTML 原型
- PRD 与讨论文档中的版本/反馈/确认记录
```
### 6. 评审验证
```
评审维度:
- 业务价值
- 技术可行性
- 资源评估
- 风险评估
输出:
- 评审结论
- 修改意见
```
模块设计访谈模式下,本阶段必须调用 `defect-analysis`,并按访谈协议将每轮发现、处置、PRD 修订和收敛结论回写到同一讨论文档。
UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用户对二者的联合确认;无 UI 模块则核对已记录的不适用理由和用户确认。
---
## 竞品分析 ## 竞品分析
> 竞品分析模板已移至 `req-compare` 插件。涉及竞品对比时自动激活。 > 竞品分析模板已移至 `req-compare` 插件。涉及竞品对比时自动激活。
@@ -590,51 +504,6 @@ UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用
--- ---
## 设计检查清单
### PRD 完整性检查
- [ ] 模块/系统设计已完成单轮单问访谈,且全过程已写入 ai-proj 讨论文档
- [ ] 讨论记录与 PRD 均已保存到仓库本地 Markdown 和对应 ai-proj Task Document
- [ ] 两端复读正文一致,交付说明包含本地路径、task/document 标识和远程版本
- [ ] 讨论结论已由用户明确确认
- [ ] `defect-analysis` 已基于最新版 PRD 收敛到一轮 0 个新缺陷
- [ ] 无未处置的致命/高严重度缺陷
- [ ] UI 模块 HTML 原型已生成、上传并关联 Requirement;无 UI 模块已记录不适用理由和用户确认
- [ ] 原型基线指向最新版 PRD,PRD `4.2` 已回填 iframe、版本说明和验证结果
- [ ] 原型反馈导致的行为变更已回到问答、PRD 和 defect 全量重审,并生成新原型版本
- [ ] 用户已联合确认最终 PRD 与 HTML 原型
- [ ] 背景与目标明确
- [ ] 用户群体定义清晰
- [ ] 功能需求完整
- [ ] 验收标准可测试(每条 AC 附带 VP-Data / VP-Steps / VP-Pass)
- [ ] 异常情况已考虑
- [ ] 性能要求已定义
- [ ] 上线计划合理
- [ ] 风险已评估
### 交互设计检查
- [ ] 用户流程完整
- [ ] HTML 原型覆盖核心入口、主流程及 PRD 指定的关键状态
- [ ] 原型 URL 可访问,Requirement 关联可读取,iframe 可展示,核心交互可操作
- [ ] 边界情况处理
- [ ] 错误提示友好
- [ ] 反馈及时
- [ ] 操作可撤销
- [ ] 符合用户习惯
### 非功能需求检查
- [ ] 性能要求已量化(响应时间、并发量)
- [ ] 安全需求已明确(权限、数据保护)
- [ ] 兼容性要求已定义(浏览器、设备)
- [ ] 可用性目标已设定
> **技术方案可行性检查**在 design 阶段由 `req-design` 技能完成。
---
## 常用工具 ## 常用工具
### 原型设计 ### 原型设计
@@ -681,31 +550,11 @@ UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用
--- ---
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。** ## 详细内容
### 禁止行为 以下章节已移到 [references/details.md](references/details.md):
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
### 允许行为 - 功能设计流程
- memory 可影响**交互风格**(提问详略、确认节奏、语气) - 设计检查清单
- memory 可指导**命令选择**(如根据用户习惯推荐先走 req-compare 还是 req-prd) - Memory 隔离规则(强制,源自 devflow-claude 借鉴)
- memory 可影响**非产出文本**(如对话中的说明)
### Why
auto-memory 设计用于跨会话建立用户画像。但 PRD/需求文档是正式产出物,必须由**模板结构 + 当前输入**决定,不能因 memory 中的偏好自作主张调整结构,否则会导致:
- 模板章节漂移(用户不知道为什么这次少了一章)
- 历史项目内容污染(张冠李戴)
- 产出不可复现
### How to apply
执行 `/req prd` / PRD 编写 / 需求描述生成等命令时:
- 仅读取:模板文件、用户当前输入、引用的已有需求文档
- 不读取:memory 目录下的任何文件
- 产出前自检:章节数量和顺序与模板完全一致
**参考**:devflow-claude 的 `plugins/req/commands/_common.md` 同名规则。
@@ -0,0 +1,173 @@
# skills 详细内容
## 功能设计流程
### 1. 需求收集
```
输入:
- 用户反馈
- 业务需求
- 数据分析
- 竞品分析
输出:
- 需求池(ai-proj 需求列表)
```
若命中“模块设计访谈模式”,本阶段改为执行访谈协议并持续写入 ai-proj 讨论文档;访谈未确认前不进入 PRD 定稿。
### 2. 需求分析
```
方法:
- 5W1H 分析法
- 用户访谈
- 数据验证
输出:
- 需求文档
- 优先级排序
```
### 3. 方案设计
```
内容:
- 功能架构
- 交互流程
- 界面原型
- 技术方案
输出:
- PRD 文档
- 可生成原型的界面状态与交互规格
```
### 4. 缺陷收敛
```
输入:
- 已确认讨论结论
- 最新版完整 PRD
执行:
- defect-analysis 全维度审计
- 接受项修订 PRD
- 对修订后的完整 PRD 重新审计,直至一轮 0 个新缺陷
输出:
- 已收敛 PRD
- 缺陷处置记录
```
### 5. HTML 原型与反馈闭环
```
适用:
- 所有包含界面、用户操作或可视状态的模块
执行:
- 调用 req-prototype 的 upload 模式生成独立 HTML
- 上传并关联 Requirement
- iframe 回填 PRD,验证访问和关键交互
- 请用户评审;行为性反馈回到问答 → PRD → defect-analysis → 新原型版本
输出:
- 已验证、已关联的 HTML 原型
- PRD 与讨论文档中的版本/反馈/确认记录
```
### 6. 评审验证
```
评审维度:
- 业务价值
- 技术可行性
- 资源评估
- 风险评估
输出:
- 评审结论
- 修改意见
```
模块设计访谈模式下,本阶段必须调用 `defect-analysis`,并按访谈协议将每轮发现、处置、PRD 修订和收敛结论回写到同一讨论文档。
UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用户对二者的联合确认;无 UI 模块则核对已记录的不适用理由和用户确认。
---
## 设计检查清单
### PRD 完整性检查
- [ ] 模块/系统设计已完成单轮单问访谈,且全过程已写入 ai-proj 讨论文档
- [ ] 讨论记录与 PRD 均已保存到仓库本地 Markdown 和对应 ai-proj Task Document
- [ ] 两端复读正文一致,交付说明包含本地路径、task/document 标识和远程版本
- [ ] 讨论结论已由用户明确确认
- [ ] `defect-analysis` 已基于最新版 PRD 收敛到一轮 0 个新缺陷
- [ ] 无未处置的致命/高严重度缺陷
- [ ] UI 模块 HTML 原型已生成、上传并关联 Requirement;无 UI 模块已记录不适用理由和用户确认
- [ ] 原型基线指向最新版 PRD,PRD `4.2` 已回填 iframe、版本说明和验证结果
- [ ] 原型反馈导致的行为变更已回到问答、PRD 和 defect 全量重审,并生成新原型版本
- [ ] 用户已联合确认最终 PRD 与 HTML 原型
- [ ] 背景与目标明确
- [ ] 用户群体定义清晰
- [ ] 功能需求完整
- [ ] 验收标准可测试(每条 AC 附带 VP-Data / VP-Steps / VP-Pass)
- [ ] 异常情况已考虑
- [ ] 性能要求已定义
- [ ] 上线计划合理
- [ ] 风险已评估
### 交互设计检查
- [ ] 用户流程完整
- [ ] HTML 原型覆盖核心入口、主流程及 PRD 指定的关键状态
- [ ] 原型 URL 可访问,Requirement 关联可读取,iframe 可展示,核心交互可操作
- [ ] 边界情况处理
- [ ] 错误提示友好
- [ ] 反馈及时
- [ ] 操作可撤销
- [ ] 符合用户习惯
### 非功能需求检查
- [ ] 性能要求已量化(响应时间、并发量)
- [ ] 安全需求已明确(权限、数据保护)
- [ ] 兼容性要求已定义(浏览器、设备)
- [ ] 可用性目标已设定
> **技术方案可行性检查**在 design 阶段由 `req-design` 技能完成。
---
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。**
### 禁止行为
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
### 允许行为
- memory 可影响**交互风格**(提问详略、确认节奏、语气)
- memory 可指导**命令选择**(如根据用户习惯推荐先走 req-compare 还是 req-prd)
- memory 可影响**非产出文本**(如对话中的说明)
### Why
auto-memory 设计用于跨会话建立用户画像。但 PRD/需求文档是正式产出物,必须由**模板结构 + 当前输入**决定,不能因 memory 中的偏好自作主张调整结构,否则会导致:
- 模板章节漂移(用户不知道为什么这次少了一章)
- 历史项目内容污染(张冠李戴)
- 产出不可复现
### How to apply
执行 `/req prd` / PRD 编写 / 需求描述生成等命令时:
- 仅读取:模板文件、用户当前输入、引用的已有需求文档
- 不读取:memory 目录下的任何文件
- 产出前自检:章节数量和顺序与模板完全一致
**参考**:devflow-claude 的 `plugins/req/commands/_common.md` 同名规则。
@@ -1,11 +1,9 @@
--- ---
name: req-test-gate name: req-test-gate
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。 description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。
arguments: <subcommand> [args] metadata:
arguments: <subcommand> [args]
--- ---
# 测试与质量门禁制度 (req-test-gate) v2.1
## 概述 ## 概述
三层质量门禁架构,确保从需求到上线每个阶段都有结构化验证。 三层质量门禁架构,确保从需求到上线每个阶段都有结构化验证。
@@ -348,48 +346,6 @@ Task({
### 完整模板(scope = backend / fullstack) ### 完整模板(scope = backend / fullstack)
```markdown ```markdown
## 测试报告 - {需求标题}
| 字段 | 值 |
|------|-----|
| 需求 | REQ-XXXX |
| 类型 | {code/skill/ops} |
| 时间 | {YYYY-MM-DD HH:MM} |
| 门禁版本 | test-gate v2 |
### 前置条件 (Gate 1)
- CR 状态: ✅ 已通过
- 开发任务: ✅ 全部完成 ({N}/{N})
### 测试执行 (Gate 2)
| 序号 | 阶段 | 测试项 | 命令/方法 | 结果 | 备注 |
|------|------|--------|----------|------|------|
| T1 | 2A | 后端编译 | ... | ✅/❌/⏭️ | ... |
| IT1 | 2C | 前端代理连通 | ... | ✅/❌/⏭️ | ... |
| SM1 | 2D | 健康检查 | ... | ✅/❌/⏭️ | ... |
| PC1 | 2E | 结构完整性 | ... | ✅/❌/⏭️ | ... |
### 跳过的测试
⏭️ {测试项}: {原因}
(无则写「无」)
### 质量验证 (Gate 3)
{子代理结果摘要,或「未触发(ops 类型)」}
### 回归贡献 (Gate 5)
{贡献的回归用例数,或「无需贡献」或「⏭️ 跳过: 过渡期」}
### 总结
通过: {N}/{M} | 跳过: {K} | 结论: ✅ 通过 / ❌ 未通过
```
### 文档质量门禁(被 `/req next` 检查)
- 含测试项表格(`|` 分隔符 ≥ 3 行)
- 含通过/失败结论(`✅ 通过` 或 `❌ 未通过`)
- 总字符数 ≥ 200
- 门禁版本标记(`test-gate v2` 或 `test-gate v2.1`)
## Gate 5: 回归贡献检查 ## Gate 5: 回归贡献检查
**确保 code 类型需求为回归池贡献测试用例。按 scope 区分贡献形式。** **确保 code 类型需求为回归池贡献测试用例。按 scope 区分贡献形式。**
@@ -504,49 +460,10 @@ v3: Gate 0A(scope) + 0B(约定检查) → Gate 1 → Gate 2(按scope裁剪)
--- ---
## Harness Engineering — 工程约束方法论
**项目级基础设施**的建立和维护方法论,与上述需求级门禁互补。 ## 详细内容
> 详见 [harness-engineering.md](harness-engineering.md) 完整文档。 以下章节已移到 [references/details.md](references/details.md):
### 核心理念 - 测试报告 - {需求标题}
- Harness Engineering — 工程约束方法论
**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题。**
三大支柱:
1. **Ratchet** — 遗留违规数只能减不能增 → [ratchet-pattern.md](ratchet-pattern.md)
2. **约定建立** — 每个 bug 修复后立即建立约定和检测 → [convention-flow.md](convention-flow.md)
3. **门禁层级** — 按项目成熟度逐级引入 → [project-bootstrap.md](project-bootstrap.md)
### `/harness` 命令集(手动可用 + 自动嵌入)
| 命令 | 手动 | 自动触发 |
|------|------|---------|
| `/harness assess` | 随时 | 首次 `/req dev` |
| `/harness init [level]` | 随时 | — |
| `/harness convention [name]` | 随时 | `/req cr` + bug 类型需求 |
| `/harness report` | 随时 | `/req test` Gate 0B |
### 模板
`templates/` 目录提供可复用的脚本模板:
- [ratchet-script.md](templates/ratchet-script.md) — Ratchet 脚本模板(check/baseline/report 三命令)
- [convention-script.md](templates/convention-script.md) — 约定检测脚本模板(grep 模式匹配)
- [gates-doc.md](templates/gates-doc.md) — GATES.md 文档模板
- [pre-commit-config.md](templates/pre-commit-config.md) — husky + lint-staged 配置(Node.js/Go/Monorepo)
### 与需求级门禁的关系
```
Harness(项目级护栏) req-test-gate(需求级门禁)
├── .husky/ pre-commit ├── Gate 0A: scope 分级
├── CI lint workflow ├── Gate 0B: 运行 harness 的 check-*.sh ←─ 连接点
├── scripts/check-*.sh ├── Gate 1: 前置条件
├── CLAUDE.md 约定 ├── Gate 2: 测试执行
│ ├── Gate 3: 质量验证
│ ├── Gate 4: 文档持久化
│ └── Gate 5: 回归贡献
```
> Harness 不使用 Gate 编号。它的产出物(check-*.sh 脚本)通过 Gate 0B 接入 req-test-gate 流程。
@@ -0,0 +1,90 @@
# skills 详细内容
## 测试报告 - {需求标题}
| 字段 | 值 |
|------|-----|
| 需求 | REQ-XXXX |
| 类型 | {code/skill/ops} |
| 时间 | {YYYY-MM-DD HH:MM} |
| 门禁版本 | test-gate v2 |
### 前置条件 (Gate 1)
- CR 状态: ✅ 已通过
- 开发任务: ✅ 全部完成 ({N}/{N})
### 测试执行 (Gate 2)
| 序号 | 阶段 | 测试项 | 命令/方法 | 结果 | 备注 |
|------|------|--------|----------|------|------|
| T1 | 2A | 后端编译 | ... | ✅/❌/⏭️ | ... |
| IT1 | 2C | 前端代理连通 | ... | ✅/❌/⏭️ | ... |
| SM1 | 2D | 健康检查 | ... | ✅/❌/⏭️ | ... |
| PC1 | 2E | 结构完整性 | ... | ✅/❌/⏭️ | ... |
### 跳过的测试
⏭️ {测试项}: {原因}
(无则写「无」)
### 质量验证 (Gate 3)
{子代理结果摘要,或「未触发(ops 类型)」}
### 回归贡献 (Gate 5)
{贡献的回归用例数,或「无需贡献」或「⏭️ 跳过: 过渡期」}
### 总结
通过: {N}/{M} | 跳过: {K} | 结论: ✅ 通过 / ❌ 未通过
```
### 文档质量门禁(被 `/req next` 检查)
- 含测试项表格(`|` 分隔符 ≥ 3 行)
- 含通过/失败结论(`✅ 通过` 或 `❌ 未通过`)
- 总字符数 ≥ 200
- 门禁版本标记(`test-gate v2` 或 `test-gate v2.1`)
## Harness Engineering — 工程约束方法论
**项目级基础设施**的建立和维护方法论,与上述需求级门禁互补。
> 详见 [harness-engineering.md](harness-engineering.md) 完整文档。
### 核心理念
**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题。**
三大支柱:
1. **Ratchet** — 遗留违规数只能减不能增 → [ratchet-pattern.md](ratchet-pattern.md)
2. **约定建立** — 每个 bug 修复后立即建立约定和检测 → [convention-flow.md](convention-flow.md)
3. **门禁层级** — 按项目成熟度逐级引入 → [project-bootstrap.md](project-bootstrap.md)
### `/harness` 命令集(手动可用 + 自动嵌入)
| 命令 | 手动 | 自动触发 |
|------|------|---------|
| `/harness assess` | 随时 | 首次 `/req dev` |
| `/harness init [level]` | 随时 | — |
| `/harness convention [name]` | 随时 | `/req cr` + bug 类型需求 |
| `/harness report` | 随时 | `/req test` Gate 0B |
### 模板
`templates/` 目录提供可复用的脚本模板:
- [ratchet-script.md](templates/ratchet-script.md) — Ratchet 脚本模板(check/baseline/report 三命令)
- [convention-script.md](templates/convention-script.md) — 约定检测脚本模板(grep 模式匹配)
- [gates-doc.md](templates/gates-doc.md) — GATES.md 文档模板
- [pre-commit-config.md](templates/pre-commit-config.md) — husky + lint-staged 配置(Node.js/Go/Monorepo)
### 与需求级门禁的关系
```
Harness(项目级护栏) req-test-gate(需求级门禁)
├── .husky/ pre-commit ├── Gate 0A: scope 分级
├── CI lint workflow ├── Gate 0B: 运行 harness 的 check-*.sh ←─ 连接点
├── scripts/check-*.sh ├── Gate 1: 前置条件
├── CLAUDE.md 约定 ├── Gate 2: 测试执行
│ ├── Gate 3: 质量验证
│ ├── Gate 4: 文档持久化
│ └── Gate 5: 回归贡献
```
> Harness 不使用 Gate 编号。它的产出物(check-*.sh 脚本)通过 Gate 0B 接入 req-test-gate 流程。
@@ -0,0 +1,13 @@
{
"name": "verification-before-completion-plugin",
"description": "汇报完成/关闭任务/提交 PR 前必须先跑验证并附证据;没有证据不得声称完成。",
"version": "0.1.0",
"author": {
"name": "qiudl"
},
"install_name": "verification-before-completion",
"install_type": "skill",
"dir_category": "req",
"status": "deprecated",
"invocation": "model-invoked"
}
@@ -0,0 +1,55 @@
---
name: verification-before-completion
description: 汇报「完成/修好/通过」或关闭任务、提交 PR、宣布 REQ 完成之前,必须先运行验证命令并附证据;没有证据不得声称完成。当准备说完成、关闭任务、或汇报交付前使用。
metadata:
invocation: model-invoked
owner: qiudl
---
# 完成前验证
## 0. 一句话
**没有证据的「完成」不算完成。**
## 1. 何时用
- 准备说「搞定了 / 修好了 / 通过了 / 可以发了」
- 准备把任务标 `completed`、关 issue、提 PR
- 准备宣布 REQ 完成或可发布
- 收到「你确认一下」这类要求时
何时不用:还在探索阶段、明确记录的是中间发现(这时要写「未验证」而不是「完成」)。
## 2. 证据三件套
1. **命令**:原样可复制,别人能重跑
2. **输出关键行**:可读文本,不要只看截图
3. **判定**:退出码 / 通过数 / 失败数
格式:`验证:<命令> → <结果>(通过 N / 失败 M),证据:<日志路径或输出片段>`
## 3. 按类型的验证清单
| 改动类型 | 必须验证 |
| --- | --- |
| 代码改动 | 构建 + 单测 + 类型检查(有则跑) |
| bug 修复 | 先复现(证明坏)→ 修复 → 同一用例再跑(证明好) |
| 需求级交付 | 验收标准逐条打勾 + PDV 验收通过 |
| 发布/部署 | 健康检查 + E2E + 回滚预案可用 |
| 配置/文档 | 生效验证(如 `--dump-config` 结果、命令输出) |
| 数据变更 | 变更前后数据抽查 + 回滚脚本验证 |
## 4. 反模式
- 「应该可以」「本地没问题」「理论上过了」
- 只贴成功截图,不贴命令
- 跳过失败用例不说明
- 把「编译通过」当成「功能可用」
- 把「测试跑了一部分」说成「全部通过」
## 5. 与 ai-proj 对接
- 任务标 `completed` 前,把证据挂到该任务文档;
- REQ 未 `released` 时不得报需求完成(见 `ai-proj-helper-entry` 的交付终点);
- 证据缺失时,正确做法是状态写 `in_progress` 并说明「还差哪项验证」。
@@ -0,0 +1,24 @@
{
"skill_name": "verification-before-completion",
"skill_version": "0.1.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "我改完了登录接口,可以提交了吗",
"expected_output": "要求给出构建/单测命令与结果、失败数;附证据路径后再提交"
},
{
"id": 2,
"kind": "edge_case",
"prompt": "测试环境没有依赖装不上,先合了吧,本地我手动看过没问题",
"expected_output": "拒绝以「手动看过」作为证据;说明缺失依赖要记录为未验证项,状态保持 in_progress"
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "测试跑过了 10 个,还有 2 个没跑,能说全部通过吗",
"expected_output": "不能:必须写明通过 10 / 未跑 2 及原因,不得声称全部通过"
}
]
}
+1
View File
@@ -49,6 +49,7 @@ PLUGIN_MAP = {
"karpathy-guidelines-plugin": ("karpathy-guidelines", "skill", "dev"), "karpathy-guidelines-plugin": ("karpathy-guidelines", "skill", "dev"),
"pull-request-plugin": ("pull-request", "skill", "dev"), "pull-request-plugin": ("pull-request", "skill", "dev"),
"review-checklist-plugin": ("review-checklist", "skill", "dev"), "review-checklist-plugin": ("review-checklist", "skill", "dev"),
"slark-dsh-cicd-plugin": ("slark-dsh-cicd", "skill", "dev"),
# skills-integration # skills-integration
"data-excel-plugin": ("data-excel", "skill", "integration"), "data-excel-plugin": ("data-excel", "skill", "integration"),