Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
237631147a | ||
|
|
7d47762303 | ||
|
|
a7a753b833 | ||
|
|
aecd2ce521 | ||
|
|
41a1032ccf | ||
|
|
d5bdfcfb7c | ||
|
|
d43b414fc9 | ||
|
|
dde146995e | ||
|
|
d5550d62c5 | ||
|
|
c74fe71f0a | ||
|
|
2e0f7fe68c | ||
|
|
3ffee89840 | ||
|
|
998b22e905 | ||
|
|
826bba8485 | ||
|
|
f5c5bd3f40 | ||
|
|
4e6ae9036a | ||
|
|
3b38deb078 | ||
|
|
16e58047a0 | ||
|
|
dec25562a4 |
@@ -401,6 +401,32 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "slark-cicd-plugin",
|
||||
"source": "./skills-dev/slark-cicd-plugin",
|
||||
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
|
||||
"version": "1.1.0",
|
||||
"category": "devops",
|
||||
"keywords": [
|
||||
"devops",
|
||||
"deployment",
|
||||
"operations"
|
||||
],
|
||||
"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",
|
||||
"source": "./skills-req/req-audit-plugin",
|
||||
@@ -470,7 +496,7 @@
|
||||
"name": "req-plugin",
|
||||
"source": "./skills-req/req-plugin",
|
||||
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
@@ -482,8 +508,8 @@
|
||||
{
|
||||
"name": "req-prd-plugin",
|
||||
"source": "./skills-req/req-prd-plugin",
|
||||
"description": "产品需求设计技能。覆盖问答访谈、PRD、缺陷收敛及 HTML 原型验证闭环。纯产品视角,不含技术实现。",
|
||||
"version": "2.1.0",
|
||||
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
|
||||
"version": "2.3.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
@@ -495,8 +521,8 @@
|
||||
{
|
||||
"name": "req-prototype-plugin",
|
||||
"source": "./skills-req/req-prototype-plugin",
|
||||
"description": "原型生成与关联。支持 HTML 正式交付、Requirement 关联、iframe 验证闭环及 Stitch AI 视觉探索。",
|
||||
"version": "2.1.0",
|
||||
"description": "原型生成与关联。支持 HTML 本地留源、OSS 正式交付、Requirement/iframe 验证及 Stitch AI。",
|
||||
"version": "2.2.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# 已拒绝:为某一种 harness 单开分支
|
||||
|
||||
- 状态:拒绝(长期有效)
|
||||
- 日期:2026-10-31
|
||||
|
||||
## 想法
|
||||
|
||||
为 Codex / DSH / Claude 某一个客户端的特例,单独复制一份技能或加一个 harness 分支。
|
||||
|
||||
## 为什么拒绝
|
||||
|
||||
1. 复制出来的两份技能会立刻开始漂移,评测也要做两遍;
|
||||
2. 客户端差异应交给安装器(目录、调用模式、字段映射)处理,不该写进技能内容;
|
||||
3. 特例需求通常是个人的工作流调整,属于 `AGENTS.md` / `CLAUDE.md` 或 fork 的范围。
|
||||
|
||||
## 允许的替代路径
|
||||
|
||||
- 客户端差异 → 安装器加 target / 字段映射;
|
||||
- 个人流程 → 个人目录或 fork。
|
||||
@@ -0,0 +1,20 @@
|
||||
# 已拒绝:新增「随手想到」的技能
|
||||
|
||||
- 状态:拒绝(长期有效)
|
||||
- 日期:2026-10-31
|
||||
|
||||
## 想法
|
||||
|
||||
「我觉得应该有个技能做 X」——从想象出发新增技能,而不是从真实失败出发。
|
||||
|
||||
## 为什么拒绝
|
||||
|
||||
1. 技能目录是每次会话启动就要付的上下文预算,加一个就多一份;
|
||||
2. 没有真实失败做依据的技能,写出来是泛泛而谈,实际触发率极低;
|
||||
3. 技能之间会互相抢触发(description 相似),新增一个会稀释已有技能的准确率。
|
||||
|
||||
## 允许的替代路径
|
||||
|
||||
- 先用现有技能做一遍真实任务,记录卡点;
|
||||
- 确实组合不出来,再按 `SCOPE.md` 门槛提,附真实失败证据;
|
||||
- 只是个人偏好 → 放 `skills-personal/`,不进团队库。
|
||||
@@ -0,0 +1,19 @@
|
||||
# 已拒绝:把个人工作流调优塞进团队技能
|
||||
|
||||
- 状态:拒绝(长期有效)
|
||||
- 日期:2026-10-31
|
||||
|
||||
## 想法
|
||||
|
||||
把某个人的偏好(提交信息格式、分支命名、审查语气、工具习惯)直接写进团队技能。
|
||||
|
||||
## 为什么拒绝
|
||||
|
||||
1. 团队技能要能被所有人用,个人偏好会变成隐式规训;
|
||||
2. 一旦写进技能,改起来要走 PR,个人改不动;
|
||||
3. 这类偏好本来就该放个人 `AGENTS.md` / `CLAUDE.md`。
|
||||
|
||||
## 允许的替代路径
|
||||
|
||||
- 个人偏好 → 个人配置文件或 `skills-personal/`;
|
||||
- 如果是团队共识(不是个人偏好),走 `SCOPE.md` 门槛并附真实失败。
|
||||
@@ -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 个警告**。
|
||||
@@ -52,3 +52,10 @@ skills:
|
||||
- `skills-personal/` 不被 Git 跟踪,用于存放个人配置和工具
|
||||
- 其余 `skills-*` 目录均由 Git 版本控制
|
||||
- 只有受 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`。
|
||||
|
||||
@@ -195,3 +195,18 @@ A: SSE 模式直连远程服务器,零依赖开箱即用(推荐)。stdio
|
||||
- **管理员**:Donglin Lai (qiudl)
|
||||
- **邮箱**:qiudl@zhiyuncai.com
|
||||
- **仓库**: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` 技能降级而来)。
|
||||
|
||||
@@ -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。
|
||||
@@ -67,3 +67,24 @@ Claude Code 需要显式选择目标:
|
||||
**如何移除技能?**
|
||||
|
||||
删除插件目录、重新生成 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
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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)
|
||||
- 不得把「某步骤完成」当结束;不得无证据声称完成
|
||||
- 不得把凭据/内网地址/绝对路径写进技能或文档
|
||||
@@ -60,7 +60,7 @@ def load_config():
|
||||
def get_category_and_keywords(plugin_name):
|
||||
if any(x in plugin_name for x in ['dev-', 'coding', 'frontend']):
|
||||
return "development", ["development", "coding", "workflow"]
|
||||
elif any(x in plugin_name for x in ['ops-', 'deploy', 'server']):
|
||||
elif any(x in plugin_name for x in ['ops-', 'deploy', 'server', 'slark-cicd', 'slark-dsh-cicd']):
|
||||
return "devops", ["devops", "deployment", "operations"]
|
||||
elif any(x in plugin_name for x in ['ai-proj', 'req']):
|
||||
return "productivity", ["project-management", "tasks", "requirements"]
|
||||
@@ -96,6 +96,14 @@ def scan_plugins(directory, source_prefix, disabled):
|
||||
with open(manifest_path) as 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)
|
||||
|
||||
plugins.append({
|
||||
|
||||
+97
-5
@@ -5,7 +5,11 @@
|
||||
# ./install-skills.sh [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
|
||||
# --category <cat> Only install plugins in dir_category=<cat>
|
||||
# Valid values: biz, core, dev, integration, personal, req
|
||||
@@ -30,6 +34,10 @@ FORCE=false
|
||||
CLEANUP=false
|
||||
LIST_ONLY=false
|
||||
INSTALL_ACTION=false
|
||||
LINK_MODE=false
|
||||
AUDIT_ONLY=false
|
||||
INCLUDE_BETA=false
|
||||
NO_VALIDATE=false
|
||||
|
||||
# ── Colour helpers ─────────────────────────────────────────────────────────────
|
||||
GREEN='\033[0;32m'
|
||||
@@ -48,9 +56,13 @@ dry() { echo -e "${YELLOW}[dry]${RESET} $*"; }
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--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 ;;
|
||||
--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 ;;
|
||||
--cleanup) CLEANUP=true ;;
|
||||
--list) LIST_ONLY=true ;;
|
||||
@@ -77,13 +89,21 @@ case "$AGENT_TARGET" in
|
||||
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}"
|
||||
;;
|
||||
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)
|
||||
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.claude/skills}"
|
||||
COMMANDS_DIR="${AI_PROJ_HELPER_COMMANDS_DIR:-${HOME}/.claude/commands}"
|
||||
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
|
||||
;;
|
||||
esac
|
||||
@@ -281,8 +301,19 @@ install_plugin() {
|
||||
effective_install_type="skill"
|
||||
fi
|
||||
|
||||
local status
|
||||
status="$(read_field "$json_path" status)"
|
||||
status="${status:-promoted}"
|
||||
|
||||
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
|
||||
fi
|
||||
|
||||
@@ -305,6 +336,21 @@ install_plugin() {
|
||||
return
|
||||
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.
|
||||
# Legacy state is adopted automatically only when the target is missing or
|
||||
# already identical to the repository source.
|
||||
@@ -365,7 +411,7 @@ install_plugin() {
|
||||
else
|
||||
mkdir -p "$dst_dir"
|
||||
# rsync resolved source (handles nested skills/ structures)
|
||||
rsync -a --delete "$src_dir/" "$dst_dir/"
|
||||
rsync -a --checksum --delete "$src_dir/" "$dst_dir/"
|
||||
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
|
||||
ok "$install_name → skill (v$version)"
|
||||
INSTALL_ACTION=true
|
||||
@@ -415,6 +461,40 @@ cleanup_removed() {
|
||||
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() {
|
||||
if [[ "$LIST_ONLY" == true ]]; then
|
||||
@@ -429,7 +509,19 @@ main() {
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$AUDIT_ONLY" == true ]]; then
|
||||
audit_target
|
||||
return
|
||||
fi
|
||||
|
||||
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"
|
||||
[[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER"
|
||||
|
||||
|
||||
Executable
+106
@@ -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())
|
||||
@@ -0,0 +1,5 @@
|
||||
# 安全扫描允许清单(人工确认过的误报)
|
||||
# 格式:<路径子串>::<匹配文本中的特征串>
|
||||
# 例:某技能文档把危险命令当作“禁止清单”引用,而非真的要执行。
|
||||
skills-dev/slark-cicd-plugin::NEVER_AUTO
|
||||
skills-dev/slark-cicd-plugin::destructive
|
||||
Executable
+82
@@ -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
|
||||
Executable
+162
@@ -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_type": "skill",
|
||||
"dir_category": "biz"
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: biz-contract
|
||||
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_type": "skill",
|
||||
"dir_category": "biz"
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: biz-ops
|
||||
description: 商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 商务运营 (biz-ops)
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "biz-plan",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz"
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: biz-plan
|
||||
description: 商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 商业计划书撰写技能 (biz-plan)
|
||||
|
||||
## 概述
|
||||
|
||||
本技能用于辅助商业计划书的撰写与优化,特别适用于 B2B SaaS、供应链平台、产业互联网等领域。
|
||||
@@ -400,361 +400,9 @@ description: 商业计划书撰写。支持融资BP、战略规划、商业模
|
||||
|
||||
---
|
||||
|
||||
## PPT 设计能力
|
||||
|
||||
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
|
||||
## 详细内容
|
||||
|
||||
### PPT 输出方式
|
||||
以下章节已移到 [references/details.md](references/details.md):
|
||||
|
||||
当用户要求生成 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 文件完全离线可用,无需网络
|
||||
- PPT 设计能力
|
||||
|
||||
@@ -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_type": "skill",
|
||||
"dir_category": "biz"
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: finance
|
||||
description: 财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 财务对账技能
|
||||
|
||||
## 功能概述
|
||||
|
||||
- **银行对账**: 银行流水 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. **数据格式统一**: 确保日期、金额格式一致后再对账
|
||||
@@ -946,3 +495,9 @@ else:
|
||||
- **data-excel**: 处理复杂的 Excel 数据转换
|
||||
- **feishu**: 将对账报告保存到飞书云文档
|
||||
- **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 安装/审计命令"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,11 +2,6 @@
|
||||
name: ai-proj
|
||||
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 查看项目列表
|
||||
@@ -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 获取日报
|
||||
@@ -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 快速登录
|
||||
@@ -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
|
||||
@@ -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(一句话)
|
||||
2. 关键点 2(一句话)
|
||||
@@ -1246,139 +715,15 @@ await update_requirement({
|
||||
- 第2层:组件名(职责)
|
||||
- 第3层:组件名(职责)
|
||||
|
||||
## 实施周期
|
||||
X周(阶段1 + 阶段2 + 阶段3)
|
||||
```
|
||||
|
||||
**详细内容放在关联文档**:
|
||||
- 需求背景与业务价值
|
||||
- 详细的技术方案
|
||||
- 实施路线图与时间规划
|
||||
- 风险评估与应对措施
|
||||
- 验收标准
|
||||
## 详细内容
|
||||
|
||||
**Markdown 格式注意事项**:
|
||||
以下章节已移到 [references/details.md](references/details.md):
|
||||
|
||||
```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 字以内
|
||||
- 详细内容使用关联文档
|
||||
- 复杂格式改为纯文本
|
||||
- 六、每日聚焦
|
||||
- 八、项目手册
|
||||
- 十一、远程同步
|
||||
- 自然语言操作示例
|
||||
- Google 日历集成配置
|
||||
- 十三、最佳实践与故障排查
|
||||
- 实施周期
|
||||
|
||||
@@ -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_type": "skill",
|
||||
"dir_category": "core"
|
||||
"dir_category": "core",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
---
|
||||
name: publish
|
||||
description: 发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。
|
||||
metadata:
|
||||
user_invocable: true
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 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)
|
||||
- [Multi-Agent Systems 设计模式](https://arxiv.org/abs/2308.00352)
|
||||
- [Claude Code Skills 文档](https://docs.anthropic.com/claude-code/skills)
|
||||
|
||||
## 并行编排流程
|
||||
|
||||
把大任务拆给多个子 agent 时按这个顺序:
|
||||
|
||||
1. **先划写入边界**:每个子 agent 拥有互不重叠的目录/文件;共享文件(锁文件、入口文件)由编排者最后统一改。
|
||||
2. **子 agent 必须新上下文**:不要 fork 父上下文,否则它继承了本该甩掉的包袱。
|
||||
3. **每个子任务给验收标准 + 回报格式**:三句话(结果 / 证据路径 / 下一步)。
|
||||
4. **子 agent 不做合并与发布**:只交分支或补丁,合并由编排者做。
|
||||
5. **汇总时先跑集成验证**,再回报;冲突由编排者解,不让子 agent 互改。
|
||||
6. 两个子 agent 不得写同一文件;不确定时先串行。
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
name: ai-chat
|
||||
description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。
|
||||
metadata:
|
||||
arguments: <subcommand> [args]
|
||||
---
|
||||
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "ai-proj-cicd-release",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
"dir_category": "dev",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
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.
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# AI-Proj CI/CD release
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "ai-proj-macos-release",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
"dir_category": "dev",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
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.
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# AI-Proj macOS release
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "dev-android",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
"dir_category": "dev",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: dev-android
|
||||
description: Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 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
|
||||
```
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
name: dev-deploy
|
||||
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) |
|
||||
| 11 | SSH 长连接断开 | xcodebuild 3-4 分钟无输出,Tailscale 断连。用 nohup 后台执行 |
|
||||
| 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_type": "skill",
|
||||
"dir_category": "dev"
|
||||
"dir_category": "dev",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: dev-ios
|
||||
description: iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# iOS 开发插件 (dev-ios)
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "dev-pda",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
"dir_category": "dev",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: dev-pda
|
||||
description: PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# PDA 应用开发插件 (dev-pda)
|
||||
|
||||
@@ -303,3 +303,13 @@ mcp__ai-proj__link_tasks_to_requirement({
|
||||
4. **严重度准确** — 不要所有问题都标 High,按实际影响分级
|
||||
5. **建议可操作** — 每个发现必须附带具体修复建议
|
||||
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/` 下的集成测试。
|
||||
|
||||
识别方式:提取文件名中的功能前缀(如 `impersonation`),若在三个目录中均出现,则触发集成测试生成。
|
||||
|
||||
## TDD 循环(红 → 绿 → 重构)
|
||||
|
||||
1. **一次只做一个行为**:写一个小测试,先确认它**失败**,并确认失败原因就是你要修的那件事(不是编译错误/环境问题)。
|
||||
2. **最小实现让它变绿**,不做顺手重构。
|
||||
3. **绿了再重构**,重构期间测试保持绿。
|
||||
4. 禁止先写实现再补测试;补的测试要能证明它真的会红(临时改坏一次验证)。
|
||||
5. 交付时附:测试命令、通过/失败数、以及「曾经红过」的证据(日志或截图记录)。
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
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.
|
||||
metadata:
|
||||
arguments: "[component|page|storybook] <description>"
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "slark-cicd-plugin",
|
||||
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
|
||||
"version": "1.1.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
},
|
||||
"install_name": "slark-cicd",
|
||||
"install_type": "skill",
|
||||
"dir_category": "dev"
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
---
|
||||
name: slark-cicd
|
||||
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、复现或验证运行时行为、或排查发布链/推送路由问题时使用。
|
||||
---
|
||||
|
||||
# Slark CI/CD runbook
|
||||
|
||||
驱动 Slark 从改代码到上线的端到端流程。权威流程与完整细节见仓库文档
|
||||
`docs/cicd-staging-production.md`(本技能命令与之一致);本文件是可直接照做的分阶段清单。
|
||||
|
||||
仓库:`qiudl/qiu-slark`(本机克隆通常在 `/Users/donglinlai/coding/slark`)。所有命令在**仓库根**执行。
|
||||
`ai-proj` 相关命令加 `GODEBUG=netdns=go` 前缀。
|
||||
|
||||
## 铁律(先记死)
|
||||
|
||||
- GitHub `origin/main` 是唯一代码权威、合并入口与生产发布来源;`internal` 只用于 CI/镜像,永不发布。
|
||||
- 生产发布只接受**最新 `origin/main` 的精确 40 位 SHA**;`HEAD != origin/main` 会被 `deploy.sh` 拒绝。
|
||||
- 生产发布必须在**普通 clone** 中执行,且 `.git` 必须是目录;`release-prod.sh` / `deploy.sh` 会拒绝
|
||||
`.git` 为指针文件的 git worktree。不要污染现有 checkout:主目录脏或被占用时,新建干净的临时 clone。
|
||||
- **生产发布前必须先发布并验收 staging,不得跳过**:同一最新 `origin/main` 精确 SHA 必须先完成迁移演练、
|
||||
staging 正式部署、隔离检查和需求功能验收。把证据报告给用户后,必须再次取得明确的生产确认;开始 staging
|
||||
前获得的生产授权不能替代这次确认。未通过、未完成或用户未确认时停止,不得执行生产发布命令。
|
||||
- 审批式部署,永不自动:亲手敲的 `sudo`/`rm -rf`/`dd`/`curl|sh`/包安装是 `destructive`(NEVER_AUTO),
|
||||
**每次都要人审**;命中审批闸就**等人批**,绝不绕过、绝不把这些 key 加进 auto-approve 白名单。
|
||||
- staging 与生产硬隔离:staging 脚本带生产 IP 拒运行护栏、不 push origin、不反向 rsync。
|
||||
- 当前 Slark staging 权威目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e`
|
||||
(Name=`slark-staging`,2026-08-18 核验公网 IP `15.135.112.181`)。公网 IP 自动分配,发布前必须按
|
||||
实例 ID 重查并确认 Name/状态;不得凭历史 IP 判断目标。
|
||||
- `82.157.141.202` 是 ai-proj 预生产宿主机。其 `/opt/slark-staging` cohost 是隔离的 Slark 兼容验收栈,
|
||||
但不是 Slark 独立预生产;只有任务明确指定 cohost 时才能使用,不得作为普通 Slark staging 的默认目标。
|
||||
- staging 发布若影响 `packages/daemon`、`packages/runtime`、Server/daemon 公共协议或 Desktop 公共运行链,
|
||||
必须把 `server-staging` 与 `daemon-staging` 更新到同一精确 SHA;两者版本不一致不得宣告 staging 成功。
|
||||
- 只修 PR 范围内的 CI 失败;不为过检去改 CI/workflow 或做无关改动。
|
||||
|
||||
## 选择链路
|
||||
|
||||
- 「发布/上线/部署生产」→ 生产发布链:阶段 1→2→3(同 SHA staging 验收)→ 人工确认 →4→5。
|
||||
- 「Desktop/桌面版打包、发布、上传 OSS」→ 阶段 1→2 后走 `docs/desktop-oss-release.md` 的 m5max 双平台链路。
|
||||
- 「盯 CI/合并 PR」→ 阶段 1→2(用 babysit 心态循环到 green + mergeable + 评论收口)。
|
||||
- 「复现/验证运行时行为、别碰生产」→ staging 预验链(阶段 3,可独立随时跑)。
|
||||
|
||||
## 阶段 1:PR 与内网 gate(`ci/internal-gate`)
|
||||
|
||||
真实门禁是内网 CI(GitHub Actions 已停用)。检查与自查:
|
||||
|
||||
```bash
|
||||
gh pr checks <PR> # 应见 ci/internal-gate
|
||||
gh pr view <PR> --json state,mergeable,mergeStateStatus,isDraft,reviewDecision
|
||||
```
|
||||
|
||||
- 基础门(全 PR):release-contract / member-gate / authz-matrix / controlled-content-exits /
|
||||
onconflict-predicate / migration-idempotency / wsl-path-translation / typecheck / test(全量)。
|
||||
- 条件外部门(按改动路径):改 server DB/迁移/schema → `postgresql-security`;改 web → `web-e2e`;
|
||||
android / daemon+desktop 各有门。
|
||||
- 本地复跑基础门可跑部分:`pnpm -r typecheck && pnpm test`。
|
||||
|
||||
红了:只修本 PR 范围内的失败。疑似无关的合并阻塞→先 merge 最新 `main`(可能别的 PR 已修)。
|
||||
`BLOCKED: <label> requires <VAR>` = runner 环境未配外部门命令,属运维问题,不是 PR 代码问题。
|
||||
|
||||
## 阶段 2:合并到 `origin/main`
|
||||
|
||||
合并前确认:`mergeable=MERGEABLE`、`mergeStateStatus=CLEAN`、非 draft、`ci/internal-gate` 绿、评论收口。
|
||||
|
||||
```bash
|
||||
gh pr merge <PR> --merge # 仓库风格:merge commit
|
||||
```
|
||||
|
||||
若本机 `main` 被其他 worktree 占用导致 `--delete-branch` 本地 checkout 失败(属本地副作用):
|
||||
|
||||
```bash
|
||||
gh pr view <PR> --json state,mergedAt,mergeCommit # 确认 GitHub 侧 MERGED
|
||||
git push origin --delete <feature-branch> # 删远端
|
||||
git branch -D <feature-branch> # 删本地(先切走)
|
||||
```
|
||||
|
||||
## 阶段 3:staging 真机预验(生产前强制,也可独立运行)
|
||||
|
||||
生产候选必须使用最新 `origin/main` 的同一精确 SHA。先执行迁移演练,再正式部署;随后完成隔离检查和本次需求的
|
||||
功能验收。验收不能只看通用 health:凡需求涉及真实集成(如飞书 OAuth),staging 缺少对应 provider/凭据时必须
|
||||
明确标为未完成并停止生产链,除非用户在看到该限制后明确接受。完成后报告目标实例、SHA、Server/Daemon revision、
|
||||
迁移演练、健康、隔离和功能结果,并等待用户再次确认是否进入生产。
|
||||
|
||||
### 3.1 当前权威路径:Slark 独立 staging
|
||||
|
||||
稳定目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e`(Name=`slark-staging`)。它使用自动分配
|
||||
公网 IP,因此每次部署前先查询当前 IP;2026-08-18 核验值为 `15.135.112.181`:
|
||||
|
||||
```bash
|
||||
aws ec2 describe-instances --region ap-southeast-2 \
|
||||
--instance-ids i-013e5ca3fdfddc13e \
|
||||
--query 'Reservations[0].Instances[0].{Name:Tags[?Key==`Name`]|[0].Value,State:State.Name,PublicIp:PublicIpAddress}' \
|
||||
--output table
|
||||
|
||||
export STAGING_SSH_KEY=~/.ssh/cloud-server-syd.pem
|
||||
export STAGING_REMOTE=ubuntu@15.135.112.181
|
||||
export STAGING_REMOTE_IP=15.135.112.181
|
||||
export STAGING_BASE=http://15.135.112.181
|
||||
|
||||
scripts/deploy-staging.sh --migrate-dry-run
|
||||
scripts/deploy-staging.sh
|
||||
STAGING_TOKEN=$(cat /tmp/.staging_token) scripts/staging-seed.sh
|
||||
scripts/staging-verify-isolation.sh
|
||||
```
|
||||
|
||||
不得把查询到的 Name 非 `slark-staging` 的机器当作目标。四件套细节与环境隔离约束见
|
||||
`docs/req-20260811-0059-staging-environment-design.md`。
|
||||
|
||||
### 3.2 非权威路径:ai-proj cohost(仅明确指定时)
|
||||
|
||||
ai-proj cohost 位于 `ubuntu@82.157.141.202:/opt/slark-staging`。全程使用隔离数据、数据库、密钥和状态目录,
|
||||
但宿主机身份仍是 ai-proj staging;普通“Slark 预生产”不得走这里。仅在任务明确要求 cohost 兼容验收时,
|
||||
先在**干净的独立 worktree**检出待验精确 SHA,再执行:
|
||||
|
||||
```bash
|
||||
STAGING_SSH_KEY=~/.ssh/ai_proj_stg.pem \
|
||||
STAGING_KNOWN_HOSTS_FILE=~/.ssh/known_hosts \
|
||||
STAGING_REMOTE=ubuntu@82.157.141.202 \
|
||||
STAGING_REMOTE_IP=82.157.141.202 \
|
||||
scripts/deploy-staging-cohost.sh
|
||||
```
|
||||
|
||||
如果改动影响 daemon/runtime/公共协议,还需用 Compose `daemon` profile 更新 `daemon-staging`,并核对
|
||||
Server/daemon revision 同为待验 SHA。
|
||||
|
||||
### 3.3 环境选择检查
|
||||
|
||||
普通 Slark staging 默认走 3.1;只有用户或需求明确说“ai-proj cohost”才走 3.2。任何验收都必须记录:
|
||||
环境稳定身份、当次 IP、待验 40 位 SHA、Server/daemon revision/status、Server health 和目标功能结果。
|
||||
|
||||
routed-remote 要 `network=enabled` 必须同时满足:① agent `workspace_access=write` +
|
||||
`repository_access=write`;② per-agent 工作区是可写 git worktree。细节见
|
||||
`docs/req-20260811-0059-staging-environment-design.md`。staging 刻意不碰 `deploy.sh` 的生产闸门。
|
||||
|
||||
## 阶段 4:生产发布(审批式)
|
||||
|
||||
**前置门禁**:阶段 3 已对同一 SHA 完成 staging 部署和验收,证据已报告,且用户在报告之后明确确认生产发布。
|
||||
缺任一项即停止;不得把 PR 合并授权、较早的生产授权或单纯的“继续”视为验收后确认。
|
||||
|
||||
**唯一姿势**:从干净的普通 clone 发布最新 `origin/main`(`HEAD == origin/main` 精确 SHA)。staging 可用
|
||||
独立 worktree,但生产脚本为保证同步与发布边界,会拒绝 worktree;不要在用户已有脏 checkout 中清理或发布。
|
||||
|
||||
```bash
|
||||
# 若当前仓库不是普通 clone,先在安全父目录创建一次性干净 clone:
|
||||
git clone https://github.com/qiudl/qiu-slark.git <clean-production-clone>
|
||||
cd <clean-production-clone>
|
||||
|
||||
git fetch origin main
|
||||
test -d .git # 必须通过;worktree 的 .git 是文件
|
||||
git status --porcelain # 必须干净
|
||||
git checkout --detach "$(git rev-parse origin/main)" # 在产品分支上时先 detach 到该 SHA
|
||||
|
||||
pnpm release:prod # = release-prod.sh → deploy.sh --backend --no-install --no-mirror
|
||||
# 备选:scripts/deploy.sh --pull --backend
|
||||
```
|
||||
|
||||
- `deploy.sh` 用 `check-production-release-sha.sh` 锁 `HEAD == origin/main` SHA,不符即拒。
|
||||
- `--backend` 才跑迁移:owner 连接(DDL 权)与运行时 `slark_app`(DML-only)分离;幂等、失败即非零。
|
||||
- 命中审批闸(发布/sudo)→ **等人批**;不要绕。脚本内部 SSH 载荷子进程的 sudo 不触发闸。
|
||||
- 普通工作 clone 发布完切回原分支:`git checkout <branch>`;一次性发布 clone 可保留作审计,清理时用可恢复方式。
|
||||
|
||||
### HANDOFF_BLOCKED 恢复入口
|
||||
|
||||
发布准备遇到 `HANDOFF_BLOCKED`、handoff manifest=`failed_closed` 或命令状态不确定时,停止发布切换;禁止靠重跑
|
||||
发布脚本、直接改表或默认 replay 来“解卡”。先把 manifest、命令、execution、lease/outbox 和审计证据做只读盘点,
|
||||
再让用户明确授权每条记录的 reconciliation action/outcome 及是否允许继续发布。恢复细节见
|
||||
[references/production-handoff-recovery.md](references/production-handoff-recovery.md);仅遇到该类事故时读取。
|
||||
|
||||
`COMMAND_AUTHORITY_CHANGED` 是有效并发护栏,不是可忽略错误:重新读取当前 authority/transport/execution generation,
|
||||
确认期间没有新活动,再在原授权范围内幂等续办。若发现新工作、活跃 lease/outbox 或无法解释的副作用,立即停止并
|
||||
重新请求决策。
|
||||
|
||||
## 阶段 5:发布后核查
|
||||
|
||||
```bash
|
||||
scripts/verify-deploy.sh # 健康(前端/api/health=200)、bundle、server/daemon SHA、迁移集、federation、凭据
|
||||
```
|
||||
|
||||
预期 `✓ deploy verification passed: server=<sha> daemon=<sha>` 且 `<sha> == origin/main`,舰队 `online ≥ 发布前`。
|
||||
完成前还要独立复核 governed release 账本为 `succeeded/complete`、四组件实际 SHA 均为目标 SHA、租约已释放、
|
||||
新 handoff manifest=`verified`;若本次处理过旧 manifest,还要核对其全部 case 已达授权终态且 replay 数为 0。
|
||||
|
||||
默认生产包装器使用 `--no-mirror`:构建 daemon candidate binary 用于运行态验证不等于上传镜像资产。镜像落后告警
|
||||
在该模式下是预期的非阻断结果;不得未经单独授权补传镜像。交付说明要明确国内安装器可能回退 upstream,避免把
|
||||
本地构建、生产 daemon 更新和镜像上传混为一谈。
|
||||
|
||||
## Desktop m5max 双平台发布
|
||||
|
||||
Desktop 安装包不走 Server 生产部署脚本。权威流程见仓库 `docs/desktop-oss-release.md`,已验证基线为
|
||||
REQ-20260819-0005 / Desktop 0.3.1:
|
||||
|
||||
- 版本 PR 通过 `ci/internal-gate` 并合并后,m5max 从最新 `origin/main` 精确 SHA 的干净普通 clone 构建。
|
||||
- m5max 原生构建、Developer ID 签名并公证/staple macOS DMG/ZIP;交叉构建 Windows x64 NSIS EXE。
|
||||
- Darwin 不能执行 Windows 原生 `smoke:windows`。只有发布负责人明确接手 Windows 原生验收时才能继续交叉打包,交付证据必须标注该边界。
|
||||
- 资产不回传开发机;由 m5max 直接上传 `xiaoqu-public-file/slark/desktop/releases/v<version>/`,manifest 最后更新。
|
||||
- `xiaoqu-public-file` 的 canonical 凭证源是发布控制机上的 `/Users/donglinlai/coding/param.rxt`(必须为 `0600`);AK/SK 分别位于 XML 标签 `ossAccessKeyId` / `ossAccessKeySecret`。不得改猜 Bitwarden、Keychain、通用 `credentials.env` 或 m5max 本地配置;只有该文件缺失、权限不符或标签解析为空时才停止并报告。
|
||||
- m5max 不持久化 OSS 凭证。发布控制机从 `param.rxt` 提取 AK/SK 后,只用 `printf '%s\n%s\n' ... | ssh m5max ...` 经 SSH stdin 传两行;远端用 `read -r` 接收并仅为当次 shell 导出 `OSS_ACCESS_KEY_ID` / `OSS_ACCESS_KEY_SECRET`,随后运行上传脚本。禁止 `scp` 凭证文件、写远端临时文件或把值打印到日志。
|
||||
- m5max 非交互 SSH 必须显式补 `PATH=/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$PATH`;已验证 `ossutil` 位于 `/Users/johnq/.local/bin/ossutil`。执行前检查 Node、三个安装包、精确 main SHA 和干净工作树,避免因交互 shell 环境差异重新猜路径。
|
||||
- m5max 默认出口若走 `utun`,只为当次 `ossutil` 使用 `--bind-address "$(ipconfig getifaddr en0)"`,不得修改整机 Tailscale/默认路由。
|
||||
- 完成前核对三份资产 SHA256/大小、macOS 签名与公证、公开 URL/manifest,并回写 ai-proj 证据。
|
||||
|
||||
## 推送路由自查(换机器/新克隆必做)
|
||||
|
||||
```bash
|
||||
git remote -v # origin 收发都应是 https://github.com/qiudl/qiu-slark.git
|
||||
git config --get-regexp '^url\.' # 有输出 = pushInsteadOf 改写,push 可能被静默重定向到内网
|
||||
scripts/git-release-governance.sh check-local
|
||||
scripts/git-release-governance.sh status
|
||||
```
|
||||
|
||||
## 故障分流
|
||||
|
||||
| 现象 | 处置 |
|
||||
| --- | --- |
|
||||
| `release-prod` 拒绝 HEAD≠origin/main | `git fetch origin main` 后 detach 到 origin/main SHA,工作树须干净 |
|
||||
| `release-prod` 拒绝 git worktree / `.git` 不是目录 | 改用干净的普通 clone,锁定最新 `origin/main`;不要在现有脏 checkout 中强行切换 |
|
||||
| `ci/internal-gate` red | 看 gate 分组日志找 FAIL 项;`pnpm -r typecheck && pnpm test` 本地复现修复 |
|
||||
| `BLOCKED: <label> requires <VAR>` | runner 未配外部门命令,报运维配 `SLARK_CI_*_CMD`,非 PR 问题 |
|
||||
| 合并阻塞但与 PR 无关 | merge 最新 `main` 再看 |
|
||||
| push 成功但 GitHub 没有 | 查 `git config --get-regexp '^url\.'`,移除 pushInsteadOf 或显式指定 GitHub URL |
|
||||
| staging routed-remote 恒断网 | 查两道放网杠杆(workspace_access=write + 可写 git worktree) |
|
||||
| cohost Server 已更新但 daemon 仍是旧 SHA | 用 `--profile daemon up -d daemon-staging` 更新 daemon;核对两容器 revision 后才算完成 |
|
||||
| cohost daemon 启动即退出/fatal | 看 `docker logs slark_daemon_staging`;修复凭据/环境或版本契约,禁止用旧 daemon 冒充验收通过 |
|
||||
| verify 失败 SHA 不符 | 部署到了旧码,重发 |
|
||||
| `HANDOFF_BLOCKED` / manifest=`failed_closed` | 停止切换并读取 handoff recovery 参考;只读盘点后取得逐项 action/outcome 授权,禁止默认 replay |
|
||||
| reconciliation 返回 `COMMAND_AUTHORITY_CHANGED` | 查当前 generation 与新活动;无新活动才在原授权内幂等续办,有变化则停止重新决策 |
|
||||
| `--no-mirror` 后提示镜像落后 | 预期非阻断;报告 upstream fallback,未经单独授权不上传镜像 |
|
||||
| 生产授权已给但 staging 尚未验收 | 先完成同 SHA staging 全量验收,报告证据并重新等用户确认;不得沿用较早授权 |
|
||||
| staging 缺少需求依赖的真实集成配置 | 将对应验收标为未完成并停止生产链;补齐配置后重验,或让用户基于明确限制作决定 |
|
||||
|
||||
## ai-proj 流程(强制)
|
||||
|
||||
任何改产品/代码行为的工作,动手前先在 ai-proj 立项 + 建 Task + 关联,实现/测试/交付回写;
|
||||
commit/PR 引用 `REQ-...`。纯文档/工具/只读排查除外。详见仓库 `AGENTS.md` / `CLAUDE.md`。
|
||||
@@ -0,0 +1,70 @@
|
||||
# Production handoff recovery
|
||||
|
||||
仅在生产发布被 coding-session handoff 阻塞、manifest 进入 `failed_closed`,或存在 `needs_review` / 不确定命令时读取。
|
||||
目标是保留 fail-closed 语义,在明确授权下终结歧义状态,并证明没有误重放命令。
|
||||
|
||||
## 先冻结发布,不先解锁
|
||||
|
||||
- 停止发布切换和自动重试;记录 release ID、候选 SHA、manifest ID、阻塞发生阶段与原始错误。
|
||||
- 不把“继续”“再试一次”解释为 replay、force-end 或生产发布授权。向用户展示证据后,确认语句应绑定 manifest、
|
||||
条目数量或 command ID、目标 action/outcome、是否禁止 replay,以及要继续发布的精确 SHA。
|
||||
- 不直接更新 handoff、command、execution 表来改变状态。优先走已部署版本的内部治理 API;只有 API 无法恢复且
|
||||
已获得对应操作授权时,才可调用该版本现有 domain repository,并保留 lifecycle event 与 audit log。
|
||||
|
||||
## 只读证据盘点
|
||||
|
||||
对 manifest 中每条 item 核对并保存摘要:
|
||||
|
||||
- command ID、session/project/computer ID、command status、execution state;
|
||||
- manifest 记录的 authority term、transport generation、execution generation,以及当前实际值;
|
||||
- command/execution lease 是否过期,outbox 是否仍有 active/pending 记录;
|
||||
- session 中是否还有其他活跃命令或新事件;
|
||||
- daemon acknowledgement、result/receipt、审计事件是否能证明执行或未执行。
|
||||
|
||||
若 lease/outbox 仍活跃、存在新命令、证据相互矛盾,或无法排除外部副作用,停止恢复并请求新的人工判断。
|
||||
|
||||
## Action 与 outcome 必须匹配证据
|
||||
|
||||
- `no_action / not_executed`:只有权威证据能证明命令从未执行,且治理接口允许该组合时使用。
|
||||
- `terminate / terminated`:用于显式受控终结仍处于 uncertain/active 语义的命令;先通过 domain 操作取消 command、
|
||||
force-end execution、完成 session 并写审计,再完成 reconciliation,不产生 replay。
|
||||
- `mark_failed / failed`:证据确认失败、且不能安全恢复为成功或未执行时使用。
|
||||
- `replay / replayed_success`:仅在用户明确授权 replay 且 exactly-once/幂等边界得到证明时使用。发布解卡绝不能默认 replay。
|
||||
|
||||
不要为了迎合预先选择的 outcome 忽略现场证据;如果授权与可证明事实不兼容,停止并把差异报告给用户。
|
||||
|
||||
## 受治理恢复顺序
|
||||
|
||||
1. 使用精确 manifest/item 建立或读取 reconciliation case。
|
||||
2. 取得具备 action、evidence digest、operator 和有效期约束的审批。
|
||||
3. 记录 decision;执行 domain 终结或治理 apply;随后用权威运行态证据 confirm terminal outcome。
|
||||
4. 若 apply 返回 `COMMAND_AUTHORITY_CHANGED`,不要覆盖或绕过。重新读取当前三类 generation 和新活动:
|
||||
- generation 只因刚完成的受控终结而变化,且无新命令、lease/outbox 时,可在同一授权 action 下幂等续办;
|
||||
- generation 对应新工作或原因不明时,停止并重新审批。
|
||||
5. 每个 item 都 terminal 后,确认 manifest 自动或受治理地进入 `reconciled`,再启动新的发布尝试。
|
||||
|
||||
凭据失败发生在事务前时应视为零变更并记录。不要反复猜测 owner 凭据;不得打印连接串或密钥。若必须使用运行时
|
||||
`slark_app` 连接,只能通过已部署 domain repository、正确的 per-transaction project/user RLS context 和审计路径操作。
|
||||
|
||||
## 零重放与发布恢复验收
|
||||
|
||||
恢复完成至少证明:
|
||||
|
||||
- `terminalCount == totalCount`,每条 decision action 与 terminal outcome 等于用户授权;
|
||||
- 禁止 replay 的场景下,decision action=`replay` 数和 execution replay attempt 增量均为 0;
|
||||
- `terminate/terminated` 场景下,command=`cancelled`、execution=`force_ended`,并存在对应 lifecycle/audit 证据;
|
||||
- 旧 manifest=`reconciled`,没有 active handoff reservation 残留。
|
||||
|
||||
继续生产发布时使用新 governed release / handoff manifest,不复用 failed-closed manifest。重新检查候选仍是最新
|
||||
`origin/main`、同 SHA staging eligibility receipt 仍有效、工作树干净;任何一项变化都回到 staging 与人工确认门禁。
|
||||
|
||||
发布成功后再独立执行一次 `scripts/verify-deploy.sh`,并只读核对:release=`succeeded/complete`、target/actual Web、
|
||||
Server、daemon、artifact SHA 全相等、lease 已释放、finished 已记录、新 manifest=`verified`、舰队在线数不低于基线。
|
||||
将旧 manifest reconciliation 与新发布证据一起回写 ai-proj。
|
||||
|
||||
## `--no-mirror` 的解释边界
|
||||
|
||||
- `pnpm release:prod` 默认走 `deploy.sh --backend --no-install --no-mirror`;不上传 daemon 镜像资产。
|
||||
- 发布过程仍可能在隔离 candidate 目录构建 daemon binary,用于同 SHA 验证或部署运行态 daemon;这不是镜像上传。
|
||||
- mirror freshness 告警表示镜像源仍旧,国内安装器可能回退到较慢 upstream。它不阻断当前生产运行态发布。
|
||||
- 补传 mirror 是单独的外部写操作,需要单独授权和凭据;不能为了消除告警在发布收尾时顺手执行。
|
||||
@@ -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_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: data-excel
|
||||
description: Excel 数据处理与 BI 集成。通过自然语言操作 Excel 文件的读取、编辑、转换,并支持导入到 BI 系统(Metabase)进行可视化分析。当用户提到 Excel、表格处理、数据导入、BI 分析相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Excel 数据处理与 BI 集成 Skill
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "doubao-voice",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: doubao-voice
|
||||
description: 豆包语音API调用。支持语音合成(TTS)和唱歌。当用户提到语音合成、文字转语音、唱歌、豆包语音相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 豆包语音API技能
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "feishu-bitable",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: feishu-bitable
|
||||
description: 飞书多维表格操作。用于记录增删改查、批量操作、筛选排序、数据同步。当需要操作飞书多维表格时使用。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 飞书多维表格 (Bitable)
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "feishu-docx",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: feishu-docx
|
||||
description: 飞书云文档操作。用于创建、编辑云文档,插入内容块,会议纪要生成。当需要操作飞书云文档时使用。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 飞书云文档 (Docx)
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "feishu",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: feishu
|
||||
description: 飞书文档与多维表格操作入口。当用户提到飞书、云文档、多维表格、Bitable 相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 飞书集成
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "siyuan",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: siyuan
|
||||
description: 思源笔记 API 集成。通过自然语言创建、编辑、搜索笔记,管理笔记本和文档树。当用户提到思源笔记、SiYuan、笔记管理、知识库相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 思源笔记 API 集成 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 需要保密,不要提交到代码仓库
|
||||
@@ -665,72 +544,6 @@ print(f"内容长度: {len(result['content'])} 字符")
|
||||
- **最后更新**: 2026-01-15 07:35:00 ACDT
|
||||
- **验证时间**: 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
|
||||
@@ -890,83 +703,11 @@ YYYY-MM-DD HH:MM:SS - 验证通过
|
||||
记录手动运维操作时应包含:
|
||||
|
||||
```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
|
||||
具体命令
|
||||
```
|
||||
结果: [...]
|
||||
|
||||
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
|
||||
- 备注
|
||||
- 配置变更
|
||||
- YYYY-MM-DD HH:MM 操作记录
|
||||
|
||||
@@ -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_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: siyuan-to-feishu
|
||||
description: 将思源笔记导出为 PDF 并发送到飞书群。当用户提到"发送笔记"、"导出PDF发飞书"、"/siyuan send"、"分享到飞书"相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# siyuan-to-feishu Skill
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
},
|
||||
"install_name": "wecom",
|
||||
"install_type": "skill",
|
||||
"dir_category": "integration"
|
||||
"dir_category": "integration",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: 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/ |
|
||||
| 调试工具 | https://developer.work.weixin.qq.com/devtool/interface |
|
||||
| 管理后台 | 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']}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
name: req-design
|
||||
description: 需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。
|
||||
metadata:
|
||||
arguments: <REQ-ID>
|
||||
---
|
||||
|
||||
@@ -474,3 +475,13 @@ PRD 说「做什么」(用户故事、业务规则、验收标准),设计
|
||||
| V1.0 | 2026-01-26 | 初始版本(原名 req-dev) |
|
||||
| V2.0 | 2026-04-06 | 重构为 req-design:移除架构指南和编码规范,聚焦 API 契约 + 任务拆分 |
|
||||
| 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",
|
||||
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
},
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
---
|
||||
name: req
|
||||
description: 需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。
|
||||
metadata:
|
||||
arguments: <subcommand> [args]
|
||||
---
|
||||
|
||||
# req - 需求工作流管理
|
||||
|
||||
需求全流程工作流管理,与 ai-proj CLI 深度集成。
|
||||
|
||||
## 需求生命周期
|
||||
|
||||
**审批状态**(status):
|
||||
@@ -48,6 +44,7 @@ analysis → design → dev → review → testing → [待部署池] → releas
|
||||
- **操作前先确认实际 ID** — 从 URL 提取 ID(如 `/requirements/897` → ID=897)
|
||||
- **务实路线关闭也必须补全关联任务** — 每个进度条阶段需创建关联任务
|
||||
- **阶段内容门禁(防空转)** — `/req next` 时检查关键阶段任务是否有实质内容
|
||||
- **复用盘点先于估工(Gate 5,强制)** — PRD 必须含「现有实现与复用盘点」章节:每个能力点给出**现有实现(file:line)+ 结论(复用 / 改造 / 新增)**;结论=复用/改造的只按「接入差异」估工,结论=新增的必须写「为什么现有实现不能满足」。没有 file:line 证据、或标「未排查」的,**禁止 submit、禁止按全新增估工**;独立复查由非 PRD 写作者用 grep 复核并在 note 留命令与命中结果
|
||||
- **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 submit` (直接调用) | `/req submit` | 必须先通过 4 道门禁检查 |
|
||||
| `ai-proj req submit` (直接调用) | `/req submit` | 必须先通过 5 道门禁检查 |
|
||||
| `ai-proj req archive` | `/req done` | 必须先执行类型化门禁检查 |
|
||||
| `ai-proj req approve` | `/req review pass` | 必须先展示 PRD 摘要并等待用户确认 |
|
||||
| `ai-proj req advance --to <stage>` (force) | `/req next` | 必须先 AskUserQuestion 确认 + 记录跳过原因 |
|
||||
@@ -73,13 +70,15 @@ ai-proj req create \
|
||||
# → 结果必定是 draft 状态
|
||||
```
|
||||
|
||||
### 从 draft 到 submit 的 4 道门禁
|
||||
### 从 draft 到 submit 的 5 道门禁
|
||||
|
||||
```
|
||||
Gate 1: 需求讨论 ── description 末尾有「## 讨论结论」章节
|
||||
Gate 2: 方案确认 ── AskUserQuestion 获得「方案确认通过」
|
||||
Gate 3: PRD 文档 ── linkRole=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 原型生成 + 迭代 |
|
||||
| `dev-test` | 测试 + 质量门禁 |
|
||||
| `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",
|
||||
"description": "产品需求设计技能。覆盖问答访谈、PRD、缺陷收敛及 HTML 原型验证闭环。纯产品视角,不含技术实现。",
|
||||
"version": "2.1.0",
|
||||
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
|
||||
"version": "2.3.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
name: req-prd
|
||||
description: 产品设计与需求管理。用于 PRD 文档编写、需求分析、用户故事创建、功能设计和原型规划。当用户提到产品设计、PRD、需求文档、功能规划、用户故事相关任务时自动激活。
|
||||
description: 产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。
|
||||
---
|
||||
|
||||
# 产品需求设计 Skill (req-prd)
|
||||
|
||||
## 概述
|
||||
|
||||
本技能用于辅助产品设计和需求管理工作,包括:
|
||||
@@ -19,6 +16,36 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
||||
- `req-prototype` — UI 模块在 PRD/缺陷收敛后生成、上传并关联 HTML 原型
|
||||
- `defect-analysis` — 设计访谈确认后,对最新版 PRD 反复审计和修订直至收敛
|
||||
|
||||
## 产品需求文档双写门禁
|
||||
|
||||
本技能创建或修改的所有正式需求文档都必须同时保存到:
|
||||
|
||||
1. 当前产品仓库的本地 Markdown 文件;
|
||||
2. Requirement 对应角色任务的 ai-proj Task Document。
|
||||
|
||||
适用文档至少包括原始诉求/讨论记录、PRD、缺陷优化记录、原型版本与评审记录。Requirement description 只保存已确认的讨论摘要,不能替代 Task Document。
|
||||
|
||||
### 路径与任务映射
|
||||
|
||||
优先遵循仓库已有文档目录和命名约定;没有约定时使用:
|
||||
|
||||
| 文档 | 本地默认路径 | ai-proj 任务角色 |
|
||||
|------|--------------|------------------|
|
||||
| 需求讨论记录 | `docs/product/{REQ-ID}-{slug}-discussion.md` | `documentation` |
|
||||
| PRD(含缺陷修订和原型回填) | `docs/product/{REQ-ID}-{slug}-prd.md` | `prd` |
|
||||
|
||||
同一 Requirement、同一角色只维护一个当前任务文档。不得把本地文件路径当成 ai-proj 持久化,也不得只把远程 Task Document 导出一次后继续单边修改。
|
||||
|
||||
### 每次写入协议
|
||||
|
||||
1. 修改前同时读取本地文件与 ai-proj Task Document;任一不存在则基于另一份初始化,二者都不存在才新建空模板;
|
||||
2. 若两份内容不一致,比较文档 ID、版本、更新时间和内容摘要,保留双方未知内容并显式合并;无法安全合并时停止并请用户选择,禁止静默覆盖;
|
||||
3. 先用安全文件编辑方式写入本地 Markdown,再创建或更新对应 Task Document;
|
||||
4. 写后重新读取两端,计算或比较内容摘要,确认正文一致,并记录本地路径、task_id、document_id、version/updated_at;
|
||||
5. 任一端写入或复读失败都属于未完成的部分写入:保留已成功一端用于恢复,立即报告并停止后续阶段,不得宣称“已保存”“已收敛”或提交评审。
|
||||
|
||||
问答每一轮、每次 PRD 修订、每轮 defect 处置和每次原型回填都执行该协议;不能等到流程结束再一次性补传。
|
||||
|
||||
## 模块设计访谈模式
|
||||
|
||||
设计模块、系统、跨域流程,或目标/边界/业务规则尚未确定时,必须先执行问答式设计访谈,不得直接补全假设后生成 PRD。用户明确要求“你问我答”时也进入此模式。范围小、规则已完整确认的需求可以直接编写 PRD。
|
||||
@@ -41,8 +68,8 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
||||
|
||||
1. 原型基于最新版、已完成缺陷收敛的 PRD,并记录 PRD 文档标识、版本或内容摘要;
|
||||
2. 覆盖核心入口、主流程以及 PRD 明确要求的空态、失败态、无权限态和确认/撤销反馈;
|
||||
3. 上传后重新读取 Requirement,确认原型 URL/版本已关联,并验证 URL 可访问、iframe 可展示、核心交互可操作;
|
||||
4. 将 iframe、原型版本、版本说明和验证结果回填 PRD `4.2 界面原型`,并把生成、反馈、修订和确认写入同一讨论文档;
|
||||
3. 将本地 HTML 源文件通过 ai-proj 上传到 OSS;上传后重新读取 Requirement,确认 OSS URL/版本已关联,并验证 URL 可访问、iframe 可展示、核心交互可操作;
|
||||
4. 将 iframe、原型版本、版本说明和验证结果双写到本地 PRD 与 prd 角色 Task Document,并把生成、反馈、修订和确认双写到本地讨论记录与 documentation 任务文档;
|
||||
5. 用户明确确认最终 PRD 与原型表达的是同一方案。
|
||||
|
||||
纯后端、批处理、基础设施等确实没有用户界面的模块可以跳过,但必须在讨论文档和 PRD `4.2` 中记录“无 UI,原型不适用”的理由及用户确认,不得静默省略。
|
||||
@@ -107,6 +134,21 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
||||
- 会议/邮件/聊天记录链接
|
||||
- 客户特殊约束(如"必须在 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.1 目标用户
|
||||
[用户画像描述]
|
||||
@@ -401,6 +443,8 @@ mcp__ai-proj__link_tasks_to_requirement
|
||||
|
||||
### 文档管理
|
||||
|
||||
以下 MCP 操作只完成 ai-proj 侧写入;每次调用前后都必须按“产品需求文档双写门禁”同步并校验本地 Markdown。
|
||||
|
||||
```bash
|
||||
# 创建 PRD 文档并关联任务
|
||||
mcp__ai-proj__create-and-attach
|
||||
@@ -418,103 +462,7 @@ mcp__ai-proj__export_task_document_to_file
|
||||
- taskId: 任务ID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 功能设计流程
|
||||
|
||||
### 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 模块则核对已记录的不适用理由和用户确认。
|
||||
导出命令不能代替双写校验:导出后仍需确认目标路径符合仓库约定、正文与 Task Document 当前版本一致,且没有覆盖本地新增内容。
|
||||
|
||||
---
|
||||
|
||||
@@ -556,49 +504,6 @@ UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用
|
||||
|
||||
---
|
||||
|
||||
## 设计检查清单
|
||||
|
||||
### PRD 完整性检查
|
||||
|
||||
- [ ] 模块/系统设计已完成单轮单问访谈,且全过程已写入 ai-proj 讨论文档
|
||||
- [ ] 讨论结论已由用户明确确认
|
||||
- [ ] `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` 技能完成。
|
||||
|
||||
---
|
||||
|
||||
## 常用工具
|
||||
|
||||
### 原型设计
|
||||
@@ -645,31 +550,11 @@ UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用
|
||||
|
||||
---
|
||||
|
||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。**
|
||||
## 详细内容
|
||||
|
||||
### 禁止行为
|
||||
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
|
||||
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
|
||||
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
|
||||
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
|
||||
以下章节已移到 [references/details.md](references/details.md):
|
||||
|
||||
### 允许行为
|
||||
- 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` 同名规则。
|
||||
- 功能设计流程
|
||||
- 设计检查清单
|
||||
- Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
- 任务标题:`【讨论】需求讨论: {需求标题}`
|
||||
- 文档标题:`{REQ-ID} 需求讨论记录`
|
||||
- 本地文件:优先使用仓库约定;默认 `docs/product/{REQ-ID}-{slug}-discussion.md`
|
||||
- 一个 Requirement 只维护一个当前讨论文档,不因会话中断重复创建。
|
||||
|
||||
查找时同时核对 Requirement 关联关系、任务角色和标题,不能只按相似标题猜测。发现多个候选讨论文档时,列出标识和最近更新时间,请用户指定或授权合并;在此之前不得静默选择其中一个继续写入。
|
||||
@@ -38,7 +39,15 @@
|
||||
|
||||
若尚未指定 Requirement,先请用户提供已有 Requirement,或明确授权创建。取得 Requirement 和讨论文档前不得开始声称“已留痕”的正式访谈;不得仅为执行本技能本身擅自创建 Requirement。用户明确要求“创建需求并设计”才构成创建授权。
|
||||
|
||||
每次恢复会话时,先读取 Requirement、现有 PRD 和讨论文档,从最后一个未决问题继续。讨论文档是跨会话、上下文压缩后的权威记录;模型记忆不能覆盖文档中的用户原话和已确认决策。
|
||||
每次恢复会话时,先读取 Requirement、现有 PRD,以及讨论记录/PRD 的本地文件和 ai-proj Task Document,从最后一个未决问题继续。两端持久化内容必须一致;模型记忆不能覆盖文档中的用户原话和已确认决策。
|
||||
|
||||
### 本地与 ai-proj 双写
|
||||
|
||||
- 讨论记录和 PRD 的每次新增或修订,都必须同步到仓库本地 Markdown 与对应 ai-proj Task Document;本地文件用于代码库评审和版本控制,Task Document 用于需求关联、跨会话恢复和团队查看。
|
||||
- 修改前读取两端。内容不一致时按版本、更新时间和摘要显式合并,保留双方未知内容;不能判断时停止并请用户选择,禁止以任一旧副本覆盖另一端。
|
||||
- 每轮问答按顺序完成:更新本地讨论文件 → 更新 documentation 任务文档 → 复读两端并核对正文/摘要 → 再向用户提出下一问。
|
||||
- 每次 PRD/defect/原型回填按同样协议更新本地 PRD 文件和 prd 任务文档。默认本地路径为 `docs/product/{REQ-ID}-{slug}-prd.md`,已有项目约定优先。
|
||||
- 任一端失败时记录“部分写入”及成功端的路径/标识,停止后续流程。恢复时从成功端与失败前最后版本合并,禁止重复追加同一 Q/Round/Prototype 编号。
|
||||
|
||||
### 写入纪律
|
||||
|
||||
@@ -49,7 +58,7 @@
|
||||
- 用户原话逐字保留在引用块中;AI 的解释、推论和建议必须分栏,不能伪装成用户决定。访问令牌、密码、私钥及依法需要保护的个人敏感信息不得落库,用 `[敏感信息已脱敏]` 替代并注明脱敏原因。
|
||||
- 文档以追加式记录为主。状态为“待回答”的问题块可以在收到回答后原位补全一次;变为“已确认”后不得静默改写。纠正已确认结论时追加“决策变更”,并引用被替代的编号。
|
||||
- 更新整篇文档前重新读取最新版并保留未知内容;若读取后文档又发生变化,基于最新版合并后重试,不能用旧副本覆盖其他会话的记录。
|
||||
- 写入后读取文档确认本轮编号和正文存在。写入或校验失败时立即报告,停止进入下一轮,且不得声称“已记录”。
|
||||
- 写入后读取本地文件和 ai-proj Task Document,确认本轮编号、正文和内容摘要一致。任一写入或校验失败时立即报告,停止进入下一轮,且不得声称“已记录”。
|
||||
- Requirement 描述只同步用户确认后的“讨论结论”摘要;完整过程保留在讨论文档中。
|
||||
|
||||
## 3. 单轮单问访谈
|
||||
@@ -155,9 +164,9 @@ PRD 包含页面、表单、列表、可视状态、用户操作或跨页面流
|
||||
|
||||
### 6.3 上传、关联、回填与验证
|
||||
|
||||
1. 通过 `upload_prototype` 上传 HTML,并记录 Requirement 数字 ID、原型 URL、版本说明和上传时间;
|
||||
2. 重新读取 Requirement,确认返回的原型 URL/版本确实已关联。仅拿到上传成功响应不足以通过;
|
||||
3. 将 iframe、PRD 基线、原型版本、版本说明和验证结果回填 PRD `4.2 界面原型`;
|
||||
1. 将 HTML 源文件保存到仓库约定目录(默认 `docs/prototypes/`),再通过 `upload_prototype` 上传到 ai-proj 配置的 OSS,并记录 Requirement 数字 ID、OSS URL、版本说明和上传时间;
|
||||
2. 重新读取 Requirement,确认返回的 OSS URL/版本确实已关联。仅有本地文件或上传成功响应不足以通过;
|
||||
3. 将 iframe、PRD 基线、原型版本、版本说明和验证结果同时回填本地 PRD 与 prd 角色 Task Document;
|
||||
4. 用浏览器或等价方式验证 URL 可访问、iframe 可展示、核心导航和交互可操作、关键状态可识别。使用临时浏览器时按环境规则关闭;
|
||||
5. 将生成输入、上传结果、验证证据和待确认差异写入讨论文档。任何写入或验证失败都必须停止,不得声称原型已完成。
|
||||
|
||||
@@ -236,7 +245,9 @@ PRD 包含页面、表单、列表、可视状态、用户操作或跨页面流
|
||||
|
||||
- ai-proj Requirement 标识;
|
||||
- 讨论任务/文档标识;
|
||||
- 讨论记录本地路径及双写一致性状态;
|
||||
- PRD 任务/文档标识;
|
||||
- PRD 本地路径及双写一致性状态;
|
||||
- 问答轮数、缺陷审计轮数和收敛轮;
|
||||
- HTML 原型版本、URL、Requirement 关联与验证状态;无 UI 时给出跳过理由和用户确认;
|
||||
- 仍被接受的中/低风险;
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
{
|
||||
"name": "req-prototype-plugin",
|
||||
"description": "原型生成与关联。支持 HTML 正式交付、Requirement 关联、iframe 验证闭环及 Stitch AI 视觉探索。",
|
||||
"version": "2.1.0",
|
||||
"description": "原型生成与关联。支持 HTML 本地留源、OSS 正式交付、Requirement/iframe 验证及 Stitch AI。",
|
||||
"version": "2.2.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: req-prototype
|
||||
description: 原型生成与关联。支持两种模式:(1) Stitch AI 基于 PRD 自动生成 UI 原型截图;(2) AI 编写 HTML 原型并上传关联到需求详情页 iframe。当执行 /req prototype 或需要生成/上传界面原型时使用。
|
||||
description: 原型生成与关联。生成可交互 HTML、上传到 ai-proj OSS 并关联 Requirement/PRD iframe,也支持 Stitch AI 视觉探索。
|
||||
---
|
||||
|
||||
# 原型设计 Skill (req-prototype)
|
||||
@@ -13,7 +13,7 @@ description: 原型生成与关联。支持两种模式:(1) Stitch AI 基于 P
|
||||
|
||||
| 模式 | 命令 | 适用场景 | 输出 |
|
||||
|------|------|----------|------|
|
||||
| **HTML 上传** | `/req prototype upload` | UI 模块正式产品设计交付、评审与交互验证 | 可交互 HTML + Requirement 关联 + PRD iframe |
|
||||
| **HTML 上传** | `/req prototype upload` | UI 模块正式产品设计交付、评审与交互验证 | 本地 HTML 源文件 + OSS URL + Requirement 关联 + PRD iframe |
|
||||
| **Stitch AI** | `/req prototype` | 精细 UI 视觉探索、多屏草图 | 截图回填 PRD,后续仍需转为 HTML 正式原型 |
|
||||
|
||||
## 前置条件
|
||||
@@ -27,6 +27,17 @@ description: 原型生成与关联。支持两种模式:(1) Stitch AI 基于 P
|
||||
| PRD 已完成 defect 收敛(正式 HTML 模式) | 读取讨论文档的收敛轮和最新版 PRD 标识 | 报错:先完成 req-prd/defect-analysis 收敛 |
|
||||
| UI 原型适用 | PRD 含页面、操作流程或可视状态 | 无 UI 时记录不适用理由与用户确认,不生成空壳原型 |
|
||||
|
||||
## HTML 原型持久化门禁
|
||||
|
||||
正式 HTML 原型必须上传到 ai-proj 配置的 OSS(对象存储),并以 OSS HTTPS URL 关联 Requirement。仅生成本地文件、`file://` URL、临时 HTTP 服务或聊天附件都不算完成。
|
||||
|
||||
- 本地源文件优先遵循仓库约定;默认保存为 `docs/prototypes/{REQ-ID}-{slug}-v{N}.html`,纳入版本控制。
|
||||
- OSS 上传必须通过 `mcp__ai-proj__upload_prototype` 或 ai-proj 提供的等价正式接口完成,禁止绕过 Requirement 关联自行上传后只粘贴 URL。
|
||||
- `/tmp` 只能存放编码或校验过程中的临时副本,不能作为原型交付位置。
|
||||
- 上传后必须复读 Requirement,确认 `prototype_urls`/版本记录指向本次 OSS URL;再验证 URL 可访问、响应为 `text/html` 且可内联展示。
|
||||
- OSS 地址必须由 ai-proj 长期管理,不能把短期预签名 URL 写入 PRD;访问控制与 iframe 策略由 ai-proj 统一提供。
|
||||
- 原型元数据和 iframe 回填属于 PRD 修订,必须同时更新本地 PRD Markdown 与 prd 角色 Task Document,并复读校验一致。
|
||||
|
||||
## 子命令
|
||||
|
||||
### 0. `/req prototype upload [REQ-ID] [--note "版本说明"]` — 上传 HTML 原型(**推荐**)
|
||||
@@ -40,24 +51,24 @@ description: 原型生成与关联。支持两种模式:(1) Stitch AI 基于 P
|
||||
2. 完整读取最新版 PRD,记录任务/文档 ID、版本、更新时间和内容摘要或哈希;正式交付还要核对 defect 收敛轮
|
||||
3. 从 PRD 提取页面清单、角色入口、主流程、关键状态和验收条件,形成覆盖矩阵
|
||||
4. AI 编写带完整样式和必要原生交互的独立 HTML 原型文件(见设计规范)
|
||||
5. 保存到 /tmp/proto_<req_id>_<timestamp>.html,并在本地做结构、大小和敏感信息检查
|
||||
6. Base64 编码:base64 < /tmp/proto_<req_id>_<timestamp>.html
|
||||
7. 调用 mcp__ai-proj__upload_prototype 上传(传入 requirementId + base64 content)
|
||||
8. 重新读取 Requirement,确认原型 URL/版本已关联;不得只相信上传响应
|
||||
9. 将 iframe、PRD 基线、原型版本/说明和验证状态回填 PRD「4.2 界面原型」
|
||||
5. 保存到仓库原型目录(默认 docs/prototypes/<REQ-ID>-<slug>-v<N>.html),并做结构、大小和敏感信息检查
|
||||
6. 从本地源文件 Base64 编码;如工具需要,可在 /tmp 创建临时编码副本
|
||||
7. 调用 mcp__ai-proj__upload_prototype 上传到 OSS(传入 requirementId + base64 content)
|
||||
8. 重新读取 Requirement,确认 OSS URL/版本已关联;不得只相信上传响应
|
||||
9. 将 iframe、PRD 基线、原型版本/说明和验证状态双写到本地 PRD 与 prd 角色 Task Document
|
||||
10. 打开最终 URL 或需求详情页,验证 iframe 展示和核心交互;记录证据后关闭临时浏览器
|
||||
11. 将生成、关联、验证、用户反馈和版本状态写入同一讨论文档
|
||||
11. 将生成、关联、验证、用户反馈和版本状态双写到本地讨论记录与 documentation 角色 Task Document
|
||||
```
|
||||
|
||||
**Step 5-6 执行方式**:
|
||||
**Step 6-7 执行方式**:
|
||||
|
||||
```bash
|
||||
# 5. Base64 编码 HTML 文件
|
||||
B64=$(base64 < /tmp/proto_<req_id>_<timestamp>.html)
|
||||
# 6. Base64 编码本地 HTML 源文件
|
||||
B64=$(base64 < docs/prototypes/<REQ-ID>-<slug>-v<N>.html)
|
||||
```
|
||||
|
||||
```
|
||||
# 6. 通过 MCP 工具上传(无需本地后端)
|
||||
# 7. 通过 MCP 工具上传到 OSS(无需本地后端)
|
||||
mcp__ai-proj__upload_prototype(
|
||||
requirementId = <需求数字ID>,
|
||||
content = <B64 字符串>,
|
||||
@@ -72,7 +83,7 @@ mcp__ai-proj__upload_prototype(
|
||||
"success": true,
|
||||
"message": "原型已上传并关联到需求 <id>(version=N/A)",
|
||||
"data": {
|
||||
"url": "https://ai-proj-1252326374.cos.ap-beijing.myqcloud.com/prototypes/<uuid>.html",
|
||||
"url": "https://<ai-proj-oss-domain>/prototypes/<uuid>.html",
|
||||
"versionNote": "...",
|
||||
"uploadedAt": "...",
|
||||
"requirementId": <id>
|
||||
@@ -80,7 +91,7 @@ mcp__ai-proj__upload_prototype(
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:需求详情页自动出现「原型预览」卡片,iframe 加载 COS 上的 HTML 文件。**无需本地后端运行**。
|
||||
**效果**:需求详情页自动出现「原型预览」卡片,iframe 加载 OSS 上的 HTML 文件。**无需本地后端运行**。OSS 的具体厂商和域名由 ai-proj 服务配置,技能不得硬编码 COS、S3 或其他厂商地址。
|
||||
|
||||
**参数**:
|
||||
|
||||
@@ -183,6 +194,9 @@ AI 生成的 HTML 原型必须满足以下要求:
|
||||
#### HTML 上传后验证清单
|
||||
|
||||
- [ ] 上传响应成功且 Requirement 复读能看到同一 URL/版本
|
||||
- [ ] URL 为 ai-proj 返回的持久化 OSS HTTPS 地址,不是本地或临时地址
|
||||
- [ ] URL 不是短期预签名地址,响应 `Content-Type` 为 `text/html` 且不会强制下载
|
||||
- [ ] 仓库中保留与该 OSS 版本对应的 HTML 源文件
|
||||
- [ ] 原型 URL 返回可展示的 HTML,不是下载错误页、登录页或 404
|
||||
- [ ] 需求详情页使用 iframe 展示,没有降级为截图或图片
|
||||
- [ ] 核心入口、主流程和覆盖矩阵中的关键状态可识别/可操作
|
||||
@@ -327,7 +341,7 @@ generated_at: "<timestamp>"
|
||||
|
||||
| 异常 | 处理 |
|
||||
|------|------|
|
||||
| `mcp__ai-proj__upload_prototype` 返回失败 | 检查 requirementId 是否为数字 ID(非 display_id REQ-xxx) |
|
||||
| `mcp__ai-proj__upload_prototype` 返回失败 | 检查 requirementId 是否为数字 ID(非 display_id REQ-xxx);不得降级为只留本地文件 |
|
||||
| HTML 文件超过 5MB | 精简样式或拆分多版本上传 |
|
||||
| iframe 不显示 | 检查 `prototype_urls` 字段是否非空:`mcp__ai-proj__get_requirement` 确认 |
|
||||
| base64 命令失败 | macOS 用 `base64 < file`,Linux 用 `base64 -w 0 < file` |
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
---
|
||||
name: req-test-gate
|
||||
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。
|
||||
metadata:
|
||||
arguments: <subcommand> [args]
|
||||
---
|
||||
|
||||
# 测试与质量门禁制度 (req-test-gate) v2.1
|
||||
|
||||
## 概述
|
||||
|
||||
三层质量门禁架构,确保从需求到上线每个阶段都有结构化验证。
|
||||
@@ -348,48 +346,6 @@ Task({
|
||||
### 完整模板(scope = backend / fullstack)
|
||||
|
||||
```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: 回归贡献检查
|
||||
|
||||
**确保 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):
|
||||
|
||||
### 核心理念
|
||||
|
||||
**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题。**
|
||||
|
||||
三大支柱:
|
||||
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 流程。
|
||||
- 测试报告 - {需求标题}
|
||||
- Harness Engineering — 工程约束方法论
|
||||
|
||||
@@ -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 及原因,不得声称全部通过"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -42,6 +42,9 @@ description: Installer fixture version two.
|
||||
---
|
||||
version two
|
||||
EOF
|
||||
# Reproduce rsync's quick-check edge case: changed content with identical size
|
||||
# and mtime must still replace the recorded, unmodified installation.
|
||||
touch -r "$TEST_HOME/.agents/skills/example/SKILL.md" "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md"
|
||||
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||
grep -q 'version two' "$TEST_HOME/.agents/skills/example/SKILL.md"
|
||||
grep -q '"version": "2.0.0"' "$TEST_HOME/.agents/.ai-proj-helper-installed-skills.json"
|
||||
|
||||
@@ -49,6 +49,7 @@ PLUGIN_MAP = {
|
||||
"karpathy-guidelines-plugin": ("karpathy-guidelines", "skill", "dev"),
|
||||
"pull-request-plugin": ("pull-request", "skill", "dev"),
|
||||
"review-checklist-plugin": ("review-checklist", "skill", "dev"),
|
||||
"slark-dsh-cicd-plugin": ("slark-dsh-cicd", "skill", "dev"),
|
||||
|
||||
# skills-integration
|
||||
"data-excel-plugin": ("data-excel", "skill", "integration"),
|
||||
|
||||
Reference in New Issue
Block a user