Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04e20babc1 | ||
|
|
748ea68aa0 | ||
|
|
26352c4c69 | ||
|
|
6321a8528a | ||
|
|
b390c29af3 | ||
|
|
f41b14ad0a | ||
|
|
237631147a | ||
|
|
7d47762303 | ||
|
|
a7a753b833 |
@@ -0,0 +1,30 @@
|
|||||||
|
name: skill-gate
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "skills-**"
|
||||||
|
- "scripts/**"
|
||||||
|
- "install-skills.sh"
|
||||||
|
- "catalog.json"
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "skills-**"
|
||||||
|
- "scripts/**"
|
||||||
|
- "install-skills.sh"
|
||||||
|
- "catalog.json"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
gate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: 规范校验(strict:警告也算失败)
|
||||||
|
run: python3 scripts/validate-skills.py --strict
|
||||||
|
- name: 安全扫描(禁止高危项)
|
||||||
|
run: ./scripts/scan-skill.sh
|
||||||
|
- name: catalog 是否最新
|
||||||
|
run: python3 scripts/generate-catalog.py --check
|
||||||
|
- name: 安装器测试
|
||||||
|
run: bash tests/install-skills.test.sh
|
||||||
@@ -23,3 +23,28 @@
|
|||||||
- `generate-marketplace.py`:只发布 `promoted`,beta/deprecated 不随 marketplace 分发。
|
- `generate-marketplace.py`:只发布 `promoted`,beta/deprecated 不随 marketplace 分发。
|
||||||
|
|
||||||
- 按 SCOPE 降级两个无增量技能:`verification-before-completion`、`ai-proj-helper-entry`(规则已在用户级 AGENTS.md),入口路由内容转为 `docs/skill-routing.md`。
|
- 按 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 个警告**。
|
||||||
|
|
||||||
|
### 清理(2026-10-31 第二批)
|
||||||
|
|
||||||
|
- `scripts/scan-skill.sh`:回环地址(localhost/127.0.0.1)不再算风险;命中后走允许清单。
|
||||||
|
- 11 个文件里的 `/Users/<机器名>` 绝对路径去环境化(脚本改 `$HOME`,文档改 `~`)。
|
||||||
|
- 允许清单登记 6 类运维环境地址与示例占位符(非凭据),附理由。
|
||||||
|
- 安全扫描:57 个警告 → **0 个警告、0 个高危**。
|
||||||
|
- 新增 `.gitea/workflows/skill-gate.yml`:PR/main 跑 `validate --strict` + `scan` + `catalog --check` + 安装器测试。
|
||||||
|
|
||||||
|
- 新增 `MAINTENANCE.md`:技能维护节奏(每日漂移、每周 evals 抽样、每月目录审阅、每季度治理复核)。
|
||||||
|
|
||||||
|
### 治理增强(2026-11-01)
|
||||||
|
|
||||||
|
- 新增 git 门禁:`hooks/git/pre-commit`(validate --strict / scan / catalog --check)+ `pre-push`(安装器测试),`scripts/install-git-hooks.sh` 一键安装(Gitea Actions 无 runner,本地兜底)。
|
||||||
|
- `install-skills.sh` 支持 `--scope project --project-dir <根>`:装到项目 `.agents/skills`,独立状态文件。
|
||||||
|
- 新增 `scripts/scan-installed.sh`:扫描已装但非本仓库管理的第三方技能(当前 33 个,0 高危)。
|
||||||
|
- `generate-catalog.py` 增加 `has_evals` / `has_docs` 与 `coverage` 汇总;`validate-skills.py` 增加 `--coverage` 报告与路由表一致性检查。
|
||||||
|
- `SCOPE.md` 增加覆盖率路线图(3–4 周补齐 40 个技能的 evals 与文档);`MAINTENANCE.md` 同步。
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# 技能维护节奏
|
||||||
|
|
||||||
|
> 配套 `SCOPE.md`(收不收)、`skill-creator`(怎么建)、`docs/skill-routing.md`(用哪个)。
|
||||||
|
> 本文件管「多久做一次什么」。
|
||||||
|
|
||||||
|
## 每次改动(任何 PR)
|
||||||
|
|
||||||
|
1. 过 `SCOPE.md` 收录门槛(真实失败 + 组合不出来),新技能默认先 `beta`。
|
||||||
|
2. 跑四道门禁:`validate-skills.py --strict`、`scan-skill.sh`、`generate-catalog.py --check`、`tests/install-skills.test.sh`。
|
||||||
|
- 本机已用 `./scripts/install-git-hooks.sh` 装好 pre-commit / pre-push,commit/push 时自动执行;Gitea Actions 暂无 runner,不能依赖 CI。
|
||||||
|
- 跳过单次:`SKILL_GATE_SKIP=1 git commit ...`,提交信息里写原因。
|
||||||
|
3. 技能有增删改 → **同步回改** `docs/skill-routing.md` 与 `catalog.json`。
|
||||||
|
4. 更新 `plugin.json` 版本号与 `CHANGELOG.md`。
|
||||||
|
|
||||||
|
## 每日(随 7:30 项目日报)
|
||||||
|
|
||||||
|
- 两台机器跑 `install-skills.sh --audit`:**drift > 0 就要报**,并注明是「未安装 / 版本落后 / 本地改动」。
|
||||||
|
- 门禁失败或扫描出高危:立即处理,不等到日报。
|
||||||
|
- 例外:`beta` 与 `deprecated` 不计入 drift。
|
||||||
|
|
||||||
|
## 每周一 10:00 — evals 抽样回归
|
||||||
|
|
||||||
|
- 范围:上周改过的技能 + 3 个高风险技能(`dev-coding`、`dev-review`、`req-design`)。
|
||||||
|
- 每技能取 1 条关键用例,做「有技能 / 无技能」对比,按 rubric 打分。
|
||||||
|
- 结果记录到 `reports/evals/YYYY-Www.md`,进周报。
|
||||||
|
- 连续两次无增量的技能 → 按 `SCOPE.md` 评估降级。
|
||||||
|
- 不做全量(61 技能 × 3 用例成本过高);全量只在治理大改时做。
|
||||||
|
|
||||||
|
## 每月 1 日 10:00 — 漂移与目录审阅
|
||||||
|
|
||||||
|
- 两台机器 `--audit` 漂移必须为 0(beta/deprecated 除外)。
|
||||||
|
- 审 `catalog.json`:`description` 长度 <80 或 >600 的标黄;含「各种 / 等等 / 相关」这类泛化词的标出。
|
||||||
|
- 第三方技能(arkcli 等)有变动时,评估是否影响团队技能;跑一次 `./scripts/scan-installed.sh`。
|
||||||
|
- 跑 `python3 scripts/validate-skills.py --coverage`:报告缺 evals / 缺四段式文档的技能,按 `SCOPE.md` 的覆盖率路线图推进。
|
||||||
|
|
||||||
|
## 每季度首月 1 日 10:30 — 治理复核
|
||||||
|
|
||||||
|
- 人工过一遍 `docs/skill-routing.md`:删掉不再用的引用、补上新技能。
|
||||||
|
- 清理 `deprecated`:满 3 个月且无回滚需求 → 删除插件目录。
|
||||||
|
- 复核分桶与调用模式:还有没有命令型技能占着 `model-invoked`。
|
||||||
|
- 复核覆盖率:`--coverage` 的两项缺口是否按路线图收敛。
|
||||||
|
- 更新本文件与 `CHANGELOG.md`。
|
||||||
|
|
||||||
|
## 责任分工
|
||||||
|
|
||||||
|
- 项目经理 Bot:执行节奏、出周/月报、盯漂移与门禁。
|
||||||
|
- 改技能的 agent:负责门禁四件套与路由回改。
|
||||||
|
- 人工拍板项:废弃技能、路由变更、`promoted` 晋升。
|
||||||
@@ -208,5 +208,9 @@ A: SSE 模式直连远程服务器,零依赖开箱即用(推荐)。stdio
|
|||||||
- Claude(兼容):`./install-skills.sh --agent claude`
|
- Claude(兼容):`./install-skills.sh --agent claude`
|
||||||
- 看漂移:`./install-skills.sh --audit`
|
- 看漂移:`./install-skills.sh --audit`
|
||||||
- **校验 / 安全 / 清单**:`python3 scripts/validate-skills.py`、`./scripts/scan-skill.sh`、`python3 scripts/generate-catalog.py`
|
- **校验 / 安全 / 清单**:`python3 scripts/validate-skills.py`、`./scripts/scan-skill.sh`、`python3 scripts/generate-catalog.py`
|
||||||
|
- **本地门禁(Gitea Actions 无 runner,用 git hook 兜底)**:`./scripts/install-git-hooks.sh` 后,每次 commit 自动跑 `validate --strict` + `scan` + `catalog --check`,push 前跑安装器测试。跳过单次:`SKILL_GATE_SKIP=1`。
|
||||||
|
- **项目级安装**:`./install-skills.sh --agent codex --scope project --project-dir <项目根>` → 装到 `<项目根>/.agents/skills`。
|
||||||
|
- **第三方技能风险扫描**:`./scripts/scan-installed.sh`(扫已装但非本仓库管理的技能)。
|
||||||
- **建技能**:走 `skill-creator` 技能(收录门槛、分桶、调用模式、evals、四段式文档、路由回改),不要手写。
|
- **建技能**:走 `skill-creator` 技能(收录门槛、分桶、调用模式、evals、四段式文档、路由回改),不要手写。
|
||||||
- **路由表**:`docs/skill-routing.md`(什么任务用哪个技能,人读;由原 `ai-proj-helper-entry` 技能降级而来)。
|
- **路由表**:`docs/skill-routing.md`(什么任务用哪个技能,人读;由原 `ai-proj-helper-entry` 技能降级而来)。
|
||||||
|
- **维护节奏**:`MAINTENANCE.md`(每次改动 / 每日 / 每周 / 每月 / 每季度各做什么)。
|
||||||
|
|||||||
@@ -43,3 +43,17 @@
|
|||||||
3. `./scripts/scan-skill.sh` 无高危项。
|
3. `./scripts/scan-skill.sh` 无高危项。
|
||||||
4. `python3 scripts/generate-catalog.py` 更新 `catalog.json`。
|
4. `python3 scripts/generate-catalog.py` 更新 `catalog.json`。
|
||||||
5. 版本号 + CHANGELOG 更新,走 PR。
|
5. 版本号 + CHANGELOG 更新,走 PR。
|
||||||
|
|
||||||
|
## 覆盖率路线图(evals 与四段式文档)
|
||||||
|
|
||||||
|
目标集合:`promoted` 且 `model-invoked` 的技能(当前 40 个)。用户级命令技能(`user-invoked`)只要求四段式文档,不强制 evals。
|
||||||
|
|
||||||
|
| 阶段 | 范围 | 验收 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 第 1 周 | `core/` + `req/` 类(约 15 个) | `--coverage` 里这些类目缺口为 0 |
|
||||||
|
| 第 2 周 | `dev/` 类(约 20 个) | 同上 |
|
||||||
|
| 第 3–4 周 | 其余(`integration/`、`biz/` 里仍 model-invoked 的) | 全量缺口为 0 |
|
||||||
|
|
||||||
|
每个技能的最低要求:`evals/evals.json` 3 条(正常 / 边界 / 失败恢复)+ `docs/skills/<name>.md` 四段式(What it does / When to reach for it / Common questions / It's working if)。
|
||||||
|
|
||||||
|
先用 `python3 scripts/validate-skills.py --coverage` 看当前缺口,再按阶段补。
|
||||||
|
|||||||
+273
-62
@@ -1,6 +1,95 @@
|
|||||||
{
|
{
|
||||||
"repo": "ai-proj-helper",
|
"repo": "ai-proj-helper",
|
||||||
"generated_at": "2026-10-31T14:18:10Z",
|
"coverage": {
|
||||||
|
"required": 40,
|
||||||
|
"with_evals": 0,
|
||||||
|
"with_docs": 0,
|
||||||
|
"missing_evals": [
|
||||||
|
"ai-proj",
|
||||||
|
"pm-ask",
|
||||||
|
"pm-risk",
|
||||||
|
"agent-browser",
|
||||||
|
"agent-swarm",
|
||||||
|
"ai-chat",
|
||||||
|
"db-migration",
|
||||||
|
"defect-analysis",
|
||||||
|
"deploy-rollback",
|
||||||
|
"dev-arch",
|
||||||
|
"dev-cicd",
|
||||||
|
"dev-coding",
|
||||||
|
"dev-commit",
|
||||||
|
"dev-deploy",
|
||||||
|
"dev-integration",
|
||||||
|
"dev-mcp",
|
||||||
|
"dev-review",
|
||||||
|
"dev-scaffold",
|
||||||
|
"dev-test",
|
||||||
|
"executing-plans",
|
||||||
|
"finishing-a-development-branch",
|
||||||
|
"frontend-design",
|
||||||
|
"karpathy-guidelines",
|
||||||
|
"pull-request",
|
||||||
|
"review-checklist",
|
||||||
|
"slark-cicd",
|
||||||
|
"slark-dsh-cicd",
|
||||||
|
"req-audit",
|
||||||
|
"req-compare",
|
||||||
|
"req-design",
|
||||||
|
"req-dev",
|
||||||
|
"req-lookback",
|
||||||
|
"req",
|
||||||
|
"req-prd",
|
||||||
|
"req-prototype",
|
||||||
|
"req-research",
|
||||||
|
"req-retro",
|
||||||
|
"req-review",
|
||||||
|
"req-test-gate",
|
||||||
|
"req-workflow"
|
||||||
|
],
|
||||||
|
"missing_docs": [
|
||||||
|
"ai-proj",
|
||||||
|
"pm-ask",
|
||||||
|
"pm-risk",
|
||||||
|
"agent-browser",
|
||||||
|
"agent-swarm",
|
||||||
|
"ai-chat",
|
||||||
|
"db-migration",
|
||||||
|
"defect-analysis",
|
||||||
|
"deploy-rollback",
|
||||||
|
"dev-arch",
|
||||||
|
"dev-cicd",
|
||||||
|
"dev-coding",
|
||||||
|
"dev-commit",
|
||||||
|
"dev-deploy",
|
||||||
|
"dev-integration",
|
||||||
|
"dev-mcp",
|
||||||
|
"dev-review",
|
||||||
|
"dev-scaffold",
|
||||||
|
"dev-test",
|
||||||
|
"executing-plans",
|
||||||
|
"finishing-a-development-branch",
|
||||||
|
"frontend-design",
|
||||||
|
"karpathy-guidelines",
|
||||||
|
"pull-request",
|
||||||
|
"review-checklist",
|
||||||
|
"slark-cicd",
|
||||||
|
"slark-dsh-cicd",
|
||||||
|
"req-audit",
|
||||||
|
"req-compare",
|
||||||
|
"req-design",
|
||||||
|
"req-dev",
|
||||||
|
"req-lookback",
|
||||||
|
"req",
|
||||||
|
"req-prd",
|
||||||
|
"req-prototype",
|
||||||
|
"req-research",
|
||||||
|
"req-retro",
|
||||||
|
"req-review",
|
||||||
|
"req-test-gate",
|
||||||
|
"req-workflow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"generated_at": "2026-10-31T21:04:04Z",
|
||||||
"total": 61,
|
"total": 61,
|
||||||
"counts": {
|
"counts": {
|
||||||
"promoted/user-invoked": 18,
|
"promoted/user-invoked": 18,
|
||||||
@@ -19,7 +108,9 @@
|
|||||||
"description": "商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。",
|
"description": "商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。",
|
||||||
"description_len": 78,
|
"description_len": 78,
|
||||||
"path": "skills-biz/biz-contract-plugin/skills/SKILL.md",
|
"path": "skills-biz/biz-contract-plugin/skills/SKILL.md",
|
||||||
"digest": "3d346d4a316590a3"
|
"digest": "3c17cae1380d82a4",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "biz-ops",
|
"name": "biz-ops",
|
||||||
@@ -30,7 +121,9 @@
|
|||||||
"description": "商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。",
|
"description": "商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。",
|
||||||
"description_len": 71,
|
"description_len": 71,
|
||||||
"path": "skills-biz/biz-ops-plugin/skills/SKILL.md",
|
"path": "skills-biz/biz-ops-plugin/skills/SKILL.md",
|
||||||
"digest": "aa5433074ba277d6"
|
"digest": "aa5433074ba277d6",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "biz-plan",
|
"name": "biz-plan",
|
||||||
@@ -41,7 +134,9 @@
|
|||||||
"description": "商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。",
|
"description": "商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。",
|
||||||
"description_len": 66,
|
"description_len": 66,
|
||||||
"path": "skills-biz/biz-plan-plugin/skills/SKILL.md",
|
"path": "skills-biz/biz-plan-plugin/skills/SKILL.md",
|
||||||
"digest": "f3bb159157441ca7"
|
"digest": "b6f43a914b2f7b45",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "finance",
|
"name": "finance",
|
||||||
@@ -52,7 +147,9 @@
|
|||||||
"description": "财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。",
|
"description": "财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。",
|
||||||
"description_len": 94,
|
"description_len": 94,
|
||||||
"path": "skills-biz/finance-plugin/skills/SKILL.md",
|
"path": "skills-biz/finance-plugin/skills/SKILL.md",
|
||||||
"digest": "5191b5a91b34ecd6"
|
"digest": "0a9209bc4bf4db3a",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ai-proj-helper-entry",
|
"name": "ai-proj-helper-entry",
|
||||||
@@ -63,7 +160,9 @@
|
|||||||
"description": "ai-proj-helper 技能库路由:告诉你有哪些技能、什么任务用哪个、以及「REQ released + PDV 通过才算交付」的强制口径。人在开始需求/开发/评审/发布任务前主动调用。",
|
"description": "ai-proj-helper 技能库路由:告诉你有哪些技能、什么任务用哪个、以及「REQ released + PDV 通过才算交付」的强制口径。人在开始需求/开发/评审/发布任务前主动调用。",
|
||||||
"description_len": 96,
|
"description_len": 96,
|
||||||
"path": "skills-core/ai-proj-helper-entry-plugin/skills/SKILL.md",
|
"path": "skills-core/ai-proj-helper-entry-plugin/skills/SKILL.md",
|
||||||
"digest": "41d39b76d4c71dbf"
|
"digest": "41d39b76d4c71dbf",
|
||||||
|
"has_evals": true,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ai-proj",
|
"name": "ai-proj",
|
||||||
@@ -74,7 +173,9 @@
|
|||||||
"description": "ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。",
|
"description": "ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。",
|
||||||
"description_len": 77,
|
"description_len": 77,
|
||||||
"path": "skills-core/ai-proj-plugin/skills/SKILL.md",
|
"path": "skills-core/ai-proj-plugin/skills/SKILL.md",
|
||||||
"digest": "f2ef00fdebb29034"
|
"digest": "77ce4d2eb10e1d29",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pm-ask",
|
"name": "pm-ask",
|
||||||
@@ -85,7 +186,9 @@
|
|||||||
"description": "基于 ai-proj MCP + git log 真实数据的项目问答。禁止编造。当用户说\"/ask\"、\"问一下\"、\"统计一下\"、\"最近多少\"、\"谁做了多少\"、\"本月进度\"等项目相关问题时自动激活。",
|
"description": "基于 ai-proj MCP + git log 真实数据的项目问答。禁止编造。当用户说\"/ask\"、\"问一下\"、\"统计一下\"、\"最近多少\"、\"谁做了多少\"、\"本月进度\"等项目相关问题时自动激活。",
|
||||||
"description_len": 98,
|
"description_len": 98,
|
||||||
"path": "skills-core/pm-ask-plugin/skills/SKILL.md",
|
"path": "skills-core/pm-ask-plugin/skills/SKILL.md",
|
||||||
"digest": "e5b69a0b56985472"
|
"digest": "e5b69a0b56985472",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pm-risk",
|
"name": "pm-risk",
|
||||||
@@ -96,7 +199,9 @@
|
|||||||
"description": "三维度项目风险扫描(需求层/代码层/流程层)。当用户说\"/risk\"、\"风险扫描\"、\"有什么风险\"、\"哪些需求停滞了\"、\"分支健康\"时自动激活。",
|
"description": "三维度项目风险扫描(需求层/代码层/流程层)。当用户说\"/risk\"、\"风险扫描\"、\"有什么风险\"、\"哪些需求停滞了\"、\"分支健康\"时自动激活。",
|
||||||
"description_len": 72,
|
"description_len": 72,
|
||||||
"path": "skills-core/pm-risk-plugin/skills/SKILL.md",
|
"path": "skills-core/pm-risk-plugin/skills/SKILL.md",
|
||||||
"digest": "928cfec76018329f"
|
"digest": "928cfec76018329f",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "publish",
|
"name": "publish",
|
||||||
@@ -107,7 +212,9 @@
|
|||||||
"description": "发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。",
|
"description": "发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。",
|
||||||
"description_len": 48,
|
"description_len": 48,
|
||||||
"path": "skills-core/publish-plugin/skills/SKILL.md",
|
"path": "skills-core/publish-plugin/skills/SKILL.md",
|
||||||
"digest": "198c63f0dcb58d57"
|
"digest": "48bfbb4149baddcc",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "skill-creator",
|
"name": "skill-creator",
|
||||||
@@ -118,7 +225,9 @@
|
|||||||
"description": "团队技能的标准流程:收录门槛、分桶生命周期、调用模式、SKILL.md 规范、evals 全量、四段式文档、路由回改。当要新建技能、大改现有技能、或评审一个技能是否合格时使用。",
|
"description": "团队技能的标准流程:收录门槛、分桶生命周期、调用模式、SKILL.md 规范、evals 全量、四段式文档、路由回改。当要新建技能、大改现有技能、或评审一个技能是否合格时使用。",
|
||||||
"description_len": 88,
|
"description_len": 88,
|
||||||
"path": "skills-core/skill-creator-plugin/skills/SKILL.md",
|
"path": "skills-core/skill-creator-plugin/skills/SKILL.md",
|
||||||
"digest": "18f18c43c3fa56a6"
|
"digest": "18f18c43c3fa56a6",
|
||||||
|
"has_evals": true,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "agent-browser",
|
"name": "agent-browser",
|
||||||
@@ -129,7 +238,9 @@
|
|||||||
"description": "浏览器自动化技能。用于网页交互、E2E冒烟测试、需求验收验证、前端开发验证、截图对比。基于 Vercel agent-browser CLI。",
|
"description": "浏览器自动化技能。用于网页交互、E2E冒烟测试、需求验收验证、前端开发验证、截图对比。基于 Vercel agent-browser CLI。",
|
||||||
"description_len": 71,
|
"description_len": 71,
|
||||||
"path": "skills-dev/agent-browser-plugin/skills/SKILL.md",
|
"path": "skills-dev/agent-browser-plugin/skills/SKILL.md",
|
||||||
"digest": "f382bffe3d1c84b2"
|
"digest": "f382bffe3d1c84b2",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "agent-swarm",
|
"name": "agent-swarm",
|
||||||
@@ -140,7 +251,9 @@
|
|||||||
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
|
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
|
||||||
"description_len": 153,
|
"description_len": 153,
|
||||||
"path": "skills-dev/agent-swarm-plugin/skills/SKILL.md",
|
"path": "skills-dev/agent-swarm-plugin/skills/SKILL.md",
|
||||||
"digest": "a0d8a329ab9468f5"
|
"digest": "4164f82025a421bb",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ai-chat",
|
"name": "ai-chat",
|
||||||
@@ -151,7 +264,9 @@
|
|||||||
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
|
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
|
||||||
"description_len": 78,
|
"description_len": 78,
|
||||||
"path": "skills-dev/ai-chat-plugin/skills/SKILL.md",
|
"path": "skills-dev/ai-chat-plugin/skills/SKILL.md",
|
||||||
"digest": "97ce03993394605c"
|
"digest": "81193de50b43d95c",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ai-proj-cicd-release",
|
"name": "ai-proj-cicd-release",
|
||||||
@@ -162,7 +277,9 @@
|
|||||||
"description": "Execute and audit the AI-Proj service CI/CD flow across Gitea gates, immutable image builds, staging verification, approved production release, rollback, and evidence capture. Use for AI-Proj CI status, release PRs, staging or production deployments, failed-release diagnosis, deployed commit/schema verification, and delivery-chain repair. Do not use for unrelated repositories or macOS application packaging.",
|
"description": "Execute and audit the AI-Proj service CI/CD flow across Gitea gates, immutable image builds, staging verification, approved production release, rollback, and evidence capture. Use for AI-Proj CI status, release PRs, staging or production deployments, failed-release diagnosis, deployed commit/schema verification, and delivery-chain repair. Do not use for unrelated repositories or macOS application packaging.",
|
||||||
"description_len": 410,
|
"description_len": 410,
|
||||||
"path": "skills-dev/ai-proj-cicd-release-plugin/skills/SKILL.md",
|
"path": "skills-dev/ai-proj-cicd-release-plugin/skills/SKILL.md",
|
||||||
"digest": "49ef1b6865c2f1b3"
|
"digest": "49ef1b6865c2f1b3",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ai-proj-macos-release",
|
"name": "ai-proj-macos-release",
|
||||||
@@ -173,7 +290,9 @@
|
|||||||
"description": "Build, sign, notarize, publish, and verify the AI-Proj macOS Apple Silicon package through the repository's release chain. Use when asked to release, republish, update, or repair the downloadable macOS application, including Gatekeeper failures and download-manifest updates. Do not use for service deployments or unrelated applications.",
|
"description": "Build, sign, notarize, publish, and verify the AI-Proj macOS Apple Silicon package through the repository's release chain. Use when asked to release, republish, update, or repair the downloadable macOS application, including Gatekeeper failures and download-manifest updates. Do not use for service deployments or unrelated applications.",
|
||||||
"description_len": 337,
|
"description_len": 337,
|
||||||
"path": "skills-dev/ai-proj-macos-release-plugin/skills/SKILL.md",
|
"path": "skills-dev/ai-proj-macos-release-plugin/skills/SKILL.md",
|
||||||
"digest": "6f2496b941706137"
|
"digest": "6f2496b941706137",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "db-migration",
|
"name": "db-migration",
|
||||||
@@ -184,7 +303,9 @@
|
|||||||
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时由 req-design 推荐激活。",
|
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时由 req-design 推荐激活。",
|
||||||
"description_len": 77,
|
"description_len": 77,
|
||||||
"path": "skills-dev/db-migration-plugin/skills/SKILL.md",
|
"path": "skills-dev/db-migration-plugin/skills/SKILL.md",
|
||||||
"digest": "f8d6ebbddbbf7a71"
|
"digest": "f8d6ebbddbbf7a71",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "defect-analysis",
|
"name": "defect-analysis",
|
||||||
@@ -195,7 +316,9 @@
|
|||||||
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
||||||
"description_len": 68,
|
"description_len": 68,
|
||||||
"path": "skills-dev/defect-analysis-plugin/skills/SKILL.md",
|
"path": "skills-dev/defect-analysis-plugin/skills/SKILL.md",
|
||||||
"digest": "23dfafda92d0c622"
|
"digest": "23dfafda92d0c622",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "deploy-rollback",
|
"name": "deploy-rollback",
|
||||||
@@ -206,7 +329,9 @@
|
|||||||
"description": "回滚方案插件。部署后发现问题时的回滚策略和执行步骤。挂载在 deploy 阶段,部署出问题时激活。",
|
"description": "回滚方案插件。部署后发现问题时的回滚策略和执行步骤。挂载在 deploy 阶段,部署出问题时激活。",
|
||||||
"description_len": 49,
|
"description_len": 49,
|
||||||
"path": "skills-dev/deploy-rollback-plugin/skills/SKILL.md",
|
"path": "skills-dev/deploy-rollback-plugin/skills/SKILL.md",
|
||||||
"digest": "75ebf66a3f74195a"
|
"digest": "75ebf66a3f74195a",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-android",
|
"name": "dev-android",
|
||||||
@@ -217,7 +342,9 @@
|
|||||||
"description": "Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。",
|
"description": "Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。",
|
||||||
"description_len": 72,
|
"description_len": 72,
|
||||||
"path": "skills-dev/dev-android-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-android-plugin/skills/SKILL.md",
|
||||||
"digest": "fb058d1059b56cdd"
|
"digest": "fb058d1059b56cdd",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-arch",
|
"name": "dev-arch",
|
||||||
@@ -228,7 +355,9 @@
|
|||||||
"description": "软件架构设计技能。用于系统设计、技术选型、架构评审、设计文档编写。当用户提到架构设计、系统设计、技术方案、API 设计相关任务时自动激活。",
|
"description": "软件架构设计技能。用于系统设计、技术选型、架构评审、设计文档编写。当用户提到架构设计、系统设计、技术方案、API 设计相关任务时自动激活。",
|
||||||
"description_len": 69,
|
"description_len": 69,
|
||||||
"path": "skills-dev/dev-arch-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-arch-plugin/skills/SKILL.md",
|
||||||
"digest": "c9a419c83a307a11"
|
"digest": "c9a419c83a307a11",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-cicd",
|
"name": "dev-cicd",
|
||||||
@@ -239,7 +368,9 @@
|
|||||||
"description": "CI/CD 流水线设计、优化与排查。适配 Gitea Actions + Go/Swift/Next.js/Docker 栈。当用户提到 CI、CD、流水线、pipeline、workflow、构建失败、runner 相关任务时自动激活。",
|
"description": "CI/CD 流水线设计、优化与排查。适配 Gitea Actions + Go/Swift/Next.js/Docker 栈。当用户提到 CI、CD、流水线、pipeline、workflow、构建失败、runner 相关任务时自动激活。",
|
||||||
"description_len": 119,
|
"description_len": 119,
|
||||||
"path": "skills-dev/dev-cicd-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-cicd-plugin/skills/SKILL.md",
|
||||||
"digest": "a763f0f140516411"
|
"digest": "a763f0f140516411",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-coding",
|
"name": "dev-coding",
|
||||||
@@ -250,7 +381,9 @@
|
|||||||
"description": "软件编码开发技能。用于代码编写、功能实现、重构优化。集成 ai-proj CLI 进行任务管理和进度跟踪。核心支持 Go 后端 + Vue/React 前端开发。",
|
"description": "软件编码开发技能。用于代码编写、功能实现、重构优化。集成 ai-proj CLI 进行任务管理和进度跟踪。核心支持 Go 后端 + Vue/React 前端开发。",
|
||||||
"description_len": 81,
|
"description_len": 81,
|
||||||
"path": "skills-dev/dev-coding-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-coding-plugin/skills/SKILL.md",
|
||||||
"digest": "1b754e668965132f"
|
"digest": "cc43720aa12bb8f6",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-commit",
|
"name": "dev-commit",
|
||||||
@@ -261,7 +394,9 @@
|
|||||||
"description": "智能 git commit — 分支保护 + 自动建功能分支 + Conventional Commits 生成。当用户说\"提交代码\"、\"commit\"、\"/commit\"、\"保存修改\"时自动激活。",
|
"description": "智能 git commit — 分支保护 + 自动建功能分支 + Conventional Commits 生成。当用户说\"提交代码\"、\"commit\"、\"/commit\"、\"保存修改\"时自动激活。",
|
||||||
"description_len": 99,
|
"description_len": 99,
|
||||||
"path": "skills-dev/dev-commit-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-commit-plugin/skills/SKILL.md",
|
||||||
"digest": "1560658c4a30df45"
|
"digest": "1560658c4a30df45",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-deploy",
|
"name": "dev-deploy",
|
||||||
@@ -272,7 +407,9 @@
|
|||||||
"description": "应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。",
|
"description": "应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。",
|
||||||
"description_len": 90,
|
"description_len": 90,
|
||||||
"path": "skills-dev/dev-deploy-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-deploy-plugin/skills/SKILL.md",
|
||||||
"digest": "00873ab922fb1595"
|
"digest": "fd15a9e9135bcc49",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-integration",
|
"name": "dev-integration",
|
||||||
@@ -283,7 +420,9 @@
|
|||||||
"description": "前后端联调技能。API 契约验证、接口对接、联调报告生成。对应 req 流程 integration 阶段。纯后端需求自动跳过。",
|
"description": "前后端联调技能。API 契约验证、接口对接、联调报告生成。对应 req 流程 integration 阶段。纯后端需求自动跳过。",
|
||||||
"description_len": 64,
|
"description_len": 64,
|
||||||
"path": "skills-dev/dev-integration-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-integration-plugin/skills/SKILL.md",
|
||||||
"digest": "eff6be8eb57b7fef"
|
"digest": "eff6be8eb57b7fef",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-ios",
|
"name": "dev-ios",
|
||||||
@@ -294,7 +433,9 @@
|
|||||||
"description": "iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。",
|
"description": "iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。",
|
||||||
"description_len": 65,
|
"description_len": 65,
|
||||||
"path": "skills-dev/dev-ios-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-ios-plugin/skills/SKILL.md",
|
||||||
"digest": "44499e31f1abd5e2"
|
"digest": "44499e31f1abd5e2",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-mcp",
|
"name": "dev-mcp",
|
||||||
@@ -305,7 +446,9 @@
|
|||||||
"description": "MCP Bridge 开发插件。TypeScript MCP 服务开发,Token 管理,HTTP 客户端模式。当涉及 mcp-task-bridge 开发时按需加载。",
|
"description": "MCP Bridge 开发插件。TypeScript MCP 服务开发,Token 管理,HTTP 客户端模式。当涉及 mcp-task-bridge 开发时按需加载。",
|
||||||
"description_len": 84,
|
"description_len": 84,
|
||||||
"path": "skills-dev/dev-mcp-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-mcp-plugin/skills/SKILL.md",
|
||||||
"digest": "83d5912212fcfa4a"
|
"digest": "83d5912212fcfa4a",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-pda",
|
"name": "dev-pda",
|
||||||
@@ -316,7 +459,9 @@
|
|||||||
"description": "PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。",
|
"description": "PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。",
|
||||||
"description_len": 57,
|
"description_len": 57,
|
||||||
"path": "skills-dev/dev-pda-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-pda-plugin/skills/SKILL.md",
|
||||||
"digest": "2524f2ecd4f2a834"
|
"digest": "2524f2ecd4f2a834",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-review",
|
"name": "dev-review",
|
||||||
@@ -327,7 +472,9 @@
|
|||||||
"description": "代码评审技能。六视角对抗性扫描法(含 Karpathy Scope 审计),用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。",
|
"description": "代码评审技能。六视角对抗性扫描法(含 Karpathy Scope 审计),用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。",
|
||||||
"description_len": 91,
|
"description_len": 91,
|
||||||
"path": "skills-dev/dev-review-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-review-plugin/skills/SKILL.md",
|
||||||
"digest": "a8a1ec79b85e3227"
|
"digest": "a8a1ec79b85e3227",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-scaffold",
|
"name": "dev-scaffold",
|
||||||
@@ -338,7 +485,9 @@
|
|||||||
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架。挂载在 dev 阶段,新建模块时激活。",
|
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架。挂载在 dev 阶段,新建模块时激活。",
|
||||||
"description_len": 43,
|
"description_len": 43,
|
||||||
"path": "skills-dev/dev-scaffold-plugin/skills/SKILL.md",
|
"path": "skills-dev/dev-scaffold-plugin/skills/SKILL.md",
|
||||||
"digest": "4044787b1e468302"
|
"digest": "4044787b1e468302",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-test",
|
"name": "dev-test",
|
||||||
@@ -349,7 +498,9 @@
|
|||||||
"description": "软件测试技能。用于单元测试、集成测试、E2E测试、测试用例设计。支持 Go、Vue、React、iOS、Android 等多平台测试。",
|
"description": "软件测试技能。用于单元测试、集成测试、E2E测试、测试用例设计。支持 Go、Vue、React、iOS、Android 等多平台测试。",
|
||||||
"description_len": 67,
|
"description_len": 67,
|
||||||
"path": "skills-dev/dev-test-plugin/skills/dev-test/SKILL.md",
|
"path": "skills-dev/dev-test-plugin/skills/dev-test/SKILL.md",
|
||||||
"digest": "a03536a478eabe6c"
|
"digest": "a03536a478eabe6c",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "executing-plans",
|
"name": "executing-plans",
|
||||||
@@ -360,7 +511,9 @@
|
|||||||
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints",
|
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints",
|
||||||
"description_len": 104,
|
"description_len": 104,
|
||||||
"path": "skills-dev/executing-plans-plugin/skills/SKILL.md",
|
"path": "skills-dev/executing-plans-plugin/skills/SKILL.md",
|
||||||
"digest": "8991a50091af83a4"
|
"digest": "8991a50091af83a4",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "finishing-a-development-branch",
|
"name": "finishing-a-development-branch",
|
||||||
@@ -371,7 +524,9 @@
|
|||||||
"description": "Use when implementation is complete and all tests pass - verifies and creates PR",
|
"description": "Use when implementation is complete and all tests pass - verifies and creates PR",
|
||||||
"description_len": 80,
|
"description_len": 80,
|
||||||
"path": "skills-dev/finishing-branch-plugin/skills/SKILL.md",
|
"path": "skills-dev/finishing-branch-plugin/skills/SKILL.md",
|
||||||
"digest": "1102047ccc060e60"
|
"digest": "1102047ccc060e60",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "frontend-design",
|
"name": "frontend-design",
|
||||||
@@ -382,7 +537,9 @@
|
|||||||
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.",
|
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.",
|
||||||
"description_len": 234,
|
"description_len": 234,
|
||||||
"path": "skills-dev/frontend-design-plugin/skills/SKILL.md",
|
"path": "skills-dev/frontend-design-plugin/skills/SKILL.md",
|
||||||
"digest": "6bc30a1a562bae8c"
|
"digest": "84e9c104be7967cc",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "karpathy-guidelines",
|
"name": "karpathy-guidelines",
|
||||||
@@ -393,7 +550,9 @@
|
|||||||
"description": "Karpathy 四原则编码行为守则。减少 LLM 常见编码错误:过度实现、静默假设、顺手重构、无验证标准。在任意编码场景激活。",
|
"description": "Karpathy 四原则编码行为守则。减少 LLM 常见编码错误:过度实现、静默假设、顺手重构、无验证标准。在任意编码场景激活。",
|
||||||
"description_len": 64,
|
"description_len": 64,
|
||||||
"path": "skills-dev/karpathy-guidelines-plugin/skills/SKILL.md",
|
"path": "skills-dev/karpathy-guidelines-plugin/skills/SKILL.md",
|
||||||
"digest": "a68f347031110d6a"
|
"digest": "a68f347031110d6a",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pull-request",
|
"name": "pull-request",
|
||||||
@@ -404,7 +563,9 @@
|
|||||||
"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": "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,
|
"description_len": 219,
|
||||||
"path": "skills-dev/pull-request-plugin/skills/SKILL.md",
|
"path": "skills-dev/pull-request-plugin/skills/SKILL.md",
|
||||||
"digest": "40dac0d290782bb5"
|
"digest": "40dac0d290782bb5",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "review-checklist",
|
"name": "review-checklist",
|
||||||
@@ -415,7 +576,9 @@
|
|||||||
"description": "项目级代码评审检查清单。按项目积累特定检查项,挂载在 dev-review 下自动加载。每次 CR 时触发。",
|
"description": "项目级代码评审检查清单。按项目积累特定检查项,挂载在 dev-review 下自动加载。每次 CR 时触发。",
|
||||||
"description_len": 54,
|
"description_len": 54,
|
||||||
"path": "skills-dev/review-checklist-plugin/skills/SKILL.md",
|
"path": "skills-dev/review-checklist-plugin/skills/SKILL.md",
|
||||||
"digest": "5bae13b6a8d92650"
|
"digest": "5bae13b6a8d92650",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "slark-cicd",
|
"name": "slark-cicd",
|
||||||
@@ -426,7 +589,9 @@
|
|||||||
"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": "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,
|
"description_len": 285,
|
||||||
"path": "skills-dev/slark-cicd-plugin/skills/SKILL.md",
|
"path": "skills-dev/slark-cicd-plugin/skills/SKILL.md",
|
||||||
"digest": "2919c19d8a38dd23"
|
"digest": "1ff9b89a5fbc6e2d",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "slark-dsh-cicd",
|
"name": "slark-dsh-cicd",
|
||||||
@@ -437,7 +602,9 @@
|
|||||||
"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": "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,
|
"description_len": 306,
|
||||||
"path": "skills-dev/slark-dsh-cicd-plugin/skills/SKILL.md",
|
"path": "skills-dev/slark-dsh-cicd-plugin/skills/SKILL.md",
|
||||||
"digest": "44a1322e15d42845"
|
"digest": "9819e2d515ab44e0",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "data-excel",
|
"name": "data-excel",
|
||||||
@@ -448,7 +615,9 @@
|
|||||||
"description": "Excel 数据处理与 BI 集成。通过自然语言操作 Excel 文件的读取、编辑、转换,并支持导入到 BI 系统(Metabase)进行可视化分析。当用户提到 Excel、表格处理、数据导入、BI 分析相关任务时自动激活。",
|
"description": "Excel 数据处理与 BI 集成。通过自然语言操作 Excel 文件的读取、编辑、转换,并支持导入到 BI 系统(Metabase)进行可视化分析。当用户提到 Excel、表格处理、数据导入、BI 分析相关任务时自动激活。",
|
||||||
"description_len": 112,
|
"description_len": 112,
|
||||||
"path": "skills-integration/data-excel-plugin/skills/SKILL.md",
|
"path": "skills-integration/data-excel-plugin/skills/SKILL.md",
|
||||||
"digest": "8dc4be8f88cfd866"
|
"digest": "8dc4be8f88cfd866",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doubao-voice",
|
"name": "doubao-voice",
|
||||||
@@ -459,7 +628,9 @@
|
|||||||
"description": "豆包语音API调用。支持语音合成(TTS)和唱歌。当用户提到语音合成、文字转语音、唱歌、豆包语音相关任务时自动激活。",
|
"description": "豆包语音API调用。支持语音合成(TTS)和唱歌。当用户提到语音合成、文字转语音、唱歌、豆包语音相关任务时自动激活。",
|
||||||
"description_len": 58,
|
"description_len": 58,
|
||||||
"path": "skills-integration/doubao-voice-plugin/skills/SKILL.md",
|
"path": "skills-integration/doubao-voice-plugin/skills/SKILL.md",
|
||||||
"digest": "7dc73d3b97e61fd2"
|
"digest": "7dc73d3b97e61fd2",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "feishu-bitable",
|
"name": "feishu-bitable",
|
||||||
@@ -470,7 +641,9 @@
|
|||||||
"description": "飞书多维表格操作。用于记录增删改查、批量操作、筛选排序、数据同步。当需要操作飞书多维表格时使用。",
|
"description": "飞书多维表格操作。用于记录增删改查、批量操作、筛选排序、数据同步。当需要操作飞书多维表格时使用。",
|
||||||
"description_len": 48,
|
"description_len": 48,
|
||||||
"path": "skills-integration/feishu-bitable-plugin/skills/SKILL.md",
|
"path": "skills-integration/feishu-bitable-plugin/skills/SKILL.md",
|
||||||
"digest": "3bf37533007365a3"
|
"digest": "3bf37533007365a3",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "feishu-docx",
|
"name": "feishu-docx",
|
||||||
@@ -481,7 +654,9 @@
|
|||||||
"description": "飞书云文档操作。用于创建、编辑云文档,插入内容块,会议纪要生成。当需要操作飞书云文档时使用。",
|
"description": "飞书云文档操作。用于创建、编辑云文档,插入内容块,会议纪要生成。当需要操作飞书云文档时使用。",
|
||||||
"description_len": 46,
|
"description_len": 46,
|
||||||
"path": "skills-integration/feishu-docx-plugin/skills/SKILL.md",
|
"path": "skills-integration/feishu-docx-plugin/skills/SKILL.md",
|
||||||
"digest": "49d097bfa2c283ab"
|
"digest": "49d097bfa2c283ab",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "feishu",
|
"name": "feishu",
|
||||||
@@ -492,7 +667,9 @@
|
|||||||
"description": "飞书文档与多维表格操作入口。当用户提到飞书、云文档、多维表格、Bitable 相关任务时自动激活。",
|
"description": "飞书文档与多维表格操作入口。当用户提到飞书、云文档、多维表格、Bitable 相关任务时自动激活。",
|
||||||
"description_len": 49,
|
"description_len": 49,
|
||||||
"path": "skills-integration/feishu-plugin/skills/SKILL.md",
|
"path": "skills-integration/feishu-plugin/skills/SKILL.md",
|
||||||
"digest": "973c6ac22104ee38"
|
"digest": "973c6ac22104ee38",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "siyuan",
|
"name": "siyuan",
|
||||||
@@ -503,7 +680,9 @@
|
|||||||
"description": "思源笔记 API 集成。通过自然语言创建、编辑、搜索笔记,管理笔记本和文档树。当用户提到思源笔记、SiYuan、笔记管理、知识库相关任务时自动激活。",
|
"description": "思源笔记 API 集成。通过自然语言创建、编辑、搜索笔记,管理笔记本和文档树。当用户提到思源笔记、SiYuan、笔记管理、知识库相关任务时自动激活。",
|
||||||
"description_len": 74,
|
"description_len": 74,
|
||||||
"path": "skills-integration/siyuan-plugin/skills/SKILL.md",
|
"path": "skills-integration/siyuan-plugin/skills/SKILL.md",
|
||||||
"digest": "55f6175d81e7b524"
|
"digest": "2736c2b708791763",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "siyuan-to-feishu",
|
"name": "siyuan-to-feishu",
|
||||||
@@ -514,7 +693,9 @@
|
|||||||
"description": "将思源笔记导出为 PDF 并发送到飞书群。当用户提到\"发送笔记\"、\"导出PDF发飞书\"、\"/siyuan send\"、\"分享到飞书\"相关任务时自动激活。",
|
"description": "将思源笔记导出为 PDF 并发送到飞书群。当用户提到\"发送笔记\"、\"导出PDF发飞书\"、\"/siyuan send\"、\"分享到飞书\"相关任务时自动激活。",
|
||||||
"description_len": 76,
|
"description_len": 76,
|
||||||
"path": "skills-integration/siyuan-to-feishu-plugin/skills/SKILL.md",
|
"path": "skills-integration/siyuan-to-feishu-plugin/skills/SKILL.md",
|
||||||
"digest": "998dace910c1eb7a"
|
"digest": "998dace910c1eb7a",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "wecom",
|
"name": "wecom",
|
||||||
@@ -525,7 +706,9 @@
|
|||||||
"description": "企业微信集成。通过自然语言发送消息、管理群机器人、操作审批流程、管理通讯录。当用户提到企业微信、微信工作、群机器人、企业号、wecom相关任务时自动激活。",
|
"description": "企业微信集成。通过自然语言发送消息、管理群机器人、操作审批流程、管理通讯录。当用户提到企业微信、微信工作、群机器人、企业号、wecom相关任务时自动激活。",
|
||||||
"description_len": 77,
|
"description_len": 77,
|
||||||
"path": "skills-integration/wecom-plugin/skills/SKILL.md",
|
"path": "skills-integration/wecom-plugin/skills/SKILL.md",
|
||||||
"digest": "cec289b67805389d"
|
"digest": "7084d88ccb87ba9b",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-audit",
|
"name": "req-audit",
|
||||||
@@ -536,7 +719,9 @@
|
|||||||
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
|
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
|
||||||
"description_len": 55,
|
"description_len": 55,
|
||||||
"path": "skills-req/req-audit-plugin/skills/SKILL.md",
|
"path": "skills-req/req-audit-plugin/skills/SKILL.md",
|
||||||
"digest": "ff2e6197bbb059c6"
|
"digest": "ff2e6197bbb059c6",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-compare",
|
"name": "req-compare",
|
||||||
@@ -547,7 +732,9 @@
|
|||||||
"description": "对比式需求分析插件。系统平移、竞品借鉴、版本升级时使用参考对象对比法编写 PRD。挂载在 analysis 阶段,需要对比分析时由 req-prd 推荐激活。",
|
"description": "对比式需求分析插件。系统平移、竞品借鉴、版本升级时使用参考对象对比法编写 PRD。挂载在 analysis 阶段,需要对比分析时由 req-prd 推荐激活。",
|
||||||
"description_len": 79,
|
"description_len": 79,
|
||||||
"path": "skills-req/req-compare-plugin/skills/SKILL.md",
|
"path": "skills-req/req-compare-plugin/skills/SKILL.md",
|
||||||
"digest": "3428e3f37ef9e9e8"
|
"digest": "3428e3f37ef9e9e8",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-design",
|
"name": "req-design",
|
||||||
@@ -558,7 +745,9 @@
|
|||||||
"description": "需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。",
|
"description": "需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。",
|
||||||
"description_len": 96,
|
"description_len": 96,
|
||||||
"path": "skills-req/req-design-plugin/skills/SKILL.md",
|
"path": "skills-req/req-design-plugin/skills/SKILL.md",
|
||||||
"digest": "8ec85838666b5cc5"
|
"digest": "74848800cab6e269",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-dev",
|
"name": "req-dev",
|
||||||
@@ -569,7 +758,9 @@
|
|||||||
"description": "需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。",
|
"description": "需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。",
|
||||||
"description_len": 74,
|
"description_len": 74,
|
||||||
"path": "skills-req/req-dev-plugin/skills/SKILL.md",
|
"path": "skills-req/req-dev-plugin/skills/SKILL.md",
|
||||||
"digest": "3b0dd9396df3c236"
|
"digest": "494a5be5e7a11c0b",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-lookback",
|
"name": "req-lookback",
|
||||||
@@ -580,7 +771,9 @@
|
|||||||
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
|
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
|
||||||
"description_len": 47,
|
"description_len": 47,
|
||||||
"path": "skills-req/req-lookback-plugin/skills/SKILL.md",
|
"path": "skills-req/req-lookback-plugin/skills/SKILL.md",
|
||||||
"digest": "cc6bff614a8f7bf4"
|
"digest": "cc6bff614a8f7bf4",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req",
|
"name": "req",
|
||||||
@@ -591,7 +784,9 @@
|
|||||||
"description": "需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。",
|
"description": "需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。",
|
||||||
"description_len": 90,
|
"description_len": 90,
|
||||||
"path": "skills-req/req-plugin/skills/SKILL.md",
|
"path": "skills-req/req-plugin/skills/SKILL.md",
|
||||||
"digest": "bdb1fab4d3d8c3d2"
|
"digest": "193cf871b73faff1",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-prd",
|
"name": "req-prd",
|
||||||
@@ -602,7 +797,9 @@
|
|||||||
"description": "产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。",
|
"description": "产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。",
|
||||||
"description_len": 76,
|
"description_len": 76,
|
||||||
"path": "skills-req/req-prd-plugin/skills/SKILL.md",
|
"path": "skills-req/req-prd-plugin/skills/SKILL.md",
|
||||||
"digest": "0e5b16fcef3105bd"
|
"digest": "7c4ad2baab6b2118",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-prototype",
|
"name": "req-prototype",
|
||||||
@@ -613,7 +810,9 @@
|
|||||||
"description": "原型生成与关联。生成可交互 HTML、上传到 ai-proj OSS 并关联 Requirement/PRD iframe,也支持 Stitch AI 视觉探索。",
|
"description": "原型生成与关联。生成可交互 HTML、上传到 ai-proj OSS 并关联 Requirement/PRD iframe,也支持 Stitch AI 视觉探索。",
|
||||||
"description_len": 81,
|
"description_len": 81,
|
||||||
"path": "skills-req/req-prototype-plugin/skills/SKILL.md",
|
"path": "skills-req/req-prototype-plugin/skills/SKILL.md",
|
||||||
"digest": "7e362f4f895fb50a"
|
"digest": "7e362f4f895fb50a",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-research",
|
"name": "req-research",
|
||||||
@@ -624,7 +823,9 @@
|
|||||||
"description": "需求调研插件。代码审计、数据库分析、现有功能调研、技术可行性评估。挂载在 analysis 阶段,需要深度调研时由 req-prd 推荐激活。",
|
"description": "需求调研插件。代码审计、数据库分析、现有功能调研、技术可行性评估。挂载在 analysis 阶段,需要深度调研时由 req-prd 推荐激活。",
|
||||||
"description_len": 71,
|
"description_len": 71,
|
||||||
"path": "skills-req/req-research-plugin/skills/SKILL.md",
|
"path": "skills-req/req-research-plugin/skills/SKILL.md",
|
||||||
"digest": "a94924f53bd4c80e"
|
"digest": "a94924f53bd4c80e",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-retro",
|
"name": "req-retro",
|
||||||
@@ -635,7 +836,9 @@
|
|||||||
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
|
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
|
||||||
"description_len": 57,
|
"description_len": 57,
|
||||||
"path": "skills-req/req-retro-plugin/skills/SKILL.md",
|
"path": "skills-req/req-retro-plugin/skills/SKILL.md",
|
||||||
"digest": "636b7099d85e9549"
|
"digest": "636b7099d85e9549",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-review",
|
"name": "req-review",
|
||||||
@@ -646,7 +849,9 @@
|
|||||||
"description": "PRD 评审方法论。用于需求评审、PRD 文档审查、评审意见编写。当执行 /req review 或需要评审 PRD 文档时使用。",
|
"description": "PRD 评审方法论。用于需求评审、PRD 文档审查、评审意见编写。当执行 /req review 或需要评审 PRD 文档时使用。",
|
||||||
"description_len": 65,
|
"description_len": 65,
|
||||||
"path": "skills-req/req-review-plugin/skills/SKILL.md",
|
"path": "skills-req/req-review-plugin/skills/SKILL.md",
|
||||||
"digest": "cd1a5181d72f35f4"
|
"digest": "cd1a5181d72f35f4",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-test-gate",
|
"name": "req-test-gate",
|
||||||
@@ -657,7 +862,9 @@
|
|||||||
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。",
|
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。",
|
||||||
"description_len": 159,
|
"description_len": 159,
|
||||||
"path": "skills-req/req-test-gate-plugin/skills/SKILL.md",
|
"path": "skills-req/req-test-gate-plugin/skills/SKILL.md",
|
||||||
"digest": "0eba060fa456885d"
|
"digest": "ceebcc2516ad40d3",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "req-workflow",
|
"name": "req-workflow",
|
||||||
@@ -668,7 +875,9 @@
|
|||||||
"description": "需求完整工作流。用于从创建到归档的完整流程、Hook 自动同步、测试环境流程。当需要了解需求完整生命周期或同步策略时使用。",
|
"description": "需求完整工作流。用于从创建到归档的完整流程、Hook 自动同步、测试环境流程。当需要了解需求完整生命周期或同步策略时使用。",
|
||||||
"description_len": 61,
|
"description_len": 61,
|
||||||
"path": "skills-req/req-workflow-plugin/skills/SKILL.md",
|
"path": "skills-req/req-workflow-plugin/skills/SKILL.md",
|
||||||
"digest": "87499da3817d162d"
|
"digest": "87499da3817d162d",
|
||||||
|
"has_evals": false,
|
||||||
|
"has_docs": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "verification-before-completion",
|
"name": "verification-before-completion",
|
||||||
@@ -679,7 +888,9 @@
|
|||||||
"description": "汇报「完成/修好/通过」或关闭任务、提交 PR、宣布 REQ 完成之前,必须先运行验证命令并附证据;没有证据不得声称完成。当准备说完成、关闭任务、或汇报交付前使用。",
|
"description": "汇报「完成/修好/通过」或关闭任务、提交 PR、宣布 REQ 完成之前,必须先运行验证命令并附证据;没有证据不得声称完成。当准备说完成、关闭任务、或汇报交付前使用。",
|
||||||
"description_len": 82,
|
"description_len": 82,
|
||||||
"path": "skills-req/verification-before-completion-plugin/skills/SKILL.md",
|
"path": "skills-req/verification-before-completion-plugin/skills/SKILL.md",
|
||||||
"digest": "7c23649f4a0a8798"
|
"digest": "7c23649f4a0a8798",
|
||||||
|
"has_evals": true,
|
||||||
|
"has_docs": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ on:
|
|||||||
|
|
||||||
- devflow-claude: `plugins/req/commands/release.md`
|
- devflow-claude: `plugins/req/commands/release.md`
|
||||||
- REQ-20260416-0017(母需求)
|
- REQ-20260416-0017(母需求)
|
||||||
- 当前 build.yaml: `/Users/donglinlai/coding/qiudl/new-ai-proj/.gitea/workflows/build.yaml`
|
- 当前 build.yaml: `~/coding/qiudl/new-ai-proj/.gitea/workflows/build.yaml`
|
||||||
- Gitea Release API: https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateRelease
|
- Gitea Release API: https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateRelease
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+3
-3
@@ -23,7 +23,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
"command": "~/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
||||||
"timeout": 10
|
"timeout": 10
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/pre-tool-confirm.sh",
|
"command": "~/coding/qiudl/ai-proj-helper/hooks/pre-tool-confirm.sh",
|
||||||
"timeout": 30
|
"timeout": 30
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
### 方式 2:一键安装脚本
|
### 方式 2:一键安装脚本
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash /Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/install.sh
|
bash ~/coding/qiudl/ai-proj-helper/hooks/install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## 验证
|
## 验证
|
||||||
|
|||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# skill-gate 本地门禁:Gitea Actions 没有 runner,靠这个兜底。
|
||||||
|
# 跳过:SKILL_GATE_SKIP=1 git commit ...(需在提交信息里说明原因)
|
||||||
|
set -uo pipefail
|
||||||
|
[ "${SKILL_GATE_SKIP:-0}" = "1" ] && { echo "[skill-gate] skipped by SKILL_GATE_SKIP=1"; exit 0; }
|
||||||
|
cd "$(git rev-parse --show-toplevel)" || exit 1
|
||||||
|
fail=0
|
||||||
|
echo "[skill-gate] validate --strict"
|
||||||
|
python3 scripts/validate-skills.py --strict || fail=1
|
||||||
|
echo "[skill-gate] scan"
|
||||||
|
./scripts/scan-skill.sh >/tmp/skill-gate-scan.log 2>&1 || { fail=1; tail -5 /tmp/skill-gate-scan.log; }
|
||||||
|
echo "[skill-gate] catalog --check"
|
||||||
|
python3 scripts/generate-catalog.py --check || fail=1
|
||||||
|
if [ "$fail" -ne 0 ]; then
|
||||||
|
echo "[skill-gate] 未通过:先修复,或用 SKILL_GATE_SKIP=1 并在提交信息里说明原因"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "[skill-gate] OK"
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# skill-gate 推送前门禁:安装器测试 + 覆盖率提示
|
||||||
|
set -uo pipefail
|
||||||
|
[ "${SKILL_GATE_SKIP:-0}" = "1" ] && exit 0
|
||||||
|
cd "$(git rev-parse --show-toplevel)" || exit 1
|
||||||
|
echo "[skill-gate] install-skills 测试"
|
||||||
|
bash tests/install-skills.test.sh || { echo "[skill-gate] 测试失败"; exit 1; }
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
# 安装方式:
|
# 安装方式:
|
||||||
# 在 ~/.claude/settings.json 的 hooks.SessionStart 配置:
|
# 在 ~/.claude/settings.json 的 hooks.SessionStart 配置:
|
||||||
# {
|
# {
|
||||||
# "command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
# "command": "$HOME/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
||||||
# "timeout": 10
|
# "timeout": 10
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
|
|||||||
+27
-1
@@ -10,6 +10,8 @@
|
|||||||
# --audit Show installed vs repo versions for the selected target
|
# --audit Show installed vs repo versions for the selected target
|
||||||
# --include-beta Also install skills whose plugin.json status=beta
|
# --include-beta Also install skills whose plugin.json status=beta
|
||||||
# --no-validate Skip scripts/validate-skills.py before installing
|
# --no-validate Skip scripts/validate-skills.py before installing
|
||||||
|
# --scope <scope> user (default) or project (installs into <project>/.agents/skills)
|
||||||
|
# --project-dir <dir> Project root, required when --scope project
|
||||||
# --dry-run Preview changes without writing anything
|
# --dry-run Preview changes without writing anything
|
||||||
# --category <cat> Only install plugins in dir_category=<cat>
|
# --category <cat> Only install plugins in dir_category=<cat>
|
||||||
# Valid values: biz, core, dev, integration, personal, req
|
# Valid values: biz, core, dev, integration, personal, req
|
||||||
@@ -38,6 +40,8 @@ LINK_MODE=false
|
|||||||
AUDIT_ONLY=false
|
AUDIT_ONLY=false
|
||||||
INCLUDE_BETA=false
|
INCLUDE_BETA=false
|
||||||
NO_VALIDATE=false
|
NO_VALIDATE=false
|
||||||
|
SCOPE="user"
|
||||||
|
PROJECT_DIR=""
|
||||||
|
|
||||||
# ── Colour helpers ─────────────────────────────────────────────────────────────
|
# ── Colour helpers ─────────────────────────────────────────────────────────────
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
@@ -63,6 +67,12 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--audit) AUDIT_ONLY=true ;;
|
--audit) AUDIT_ONLY=true ;;
|
||||||
--include-beta) INCLUDE_BETA=true ;;
|
--include-beta) INCLUDE_BETA=true ;;
|
||||||
--no-validate) NO_VALIDATE=true ;;
|
--no-validate) NO_VALIDATE=true ;;
|
||||||
|
--scope)
|
||||||
|
[[ $# -ge 2 ]] || { error "--scope requires user or project"; exit 1; }
|
||||||
|
SCOPE="$2"; shift ;;
|
||||||
|
--project-dir)
|
||||||
|
[[ $# -ge 2 ]] || { error "--project-dir requires a path"; exit 1; }
|
||||||
|
PROJECT_DIR="$2"; shift ;;
|
||||||
--force) FORCE=true ;;
|
--force) FORCE=true ;;
|
||||||
--cleanup) CLEANUP=true ;;
|
--cleanup) CLEANUP=true ;;
|
||||||
--list) LIST_ONLY=true ;;
|
--list) LIST_ONLY=true ;;
|
||||||
@@ -90,7 +100,7 @@ case "$AGENT_TARGET" in
|
|||||||
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.agents/.ai-proj-helper-installed-skills.json}"
|
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.agents/.ai-proj-helper-installed-skills.json}"
|
||||||
;;
|
;;
|
||||||
dsh)
|
dsh)
|
||||||
if [[ -z "${DSH_HOME:-}" ]]; then
|
if [[ "$SCOPE" != "project" && -z "${DSH_HOME:-}" ]]; then
|
||||||
error "--agent dsh requires DSH_HOME (or set AI_PROJ_HELPER_SKILLS_DIR explicitly)"
|
error "--agent dsh requires DSH_HOME (or set AI_PROJ_HELPER_SKILLS_DIR explicitly)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -108,6 +118,20 @@ case "$AGENT_TARGET" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# ── Project scope override ─────────────────────────────────────────────────────
|
||||||
|
if [[ "$SCOPE" == "project" ]]; then
|
||||||
|
if [[ -z "$PROJECT_DIR" ]]; then
|
||||||
|
error "--scope project requires --project-dir <project root>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
[[ -d "$PROJECT_DIR" ]] || { error "project dir 不存在: $PROJECT_DIR"; exit 1; }
|
||||||
|
PROJECT_DIR="$(cd "$PROJECT_DIR" && pwd)"
|
||||||
|
SKILLS_DIR="$PROJECT_DIR/.agents/skills"
|
||||||
|
COMMANDS_DIR=""
|
||||||
|
STATE_FILE="$PROJECT_DIR/.agents/.ai-proj-helper-installed-skills.json"
|
||||||
|
info "Project scope: $PROJECT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── State helpers (plain JSON via python3) ─────────────────────────────────────
|
# ── State helpers (plain JSON via python3) ─────────────────────────────────────
|
||||||
state_get() {
|
state_get() {
|
||||||
# state_get <install_name> -> prints version or empty string
|
# state_get <install_name> -> prints version or empty string
|
||||||
@@ -478,6 +502,8 @@ for pj in sorted(root.glob('skills-*/*-plugin/.claude-plugin/plugin.json')):
|
|||||||
name = meta.get('install_name')
|
name = meta.get('install_name')
|
||||||
if not name: continue
|
if not name: continue
|
||||||
status = meta.get('status', 'promoted')
|
status = meta.get('status', 'promoted')
|
||||||
|
if status == 'deprecated':
|
||||||
|
continue
|
||||||
want = meta.get('version', '')
|
want = meta.get('version', '')
|
||||||
got = state.get(name, {}).get('version', '')
|
got = state.get(name, {}).get('version', '')
|
||||||
itype = state.get(name, {}).get('install_type', '')
|
itype = state.get(name, {}).get('install_type', '')
|
||||||
|
|||||||
@@ -55,9 +55,12 @@ def build() -> dict:
|
|||||||
fm = frontmatter(skill_md.read_text(encoding="utf-8", errors="replace"))
|
fm = frontmatter(skill_md.read_text(encoding="utf-8", errors="replace"))
|
||||||
desc = fm.get("description", "")
|
desc = fm.get("description", "")
|
||||||
body = skill_md.read_text(encoding="utf-8", errors="replace")
|
body = skill_md.read_text(encoding="utf-8", errors="replace")
|
||||||
|
install_name = meta.get("install_name") or fm.get("name")
|
||||||
|
has_evals = any((plugin_dir / "skills").rglob("evals/evals.json"))
|
||||||
|
has_docs = (ROOT / "docs" / "skills" / f"{install_name}.md").is_file()
|
||||||
skills.append(
|
skills.append(
|
||||||
{
|
{
|
||||||
"name": meta.get("install_name") or fm.get("name"),
|
"name": install_name,
|
||||||
"category": meta.get("dir_category"),
|
"category": meta.get("dir_category"),
|
||||||
"version": meta.get("version"),
|
"version": meta.get("version"),
|
||||||
"status": meta.get("status", "promoted"),
|
"status": meta.get("status", "promoted"),
|
||||||
@@ -66,14 +69,28 @@ def build() -> dict:
|
|||||||
"description_len": len(desc),
|
"description_len": len(desc),
|
||||||
"path": str(skill_md.relative_to(ROOT)),
|
"path": str(skill_md.relative_to(ROOT)),
|
||||||
"digest": hashlib.sha256(body.encode("utf-8")).hexdigest()[:16],
|
"digest": hashlib.sha256(body.encode("utf-8")).hexdigest()[:16],
|
||||||
|
"has_evals": has_evals,
|
||||||
|
"has_docs": has_docs,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
counts: dict = {}
|
counts: dict = {}
|
||||||
for skill in skills:
|
for skill in skills:
|
||||||
key = f"{skill['status']}/{skill['invocation']}"
|
key = f"{skill['status']}/{skill['invocation']}"
|
||||||
counts[key] = counts.get(key, 0) + 1
|
counts[key] = counts.get(key, 0) + 1
|
||||||
|
required = [
|
||||||
|
x for x in skills
|
||||||
|
if x["status"] == "promoted" and x["invocation"] == "model-invoked"
|
||||||
|
]
|
||||||
|
coverage = {
|
||||||
|
"required": len(required),
|
||||||
|
"with_evals": sum(1 for x in required if x["has_evals"]),
|
||||||
|
"with_docs": sum(1 for x in required if x["has_docs"]),
|
||||||
|
"missing_evals": [x["name"] for x in required if not x["has_evals"]],
|
||||||
|
"missing_docs": [x["name"] for x in required if not x["has_docs"]],
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
"repo": "ai-proj-helper",
|
"repo": "ai-proj-helper",
|
||||||
|
"coverage": coverage,
|
||||||
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||||
"total": len(skills),
|
"total": len(skills),
|
||||||
"counts": counts,
|
"counts": counts,
|
||||||
|
|||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 安装本仓库的 git 门禁钩子(pre-commit / pre-push)。
|
||||||
|
set -euo pipefail
|
||||||
|
root="$(git rev-parse --show-toplevel)"
|
||||||
|
[ -d "$root/hooks/git" ] || { echo "找不到 hooks/git"; exit 1; }
|
||||||
|
mkdir -p "$root/.git/hooks"
|
||||||
|
for h in pre-commit pre-push; do
|
||||||
|
cp "$root/hooks/git/$h" "$root/.git/hooks/$h"
|
||||||
|
chmod +x "$root/.git/hooks/$h"
|
||||||
|
echo "已安装 .git/hooks/$h"
|
||||||
|
done
|
||||||
|
echo "跳过单次检查:SKILL_GATE_SKIP=1 git commit ..."
|
||||||
@@ -3,3 +3,11 @@
|
|||||||
# 例:某技能文档把危险命令当作“禁止清单”引用,而非真的要执行。
|
# 例:某技能文档把危险命令当作“禁止清单”引用,而非真的要执行。
|
||||||
skills-dev/slark-cicd-plugin::NEVER_AUTO
|
skills-dev/slark-cicd-plugin::NEVER_AUTO
|
||||||
skills-dev/slark-cicd-plugin::destructive
|
skills-dev/slark-cicd-plugin::destructive
|
||||||
|
|
||||||
|
# 运维 runbook 里的环境地址(公开可达的服务入口,非凭据),保留以维持 runbook 可执行性
|
||||||
|
skills-req/req-plugin::http://100.118.62.18
|
||||||
|
skills-dev/slark-cicd-plugin::http://15.135.112.181
|
||||||
|
skills-dev/dev-deploy-plugin::http://39.104.87.246
|
||||||
|
skills-dev/ai-chat-plugin::http://39.105.150.219
|
||||||
|
skills-dev/dev-deploy-plugin::http://www.apple.com/DTDs
|
||||||
|
skills-dev/dev-cicd-plugin::secrets.REPO_TOKEN
|
||||||
|
|||||||
Executable
+58
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# scan-installed.sh — 扫描「已安装但不是本仓库管理」的第三方技能,找出高风险内容。
|
||||||
|
# 用法: ./scripts/scan-installed.sh [--strict] [dir ...]
|
||||||
|
# 默认扫描 ~/.agents/skills 与 $DSH_HOME/skills
|
||||||
|
# --strict: 有高危项则退出码 1
|
||||||
|
set -uo pipefail
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
STRICT=0
|
||||||
|
dirs=()
|
||||||
|
for a in "$@"; do
|
||||||
|
case "$a" in
|
||||||
|
--strict) STRICT=1 ;;
|
||||||
|
*) dirs+=("$a") ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [[ ${#dirs[@]} -eq 0 ]]; then
|
||||||
|
dirs+=("${HOME}/.agents/skills")
|
||||||
|
[[ -n "${DSH_HOME:-}" ]] && dirs+=("${DSH_HOME}/skills")
|
||||||
|
fi
|
||||||
|
|
||||||
|
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,}'
|
||||||
|
)
|
||||||
|
|
||||||
|
managed=""
|
||||||
|
for st in "${HOME}/.agents/.ai-proj-helper-installed-skills.json" \
|
||||||
|
"${DSH_HOME:-/nonexistent}/.ai-proj-helper-installed-skills.json"; do
|
||||||
|
[[ -f "$st" ]] && managed="$managed $(python3 -c "import json;print(' '.join(json.load(open('$st')).keys()))" 2>/dev/null)"
|
||||||
|
done
|
||||||
|
|
||||||
|
total=0 high=0 third=0
|
||||||
|
for dir in "${dirs[@]}"; do
|
||||||
|
[[ -d "$dir" ]] || continue
|
||||||
|
for entry in "$dir"/*; do
|
||||||
|
[[ -e "$entry" || -L "$entry" ]] || continue
|
||||||
|
name="$(basename "$entry")"
|
||||||
|
[[ " $managed " == *" $name "* ]] && continue
|
||||||
|
third=$((third + 1))
|
||||||
|
for pattern in "${HIGH_PATTERNS[@]}"; do
|
||||||
|
while IFS= read -r hit; do
|
||||||
|
echo "[HIGH] $name: ${hit#*:}"
|
||||||
|
high=$((high + 1)); total=$((total + 1))
|
||||||
|
done < <(grep -rnE --include='*.md' --include='*.sh' --include='*.py' --include='*.js' --include='*.ts' --include='*.json' -- "$pattern" "$entry" 2>/dev/null | cut -c1-180)
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "扫描完成:第三方技能 $third 个,高危 $high 处"
|
||||||
|
[[ "$STRICT" == 1 && "$high" -gt 0 ]] && { echo "存在高危第三方技能,需人工确认来源。"; exit 1; }
|
||||||
|
exit 0
|
||||||
@@ -24,7 +24,7 @@ HIGH_PATTERNS=(
|
|||||||
# Warnings: machine-specific paths, plain http endpoints.
|
# Warnings: machine-specific paths, plain http endpoints.
|
||||||
WARN_PATTERNS=(
|
WARN_PATTERNS=(
|
||||||
'/Users/[A-Za-z0-9._-]+/'
|
'/Users/[A-Za-z0-9._-]+/'
|
||||||
'http://[A-Za-z0-9.-]+'
|
'http://[A-Za-z0-9.:-]+'
|
||||||
'password[[:space:]]*=[[:space:]]*[^$]'
|
'password[[:space:]]*=[[:space:]]*[^$]'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,6 +67,14 @@ for dir in "${targets[@]}"; do
|
|||||||
done
|
done
|
||||||
for pattern in "${WARN_PATTERNS[@]}"; do
|
for pattern in "${WARN_PATTERNS[@]}"; do
|
||||||
while IFS= read -r hit; do
|
while IFS= read -r hit; do
|
||||||
|
# 本机回环地址不算风险(本地开发/测试示例)
|
||||||
|
case "$hit" in
|
||||||
|
*"http://localhost"*|*"http://127.0.0.1"*) continue ;;
|
||||||
|
esac
|
||||||
|
if is_allowlisted "${hit%%:*}" "$hit"; then
|
||||||
|
echo "[allow] $rel: ${hit#*:}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
echo "[warn] $rel: ${hit%%:*}:${hit#*:}"
|
echo "[warn] $rel: ${hit%%:*}:${hit#*:}"
|
||||||
WARN=$((WARN + 1))
|
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 < <(grep -rnE --include='*.md' --include='*.sh' --include='*.py' --include='*.js' --include='*.ts' -- "$pattern" "$dir" 2>/dev/null | cut -c1-200)
|
||||||
|
|||||||
@@ -127,16 +127,67 @@ def check_skill(rel: Path, skill_md: Path, meta: dict):
|
|||||||
if not docs.is_file():
|
if not docs.is_file():
|
||||||
warnings.append(f"{where}: promoted 技能缺少四段式文档 docs/skills/{docs.name}")
|
warnings.append(f"{where}: promoted 技能缺少四段式文档 docs/skills/{docs.name}")
|
||||||
|
|
||||||
# Relative links must resolve.
|
# Relative links must resolve (inline code spans are examples, skip them).
|
||||||
for target in re.findall(r"\]\((\.[^)\s]+|[A-Za-z0-9_-]+/[^)\s]+)\)", body):
|
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://", "#")):
|
if target.startswith(("http://", "https://", "#")):
|
||||||
continue
|
continue
|
||||||
if not (skill_md.parent / target).exists():
|
if not (skill_md.parent / target).exists():
|
||||||
warnings.append(f"{where}: 引用的相对路径不存在: {target}")
|
warnings.append(f"{where}: 引用的相对路径不存在: {target}")
|
||||||
|
|
||||||
|
|
||||||
|
IGNORED_ROUTING_TOKENS = {
|
||||||
|
"completed", "released", "delivery_stage", "status", "true", "false", "user-invoked",
|
||||||
|
"model-invoked", "promoted", "beta", "deprecated", "scoop", "scoop.md",
|
||||||
|
}
|
||||||
|
EXTERNAL_SKILLS = {
|
||||||
|
# 本仓库之外、由 DSH 用户目录提供的技能(docs/skill-routing.md 允许引用)
|
||||||
|
"project-management", "topic-distill", "diary", "dsh-worktable-page",
|
||||||
|
"code-review", "release-notes", "disk-cleanup", "workflow-one",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def check_routing(catalog_names: set[str]):
|
||||||
|
routing = ROOT / "docs" / "skill-routing.md"
|
||||||
|
if not routing.is_file():
|
||||||
|
return
|
||||||
|
text = routing.read_text(encoding="utf-8")
|
||||||
|
for token in sorted(set(re.findall(r"`([a-z][a-z0-9-]{2,40})`", text))):
|
||||||
|
if token in IGNORED_ROUTING_TOKENS or token in EXTERNAL_SKILLS:
|
||||||
|
continue
|
||||||
|
if token not in catalog_names:
|
||||||
|
warnings.append(f"docs/skill-routing.md: 路由到不存在的技能 {token!r}")
|
||||||
|
|
||||||
|
|
||||||
|
def coverage_report() -> int:
|
||||||
|
missing_evals, missing_docs = [], []
|
||||||
|
for plugin_json in sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json")):
|
||||||
|
meta = json.loads(plugin_json.read_text())
|
||||||
|
if meta.get("status", "promoted") != "promoted":
|
||||||
|
continue
|
||||||
|
skill_md = next((plugin_json.parent.parent / "skills").rglob("SKILL.md"), None)
|
||||||
|
if skill_md is None:
|
||||||
|
continue
|
||||||
|
fm, _ = parse_frontmatter(skill_md.read_text(encoding="utf-8", errors="replace"))
|
||||||
|
if (fm.get("metadata") or {}).get("invocation", "model-invoked") != "model-invoked":
|
||||||
|
continue
|
||||||
|
name = meta.get("install_name") or fm.get("name")
|
||||||
|
if not any((plugin_json.parent.parent / "skills").rglob("evals/evals.json")):
|
||||||
|
missing_evals.append(name)
|
||||||
|
if not (ROOT / "docs" / "skills" / f"{name}.md").is_file():
|
||||||
|
missing_docs.append(name)
|
||||||
|
print(f"覆盖率(promoted + model-invoked):缺 evals {len(missing_evals)} 个,缺四段式文档 {len(missing_docs)} 个")
|
||||||
|
if missing_evals:
|
||||||
|
print("缺 evals:", ", ".join(missing_evals[:20]) + ("..." if len(missing_evals) > 20 else ""))
|
||||||
|
if missing_docs:
|
||||||
|
print("缺文档:", ", ".join(missing_docs[:20]) + ("..." if len(missing_docs) > 20 else ""))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
strict = "--strict" in sys.argv
|
strict = "--strict" in sys.argv
|
||||||
|
if "--coverage" in sys.argv:
|
||||||
|
return coverage_report()
|
||||||
plugins = sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json"))
|
plugins = sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json"))
|
||||||
if not plugins:
|
if not plugins:
|
||||||
print("未找到任何 plugin.json", file=sys.stderr)
|
print("未找到任何 plugin.json", file=sys.stderr)
|
||||||
@@ -144,6 +195,11 @@ def main() -> int:
|
|||||||
for plugin_json in plugins:
|
for plugin_json in plugins:
|
||||||
check_plugin(plugin_json)
|
check_plugin(plugin_json)
|
||||||
|
|
||||||
|
catalog = ROOT / "catalog.json"
|
||||||
|
if catalog.is_file():
|
||||||
|
names = {x["name"] for x in json.loads(catalog.read_text()).get("skills", [])}
|
||||||
|
check_routing(names)
|
||||||
|
|
||||||
for warning in warnings:
|
for warning in warnings:
|
||||||
print(f"[warn] {warning}")
|
print(f"[warn] {warning}")
|
||||||
for error in errors:
|
for error in errors:
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ metadata:
|
|||||||
invocation: user-invoked
|
invocation: user-invoked
|
||||||
disable-model-invocation: true
|
disable-model-invocation: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 商务合同撰写技能 (biz-contract)
|
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本技能用于辅助商务合同的撰写与管理,支持多家公司主体、多种合同类型。
|
本技能用于辅助商务合同的撰写与管理,支持多家公司主体、多种合同类型。
|
||||||
@@ -634,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`) |
|
||||||
|
| 思源笔记 | 商务合同笔记本 /物流合同/{甲方简称}-{乙方简称}-{年份} |
|
||||||
@@ -5,9 +5,6 @@ metadata:
|
|||||||
invocation: user-invoked
|
invocation: user-invoked
|
||||||
disable-model-invocation: true
|
disable-model-invocation: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 商业计划书撰写技能 (biz-plan)
|
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本技能用于辅助商业计划书的撰写与优化,特别适用于 B2B SaaS、供应链平台、产业互联网等领域。
|
本技能用于辅助商业计划书的撰写与优化,特别适用于 B2B SaaS、供应链平台、产业互联网等领域。
|
||||||
@@ -403,361 +400,9 @@ disable-model-invocation: true
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## PPT 设计能力
|
|
||||||
|
|
||||||
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
|
## 详细内容
|
||||||
|
|
||||||
### PPT 输出方式
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
当用户要求生成 PPT 时,使用以下方式:
|
- PPT 设计能力
|
||||||
|
|
||||||
1. **HTML 演示文稿**(推荐)
|
|
||||||
- 生成单个 HTML 文件
|
|
||||||
- 支持全屏演示(按 F 键)
|
|
||||||
- 支持键盘翻页(← →)
|
|
||||||
- 可直接打印为 PDF
|
|
||||||
- 响应式设计,适配各种屏幕
|
|
||||||
|
|
||||||
2. **Markdown 演示脚本**
|
|
||||||
- 输出每页内容的文字稿
|
|
||||||
- 用户可自行复制到 PPT 软件
|
|
||||||
|
|
||||||
### HTML PPT 模板
|
|
||||||
|
|
||||||
生成 PPT 时使用以下 HTML 模板结构:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>商业计划书 - [公司名称]</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
||||||
background: #1a1a2e;
|
|
||||||
color: #fff;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide {
|
|
||||||
display: none;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
padding: 60px 80px;
|
|
||||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide.active {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 封面样式 */
|
|
||||||
.slide.cover {
|
|
||||||
text-align: center;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide.cover h1 {
|
|
||||||
font-size: 72px;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide.cover .slogan {
|
|
||||||
font-size: 28px;
|
|
||||||
color: #a0aec0;
|
|
||||||
margin-bottom: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide.cover .info {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #718096;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 内容页样式 */
|
|
||||||
.slide h2 {
|
|
||||||
font-size: 48px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide h3 {
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: #a0aec0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide p, .slide li {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 1.8;
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide ul {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide ul li {
|
|
||||||
padding: 12px 0;
|
|
||||||
padding-left: 40px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide ul li::before {
|
|
||||||
content: "▸";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 数据卡片 */
|
|
||||||
.metrics {
|
|
||||||
display: flex;
|
|
||||||
gap: 40px;
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-card {
|
|
||||||
flex: 1;
|
|
||||||
background: rgba(102, 126, 234, 0.1);
|
|
||||||
border: 1px solid rgba(102, 126, 234, 0.3);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 32px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-card .value {
|
|
||||||
font-size: 48px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-card .label {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #a0aec0;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 两栏布局 */
|
|
||||||
.two-columns {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 60px;
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 流程图 */
|
|
||||||
.flow {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 20px;
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flow-item {
|
|
||||||
background: rgba(102, 126, 234, 0.15);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 24px 32px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flow-arrow {
|
|
||||||
color: #667eea;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 页码 */
|
|
||||||
.page-number {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 30px;
|
|
||||||
right: 40px;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #4a5568;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 进度条 */
|
|
||||||
.progress {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 4px;
|
|
||||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
||||||
transition: width 0.3s;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 导航提示 */
|
|
||||||
.nav-hint {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
font-size: 14px;
|
|
||||||
color: #4a5568;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="progress" id="progress"></div>
|
|
||||||
|
|
||||||
<!-- Slide 1: 封面 -->
|
|
||||||
<div class="slide cover active">
|
|
||||||
<h1>[公司名称]</h1>
|
|
||||||
<p class="slogan">[一句话 Slogan]</p>
|
|
||||||
<p class="info">[日期] · 商业计划书</p>
|
|
||||||
<span class="page-number">1 / N</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Slide 2: 公司定位 -->
|
|
||||||
<div class="slide">
|
|
||||||
<h2>公司定位</h2>
|
|
||||||
<h3>[一句话定位]</h3>
|
|
||||||
<div class="metrics">
|
|
||||||
<div class="metric-card">
|
|
||||||
<div class="value">[X]年</div>
|
|
||||||
<div class="label">深耕行业</div>
|
|
||||||
</div>
|
|
||||||
<div class="metric-card">
|
|
||||||
<div class="value">[X]家</div>
|
|
||||||
<div class="label">头部客户</div>
|
|
||||||
</div>
|
|
||||||
<div class="metric-card">
|
|
||||||
<div class="value">[X]轮</div>
|
|
||||||
<div class="label">完成融资</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span class="page-number">2 / N</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 更多 slides... -->
|
|
||||||
|
|
||||||
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const slides = document.querySelectorAll('.slide');
|
|
||||||
const progress = document.getElementById('progress');
|
|
||||||
let current = 0;
|
|
||||||
|
|
||||||
function showSlide(n) {
|
|
||||||
slides[current].classList.remove('active');
|
|
||||||
current = (n + slides.length) % slides.length;
|
|
||||||
slides[current].classList.add('active');
|
|
||||||
progress.style.width = ((current + 1) / slides.length * 100) + '%';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
|
|
||||||
if (e.key === 'ArrowLeft') showSlide(current - 1);
|
|
||||||
if (e.key === 'f' || e.key === 'F') {
|
|
||||||
document.documentElement.requestFullscreen?.();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 触摸滑动支持
|
|
||||||
let touchStartX = 0;
|
|
||||||
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
|
|
||||||
document.addEventListener('touchend', e => {
|
|
||||||
const diff = e.changedTouches[0].clientX - touchStartX;
|
|
||||||
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
### BP PPT 标准页面结构
|
|
||||||
|
|
||||||
生成商业计划书 PPT 时,按以下结构组织:
|
|
||||||
|
|
||||||
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|
|
||||||
|------|----------|----------|----------|
|
|
||||||
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
|
|
||||||
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
|
|
||||||
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
|
|
||||||
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
|
|
||||||
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
|
|
||||||
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
|
|
||||||
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
|
|
||||||
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
|
|
||||||
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
|
|
||||||
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
|
|
||||||
| 11 | 里程碑 | 发展历程 | 时间线 |
|
|
||||||
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
|
|
||||||
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
|
|
||||||
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
|
|
||||||
|
|
||||||
### PPT 设计原则
|
|
||||||
|
|
||||||
1. **一页一重点**:每页只传达一个核心信息
|
|
||||||
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
|
|
||||||
3. **数据可视化**:用数字卡片、图表代替文字描述
|
|
||||||
4. **留白充足**:内容不要铺满,保持呼吸感
|
|
||||||
5. **配色统一**:使用 2-3 种主色,保持一致性
|
|
||||||
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
|
|
||||||
|
|
||||||
### 配色方案
|
|
||||||
|
|
||||||
**深色科技风**(推荐):
|
|
||||||
- 背景:#1a1a2e → #16213e(渐变)
|
|
||||||
- 主色:#667eea(蓝紫色)
|
|
||||||
- 辅助:#764ba2(紫色)
|
|
||||||
- 文字:#e2e8f0(浅灰)
|
|
||||||
- 次要文字:#a0aec0(中灰)
|
|
||||||
|
|
||||||
**浅色商务风**:
|
|
||||||
- 背景:#ffffff
|
|
||||||
- 主色:#2196F3(蓝色)
|
|
||||||
- 辅助:#1976D2(深蓝)
|
|
||||||
- 文字:#212121(深灰)
|
|
||||||
- 次要文字:#616161(中灰)
|
|
||||||
|
|
||||||
### 使用示例
|
|
||||||
|
|
||||||
用户请求:
|
|
||||||
> 帮我生成商业计划书 PPT
|
|
||||||
|
|
||||||
输出方式:
|
|
||||||
1. 生成完整的 HTML 文件
|
|
||||||
2. 保存到用户指定位置(如桌面、项目目录)
|
|
||||||
3. 用户用浏览器打开即可演示
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 打开 PPT
|
|
||||||
open ~/Desktop/bp-presentation.html
|
|
||||||
|
|
||||||
# 或在浏览器中打开后按 F 进入全屏模式
|
|
||||||
```
|
|
||||||
|
|
||||||
### 导出为 PDF
|
|
||||||
|
|
||||||
HTML PPT 可通过浏览器打印功能导出为 PDF:
|
|
||||||
1. 在浏览器中打开 HTML 文件
|
|
||||||
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
|
|
||||||
3. 选择"另存为 PDF"
|
|
||||||
4. 设置页面大小为横向 A4 或 16:9
|
|
||||||
|
|
||||||
### 注意事项
|
|
||||||
|
|
||||||
1. **内容优先**:先确定 BP 内容,再生成 PPT
|
|
||||||
2. **数据占位**:敏感数据可用 [X] 占位
|
|
||||||
3. **迭代优化**:可分页生成,逐步完善
|
|
||||||
4. **本地演示**:HTML 文件完全离线可用,无需网络
|
|
||||||
|
|||||||
@@ -0,0 +1,360 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## PPT 设计能力
|
||||||
|
|
||||||
|
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
|
||||||
|
|
||||||
|
### PPT 输出方式
|
||||||
|
|
||||||
|
当用户要求生成 PPT 时,使用以下方式:
|
||||||
|
|
||||||
|
1. **HTML 演示文稿**(推荐)
|
||||||
|
- 生成单个 HTML 文件
|
||||||
|
- 支持全屏演示(按 F 键)
|
||||||
|
- 支持键盘翻页(← →)
|
||||||
|
- 可直接打印为 PDF
|
||||||
|
- 响应式设计,适配各种屏幕
|
||||||
|
|
||||||
|
2. **Markdown 演示脚本**
|
||||||
|
- 输出每页内容的文字稿
|
||||||
|
- 用户可自行复制到 PPT 软件
|
||||||
|
|
||||||
|
### HTML PPT 模板
|
||||||
|
|
||||||
|
生成 PPT 时使用以下 HTML 模板结构:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>商业计划书 - [公司名称]</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||||
|
background: #1a1a2e;
|
||||||
|
color: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide {
|
||||||
|
display: none;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 60px 80px;
|
||||||
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide.active {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 封面样式 */
|
||||||
|
.slide.cover {
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide.cover h1 {
|
||||||
|
font-size: 72px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide.cover .slogan {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #a0aec0;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide.cover .info {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #718096;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内容页样式 */
|
||||||
|
.slide h2 {
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide h3 {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
color: #a0aec0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide p, .slide li {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide ul li {
|
||||||
|
padding: 12px 0;
|
||||||
|
padding-left: 40px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide ul li::before {
|
||||||
|
content: "▸";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 数据卡片 */
|
||||||
|
.metrics {
|
||||||
|
display: flex;
|
||||||
|
gap: 40px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card {
|
||||||
|
flex: 1;
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.3);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card .value {
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card .label {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #a0aec0;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 两栏布局 */
|
||||||
|
.two-columns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 60px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 流程图 */
|
||||||
|
.flow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-item {
|
||||||
|
background: rgba(102, 126, 234, 0.15);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-arrow {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页码 */
|
||||||
|
.page-number {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 40px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 进度条 */
|
||||||
|
.progress {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||||
|
transition: width 0.3s;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导航提示 */
|
||||||
|
.nav-hint {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
font-size: 14px;
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="progress" id="progress"></div>
|
||||||
|
|
||||||
|
<!-- Slide 1: 封面 -->
|
||||||
|
<div class="slide cover active">
|
||||||
|
<h1>[公司名称]</h1>
|
||||||
|
<p class="slogan">[一句话 Slogan]</p>
|
||||||
|
<p class="info">[日期] · 商业计划书</p>
|
||||||
|
<span class="page-number">1 / N</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Slide 2: 公司定位 -->
|
||||||
|
<div class="slide">
|
||||||
|
<h2>公司定位</h2>
|
||||||
|
<h3>[一句话定位]</h3>
|
||||||
|
<div class="metrics">
|
||||||
|
<div class="metric-card">
|
||||||
|
<div class="value">[X]年</div>
|
||||||
|
<div class="label">深耕行业</div>
|
||||||
|
</div>
|
||||||
|
<div class="metric-card">
|
||||||
|
<div class="value">[X]家</div>
|
||||||
|
<div class="label">头部客户</div>
|
||||||
|
</div>
|
||||||
|
<div class="metric-card">
|
||||||
|
<div class="value">[X]轮</div>
|
||||||
|
<div class="label">完成融资</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="page-number">2 / N</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 更多 slides... -->
|
||||||
|
|
||||||
|
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const slides = document.querySelectorAll('.slide');
|
||||||
|
const progress = document.getElementById('progress');
|
||||||
|
let current = 0;
|
||||||
|
|
||||||
|
function showSlide(n) {
|
||||||
|
slides[current].classList.remove('active');
|
||||||
|
current = (n + slides.length) % slides.length;
|
||||||
|
slides[current].classList.add('active');
|
||||||
|
progress.style.width = ((current + 1) / slides.length * 100) + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
|
||||||
|
if (e.key === 'ArrowLeft') showSlide(current - 1);
|
||||||
|
if (e.key === 'f' || e.key === 'F') {
|
||||||
|
document.documentElement.requestFullscreen?.();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 触摸滑动支持
|
||||||
|
let touchStartX = 0;
|
||||||
|
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
|
||||||
|
document.addEventListener('touchend', e => {
|
||||||
|
const diff = e.changedTouches[0].clientX - touchStartX;
|
||||||
|
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
### BP PPT 标准页面结构
|
||||||
|
|
||||||
|
生成商业计划书 PPT 时,按以下结构组织:
|
||||||
|
|
||||||
|
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|
||||||
|
|------|----------|----------|----------|
|
||||||
|
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
|
||||||
|
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
|
||||||
|
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
|
||||||
|
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
|
||||||
|
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
|
||||||
|
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
|
||||||
|
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
|
||||||
|
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
|
||||||
|
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
|
||||||
|
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
|
||||||
|
| 11 | 里程碑 | 发展历程 | 时间线 |
|
||||||
|
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
|
||||||
|
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
|
||||||
|
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
|
||||||
|
|
||||||
|
### PPT 设计原则
|
||||||
|
|
||||||
|
1. **一页一重点**:每页只传达一个核心信息
|
||||||
|
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
|
||||||
|
3. **数据可视化**:用数字卡片、图表代替文字描述
|
||||||
|
4. **留白充足**:内容不要铺满,保持呼吸感
|
||||||
|
5. **配色统一**:使用 2-3 种主色,保持一致性
|
||||||
|
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
|
||||||
|
|
||||||
|
### 配色方案
|
||||||
|
|
||||||
|
**深色科技风**(推荐):
|
||||||
|
- 背景:#1a1a2e → #16213e(渐变)
|
||||||
|
- 主色:#667eea(蓝紫色)
|
||||||
|
- 辅助:#764ba2(紫色)
|
||||||
|
- 文字:#e2e8f0(浅灰)
|
||||||
|
- 次要文字:#a0aec0(中灰)
|
||||||
|
|
||||||
|
**浅色商务风**:
|
||||||
|
- 背景:#ffffff
|
||||||
|
- 主色:#2196F3(蓝色)
|
||||||
|
- 辅助:#1976D2(深蓝)
|
||||||
|
- 文字:#212121(深灰)
|
||||||
|
- 次要文字:#616161(中灰)
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
|
||||||
|
用户请求:
|
||||||
|
> 帮我生成商业计划书 PPT
|
||||||
|
|
||||||
|
输出方式:
|
||||||
|
1. 生成完整的 HTML 文件
|
||||||
|
2. 保存到用户指定位置(如桌面、项目目录)
|
||||||
|
3. 用户用浏览器打开即可演示
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 打开 PPT
|
||||||
|
open ~/Desktop/bp-presentation.html
|
||||||
|
|
||||||
|
# 或在浏览器中打开后按 F 进入全屏模式
|
||||||
|
```
|
||||||
|
|
||||||
|
### 导出为 PDF
|
||||||
|
|
||||||
|
HTML PPT 可通过浏览器打印功能导出为 PDF:
|
||||||
|
1. 在浏览器中打开 HTML 文件
|
||||||
|
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
|
||||||
|
3. 选择"另存为 PDF"
|
||||||
|
4. 设置页面大小为横向 A4 或 16:9
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
|
||||||
|
1. **内容优先**:先确定 BP 内容,再生成 PPT
|
||||||
|
2. **数据占位**:敏感数据可用 [X] 占位
|
||||||
|
3. **迭代优化**:可分页生成,逐步完善
|
||||||
|
4. **本地演示**:HTML 文件完全离线可用,无需网络
|
||||||
@@ -5,9 +5,6 @@ metadata:
|
|||||||
invocation: user-invoked
|
invocation: user-invoked
|
||||||
disable-model-invocation: true
|
disable-model-invocation: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 财务对账技能
|
|
||||||
|
|
||||||
## 功能概述
|
## 功能概述
|
||||||
|
|
||||||
- **银行对账**: 银行流水 vs 系统收付款记录匹配
|
- **银行对账**: 银行流水 vs 系统收付款记录匹配
|
||||||
@@ -481,457 +478,6 @@ def export_reconciliation_to_excel(
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 常见对账场景
|
|
||||||
|
|
||||||
### 场景1: 月末银行对账
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 1. 加载数据
|
|
||||||
bank_df = load_bank_statement('银行流水_202601.xlsx')
|
|
||||||
system_df = load_system_records('收付款记录_202601.xlsx')
|
|
||||||
|
|
||||||
# 2. 执行对账
|
|
||||||
result = reconcile(bank_df, system_df)
|
|
||||||
|
|
||||||
# 3. 查看汇总
|
|
||||||
print(f"已匹配: {result['summary']['matched_count']} 笔")
|
|
||||||
print(f"银行未达: {result['summary']['bank_only_count']} 笔, 金额 {result['summary']['bank_only_amount']:,.2f}")
|
|
||||||
print(f"企业未达: {result['summary']['system_only_count']} 笔, 金额 {result['summary']['system_only_amount']:,.2f}")
|
|
||||||
|
|
||||||
# 4. 生成调节表
|
|
||||||
report = generate_bank_reconciliation_report(
|
|
||||||
bank_balance=150000.00, # 银行对账单余额
|
|
||||||
book_balance=145000.00, # 企业账面余额
|
|
||||||
bank_only=result['bank_only'],
|
|
||||||
system_only=result['system_only'],
|
|
||||||
as_of_date='2026-01-31'
|
|
||||||
)
|
|
||||||
print(report)
|
|
||||||
|
|
||||||
# 5. 导出 Excel
|
|
||||||
export_reconciliation_to_excel(result, '银行对账_202601.xlsx', '2026-01-31')
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景2: 客户对账
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 加载客户交易明细
|
|
||||||
ar_transactions = pd.read_excel('应收明细.xlsx')
|
|
||||||
|
|
||||||
# 筛选特定客户
|
|
||||||
customer_data = ar_transactions[ar_transactions['customer'] == '深圳XX公司']
|
|
||||||
|
|
||||||
# 生成对账单
|
|
||||||
statement = generate_ar_statement(
|
|
||||||
customer_name='深圳XX公司',
|
|
||||||
transactions=customer_data,
|
|
||||||
as_of_date='2026-01-31'
|
|
||||||
)
|
|
||||||
print(statement)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景3: 账龄分析
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 加载未收款数据
|
|
||||||
receivables = pd.read_excel('应收账款.xlsx')
|
|
||||||
|
|
||||||
# 账龄分析
|
|
||||||
aging = aging_analysis(
|
|
||||||
receivables=receivables,
|
|
||||||
as_of_date='2026-01-31',
|
|
||||||
aging_buckets=[30, 60, 90, 180, 365]
|
|
||||||
)
|
|
||||||
print(aging)
|
|
||||||
|
|
||||||
# 导出
|
|
||||||
aging.to_excel('账龄分析_202601.xlsx')
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景4: 扫描单据OCR识别与对账匹配(诉讼证据核对)
|
|
||||||
|
|
||||||
适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。
|
|
||||||
|
|
||||||
```python
|
|
||||||
from pdf2image import convert_from_path
|
|
||||||
import pytesseract
|
|
||||||
import pandas as pd
|
|
||||||
import re
|
|
||||||
|
|
||||||
def ocr_pdf_extract_orders(pdf_path: str, dpi: int = 150) -> pd.DataFrame:
|
|
||||||
"""
|
|
||||||
从 PDF 扫描件中 OCR 提取订单信息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
pdf_path: PDF 文件路径
|
|
||||||
dpi: 图像分辨率,越高识别越准但速度越慢
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
DataFrame 包含 order_no, page, tc_no 等字段
|
|
||||||
"""
|
|
||||||
print(f"正在转换 PDF...")
|
|
||||||
images = convert_from_path(pdf_path, dpi=dpi)
|
|
||||||
print(f"共 {len(images)} 页,开始 OCR 识别...")
|
|
||||||
|
|
||||||
all_records = []
|
|
||||||
for i, img in enumerate(images, 1):
|
|
||||||
if i % 10 == 0:
|
|
||||||
print(f" 处理进度: {i}/{len(images)}")
|
|
||||||
|
|
||||||
text = pytesseract.image_to_string(img, lang='chi_sim+eng')
|
|
||||||
|
|
||||||
# 提取多种订单号格式(根据实际业务调整)
|
|
||||||
hm_orders = re.findall(r'(HM\d{12,15})(?:-\d+)?', text) # 华住订单
|
|
||||||
hpo_orders = re.findall(r'(HPO-\d{8}-\d+)', text) # 华住采购订单
|
|
||||||
fy_orders = re.findall(r'(FY\d{9,})', text) # 凤悦订单
|
|
||||||
phgco_orders = re.findall(r'(PHGCO\d{9,})', text) # 锦江订单
|
|
||||||
tc_orders = re.findall(r'(TC\d{8,})', text) # 运单号
|
|
||||||
|
|
||||||
# 提取日期
|
|
||||||
dates = re.findall(r'(\d{4}-\d{2}-\d{2})', text)
|
|
||||||
|
|
||||||
all_orders = hm_orders + hpo_orders + fy_orders + phgco_orders
|
|
||||||
for order in all_orders:
|
|
||||||
all_records.append({
|
|
||||||
'order_no': order,
|
|
||||||
'page': i,
|
|
||||||
'tc_no': tc_orders[0] if tc_orders else None,
|
|
||||||
'date': dates[0] if dates else None,
|
|
||||||
'raw_text': text[:500] # 保留部分原文用于核查
|
|
||||||
})
|
|
||||||
|
|
||||||
df = pd.DataFrame(all_records)
|
|
||||||
df = df.drop_duplicates(subset=['order_no'])
|
|
||||||
print(f"提取完成,共 {len(df)} 条唯一订单")
|
|
||||||
return df
|
|
||||||
|
|
||||||
|
|
||||||
def extract_base_order_no(order_no: str) -> str:
|
|
||||||
"""
|
|
||||||
提取基础订单号(移除后缀如 -1, -2)
|
|
||||||
|
|
||||||
例如: HM202505227320-1 -> HM202505227320
|
|
||||||
"""
|
|
||||||
if pd.isna(order_no):
|
|
||||||
return None
|
|
||||||
order_str = str(order_no)
|
|
||||||
match = re.match(r'(HM\d{12,15}|HPO-\d{8}-\d+|FY\d{9,}|PHGCO\d{9,})', order_str)
|
|
||||||
return match.group(1) if match else order_str
|
|
||||||
|
|
||||||
|
|
||||||
def reconcile_scanned_with_excel(
|
|
||||||
scanned_df: pd.DataFrame,
|
|
||||||
excel_path: str,
|
|
||||||
main_sheet: str,
|
|
||||||
pending_sheet: str = None,
|
|
||||||
order_column: str = '华住/锦江订单号'
|
|
||||||
) -> dict:
|
|
||||||
"""
|
|
||||||
扫描单据与 Excel 对账单匹配
|
|
||||||
|
|
||||||
Args:
|
|
||||||
scanned_df: OCR 提取的订单 DataFrame
|
|
||||||
excel_path: Excel 对账单路径
|
|
||||||
main_sheet: 主数据 Sheet 名称
|
|
||||||
pending_sheet: 待结算/次月结 Sheet 名称(可选)
|
|
||||||
order_column: 订单号列名
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
{
|
|
||||||
'matched_main': 在主表中匹配的订单,
|
|
||||||
'matched_pending': 在待结算表中匹配的订单,
|
|
||||||
'not_found': 未找到的订单,
|
|
||||||
'summary': 汇总统计
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
# 加载主表
|
|
||||||
df_main = pd.read_excel(excel_path, sheet_name=main_sheet)
|
|
||||||
# 处理表头(如果第一行是表头)
|
|
||||||
if df_main.iloc[0].astype(str).str.contains(order_column).any():
|
|
||||||
df_main.columns = df_main.iloc[0]
|
|
||||||
df_main = df_main.iloc[1:].reset_index(drop=True)
|
|
||||||
|
|
||||||
# 提取基础订单号
|
|
||||||
main_orders = set(df_main[order_column].apply(extract_base_order_no).dropna())
|
|
||||||
|
|
||||||
# 加载待结算表(如有)
|
|
||||||
pending_orders = set()
|
|
||||||
df_pending = None
|
|
||||||
if pending_sheet:
|
|
||||||
try:
|
|
||||||
df_pending = pd.read_excel(excel_path, sheet_name=pending_sheet)
|
|
||||||
pending_orders = set(df_pending[order_column].apply(extract_base_order_no).dropna())
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
all_excel_orders = main_orders | pending_orders
|
|
||||||
|
|
||||||
# 扫描件订单号
|
|
||||||
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
|
||||||
|
|
||||||
# 匹配分析
|
|
||||||
matched_main = scan_orders & main_orders
|
|
||||||
matched_pending = scan_orders & pending_orders
|
|
||||||
not_found = scan_orders - all_excel_orders
|
|
||||||
|
|
||||||
# 汇总
|
|
||||||
summary = {
|
|
||||||
'scan_total': len(scan_orders),
|
|
||||||
'matched_main_count': len(matched_main),
|
|
||||||
'matched_pending_count': len(matched_pending),
|
|
||||||
'not_found_count': len(not_found),
|
|
||||||
'match_rate': (len(matched_main) + len(matched_pending)) / len(scan_orders) * 100 if scan_orders else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'matched_main': matched_main,
|
|
||||||
'matched_pending': matched_pending,
|
|
||||||
'not_found': not_found,
|
|
||||||
'summary': summary,
|
|
||||||
'df_main': df_main,
|
|
||||||
'df_pending': df_pending
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def generate_litigation_reconciliation_report(
|
|
||||||
result: dict,
|
|
||||||
scanned_df: pd.DataFrame,
|
|
||||||
output_path: str
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
生成诉讼对账分析报告
|
|
||||||
"""
|
|
||||||
summary = result['summary']
|
|
||||||
|
|
||||||
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
|
|
||||||
# Sheet1: 汇总
|
|
||||||
summary_df = pd.DataFrame({
|
|
||||||
'项目': [
|
|
||||||
'扫描件订单数',
|
|
||||||
'在主对账单中找到',
|
|
||||||
'在待结算表中找到',
|
|
||||||
'完全未找到',
|
|
||||||
'匹配率'
|
|
||||||
],
|
|
||||||
'数值': [
|
|
||||||
summary['scan_total'],
|
|
||||||
summary['matched_main_count'],
|
|
||||||
summary['matched_pending_count'],
|
|
||||||
summary['not_found_count'],
|
|
||||||
f"{summary['match_rate']:.1f}%"
|
|
||||||
]
|
|
||||||
})
|
|
||||||
summary_df.to_excel(writer, sheet_name='对账汇总', index=False)
|
|
||||||
|
|
||||||
# Sheet2: 未找到的订单
|
|
||||||
if result['not_found']:
|
|
||||||
not_found_df = scanned_df[
|
|
||||||
scanned_df['order_no'].apply(extract_base_order_no).isin(result['not_found'])
|
|
||||||
][['order_no', 'page', 'tc_no', 'date']]
|
|
||||||
not_found_df.columns = ['订单号', 'PDF页码', '运单号', '日期']
|
|
||||||
not_found_df.to_excel(writer, sheet_name='未找到订单', index=False)
|
|
||||||
|
|
||||||
# Sheet3: 待结算订单详情
|
|
||||||
if result['matched_pending'] and result['df_pending'] is not None:
|
|
||||||
pending_detail = result['df_pending'][
|
|
||||||
result['df_pending']['华住/锦江订单号'].apply(extract_base_order_no).isin(result['matched_pending'])
|
|
||||||
]
|
|
||||||
pending_detail.to_excel(writer, sheet_name='待结算订单', index=False)
|
|
||||||
|
|
||||||
print(f"报告已保存: {output_path}")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 使用示例
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 1. 从 PDF 扫描件提取订单
|
|
||||||
pdf_path = "承运商证据_6月业务.pdf"
|
|
||||||
scanned_df = ocr_pdf_extract_orders(pdf_path, dpi=150)
|
|
||||||
|
|
||||||
# 2. 与 Excel 对账单匹配
|
|
||||||
result = reconcile_scanned_with_excel(
|
|
||||||
scanned_df=scanned_df,
|
|
||||||
excel_path="对账单_2025年6月.xlsx",
|
|
||||||
main_sheet="2025.6",
|
|
||||||
pending_sheet="次月结",
|
|
||||||
order_column="华住/锦江订单号"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 3. 查看匹配结果
|
|
||||||
print(f"匹配率: {result['summary']['match_rate']:.1f}%")
|
|
||||||
print(f"未找到: {result['summary']['not_found_count']} 条")
|
|
||||||
|
|
||||||
if result['not_found']:
|
|
||||||
print("\n未找到的订单:")
|
|
||||||
for order in result['not_found']:
|
|
||||||
print(f" {order}")
|
|
||||||
|
|
||||||
# 4. 生成报告
|
|
||||||
generate_litigation_reconciliation_report(
|
|
||||||
result=result,
|
|
||||||
scanned_df=scanned_df,
|
|
||||||
output_path="诉讼对账分析报告.xlsx"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### OCR 识别注意事项
|
|
||||||
|
|
||||||
1. **环境依赖**:
|
|
||||||
```bash
|
|
||||||
# macOS
|
|
||||||
brew install tesseract tesseract-lang poppler
|
|
||||||
pip install pdf2image pytesseract pandas openpyxl
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **识别精度**:
|
|
||||||
- DPI 150 适合快速扫描,DPI 200-250 适合精确识别
|
|
||||||
- 中文识别需要 `chi_sim` 语言包
|
|
||||||
|
|
||||||
3. **订单号格式差异**:
|
|
||||||
- PDF 中可能是 `HM202505227320`
|
|
||||||
- Excel 中可能是 `HM202505227320-1`、`HM202505227320-2`(带后缀)
|
|
||||||
- 匹配时应使用基础订单号
|
|
||||||
|
|
||||||
4. **常见问题**:
|
|
||||||
- 空白页(签收单背面)会被自动跳过
|
|
||||||
- OCR 可能误识别字符(如 5→S, 0→O),建议人工抽查
|
|
||||||
- 大 PDF 处理较慢,可先用低 DPI 预览
|
|
||||||
|
|
||||||
5. **"次月结"/"待结算" 订单**:
|
|
||||||
- 这类订单通常标记为"没有报价"、"未建单"等
|
|
||||||
- 虽然在系统中有记录,但可能未计入正式结算金额
|
|
||||||
- 诉讼中需特别关注这类订单的状态
|
|
||||||
|
|
||||||
6. **多承运商订单归属校验(重要风险点)**:
|
|
||||||
- 承运商可能将其他公司的订单混入证据,企图多收运费
|
|
||||||
- 必须检查PDF订单是否出现在其他承运商的对账单中
|
|
||||||
- 如果订单同时出现在多个承运商对账单中,则为异常订单
|
|
||||||
|
|
||||||
#### 多承运商订单归属校验
|
|
||||||
|
|
||||||
```python
|
|
||||||
def check_order_ownership(
|
|
||||||
scanned_df: pd.DataFrame,
|
|
||||||
my_company_excel: str,
|
|
||||||
other_carrier_excels: list[str],
|
|
||||||
order_column: str = '华住/锦江订单号'
|
|
||||||
) -> dict:
|
|
||||||
"""
|
|
||||||
检查订单归属,识别是否有其他承运商的订单被混入
|
|
||||||
|
|
||||||
Args:
|
|
||||||
scanned_df: OCR 提取的订单 DataFrame
|
|
||||||
my_company_excel: 我司对账单路径
|
|
||||||
other_carrier_excels: 其他承运商对账单路径列表
|
|
||||||
order_column: 订单号列名
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
{
|
|
||||||
'my_orders': 属于我司的订单,
|
|
||||||
'other_carrier_orders': 属于其他承运商的订单(异常),
|
|
||||||
'disputed_orders': 同时出现在多处的争议订单,
|
|
||||||
'summary': 汇总
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
# 提取PDF订单
|
|
||||||
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
|
||||||
|
|
||||||
# 加载我司对账单订单
|
|
||||||
my_orders = set()
|
|
||||||
try:
|
|
||||||
xl = pd.ExcelFile(my_company_excel)
|
|
||||||
for sheet in xl.sheet_names:
|
|
||||||
df = pd.read_excel(my_company_excel, sheet_name=sheet)
|
|
||||||
for col in df.columns:
|
|
||||||
if '订单' in str(col):
|
|
||||||
orders = df[col].apply(extract_base_order_no).dropna()
|
|
||||||
my_orders.update(orders)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"加载我司对账单失败: {e}")
|
|
||||||
|
|
||||||
# 加载其他承运商对账单订单
|
|
||||||
other_orders = {} # {carrier_name: set of orders}
|
|
||||||
for excel_path in other_carrier_excels:
|
|
||||||
carrier_name = excel_path.split('/')[-1]
|
|
||||||
carrier_orders = set()
|
|
||||||
try:
|
|
||||||
xl = pd.ExcelFile(excel_path)
|
|
||||||
for sheet in xl.sheet_names:
|
|
||||||
df = pd.read_excel(excel_path, sheet_name=sheet)
|
|
||||||
for col in df.columns:
|
|
||||||
if '订单' in str(col):
|
|
||||||
orders = df[col].apply(extract_base_order_no).dropna()
|
|
||||||
carrier_orders.update(orders)
|
|
||||||
other_orders[carrier_name] = carrier_orders
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 分析订单归属
|
|
||||||
all_other_orders = set()
|
|
||||||
for orders in other_orders.values():
|
|
||||||
all_other_orders.update(orders)
|
|
||||||
|
|
||||||
# 分类
|
|
||||||
only_mine = scan_orders & my_orders - all_other_orders # 仅在我司
|
|
||||||
only_others = scan_orders & all_other_orders - my_orders # 仅在其他承运商(异常!)
|
|
||||||
in_both = scan_orders & my_orders & all_other_orders # 同时出现(争议)
|
|
||||||
nowhere = scan_orders - my_orders - all_other_orders # 哪里都没有
|
|
||||||
|
|
||||||
# 识别异常订单属于哪个承运商
|
|
||||||
abnormal_details = []
|
|
||||||
for order in only_others:
|
|
||||||
for carrier, orders in other_orders.items():
|
|
||||||
if order in orders:
|
|
||||||
page = scanned_df[scanned_df['order_no'].apply(extract_base_order_no) == order]['page'].values
|
|
||||||
abnormal_details.append({
|
|
||||||
'order_no': order,
|
|
||||||
'belongs_to': carrier,
|
|
||||||
'pdf_page': page[0] if len(page) > 0 else None
|
|
||||||
})
|
|
||||||
|
|
||||||
summary = {
|
|
||||||
'scan_total': len(scan_orders),
|
|
||||||
'my_orders_count': len(only_mine),
|
|
||||||
'other_carrier_count': len(only_others), # 异常订单数
|
|
||||||
'disputed_count': len(in_both),
|
|
||||||
'not_found_count': len(nowhere)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'my_orders': only_mine,
|
|
||||||
'other_carrier_orders': only_others,
|
|
||||||
'abnormal_details': abnormal_details,
|
|
||||||
'disputed_orders': in_both,
|
|
||||||
'not_found': nowhere,
|
|
||||||
'summary': summary
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 使用示例(多承运商校验)
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 检查订单归属
|
|
||||||
result = check_order_ownership(
|
|
||||||
scanned_df=scanned_df,
|
|
||||||
my_company_excel="智慧云彩对账单_2025年6月.xlsx",
|
|
||||||
other_carrier_excels=[
|
|
||||||
"友利速运对账单_2025年6月.xlsx",
|
|
||||||
"其他承运商对账单.xlsx"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
# 检查是否有异常订单
|
|
||||||
if result['summary']['other_carrier_count'] > 0:
|
|
||||||
print(f"⚠️ 发现 {result['summary']['other_carrier_count']} 个异常订单!")
|
|
||||||
print("这些订单属于其他承运商,不应由我司支付:")
|
|
||||||
for item in result['abnormal_details']:
|
|
||||||
print(f" {item['order_no']} -> 属于 {item['belongs_to']} (PDF第{item['pdf_page']}页)")
|
|
||||||
else:
|
|
||||||
print("✓ 未发现异常订单,所有订单归属正确")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **数据格式统一**: 确保日期、金额格式一致后再对账
|
1. **数据格式统一**: 确保日期、金额格式一致后再对账
|
||||||
@@ -949,3 +495,9 @@ else:
|
|||||||
- **data-excel**: 处理复杂的 Excel 数据转换
|
- **data-excel**: 处理复杂的 Excel 数据转换
|
||||||
- **feishu**: 将对账报告保存到飞书云文档
|
- **feishu**: 将对账报告保存到飞书云文档
|
||||||
- **siyuan**: 记录对账工作日志到思源笔记
|
- **siyuan**: 记录对账工作日志到思源笔记
|
||||||
|
|
||||||
|
## 详细内容
|
||||||
|
|
||||||
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
|
- 常见对账场景
|
||||||
|
|||||||
@@ -0,0 +1,453 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 常见对账场景
|
||||||
|
|
||||||
|
### 场景1: 月末银行对账
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 1. 加载数据
|
||||||
|
bank_df = load_bank_statement('银行流水_202601.xlsx')
|
||||||
|
system_df = load_system_records('收付款记录_202601.xlsx')
|
||||||
|
|
||||||
|
# 2. 执行对账
|
||||||
|
result = reconcile(bank_df, system_df)
|
||||||
|
|
||||||
|
# 3. 查看汇总
|
||||||
|
print(f"已匹配: {result['summary']['matched_count']} 笔")
|
||||||
|
print(f"银行未达: {result['summary']['bank_only_count']} 笔, 金额 {result['summary']['bank_only_amount']:,.2f}")
|
||||||
|
print(f"企业未达: {result['summary']['system_only_count']} 笔, 金额 {result['summary']['system_only_amount']:,.2f}")
|
||||||
|
|
||||||
|
# 4. 生成调节表
|
||||||
|
report = generate_bank_reconciliation_report(
|
||||||
|
bank_balance=150000.00, # 银行对账单余额
|
||||||
|
book_balance=145000.00, # 企业账面余额
|
||||||
|
bank_only=result['bank_only'],
|
||||||
|
system_only=result['system_only'],
|
||||||
|
as_of_date='2026-01-31'
|
||||||
|
)
|
||||||
|
print(report)
|
||||||
|
|
||||||
|
# 5. 导出 Excel
|
||||||
|
export_reconciliation_to_excel(result, '银行对账_202601.xlsx', '2026-01-31')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景2: 客户对账
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 加载客户交易明细
|
||||||
|
ar_transactions = pd.read_excel('应收明细.xlsx')
|
||||||
|
|
||||||
|
# 筛选特定客户
|
||||||
|
customer_data = ar_transactions[ar_transactions['customer'] == '深圳XX公司']
|
||||||
|
|
||||||
|
# 生成对账单
|
||||||
|
statement = generate_ar_statement(
|
||||||
|
customer_name='深圳XX公司',
|
||||||
|
transactions=customer_data,
|
||||||
|
as_of_date='2026-01-31'
|
||||||
|
)
|
||||||
|
print(statement)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景3: 账龄分析
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 加载未收款数据
|
||||||
|
receivables = pd.read_excel('应收账款.xlsx')
|
||||||
|
|
||||||
|
# 账龄分析
|
||||||
|
aging = aging_analysis(
|
||||||
|
receivables=receivables,
|
||||||
|
as_of_date='2026-01-31',
|
||||||
|
aging_buckets=[30, 60, 90, 180, 365]
|
||||||
|
)
|
||||||
|
print(aging)
|
||||||
|
|
||||||
|
# 导出
|
||||||
|
aging.to_excel('账龄分析_202601.xlsx')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景4: 扫描单据OCR识别与对账匹配(诉讼证据核对)
|
||||||
|
|
||||||
|
适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。
|
||||||
|
|
||||||
|
```python
|
||||||
|
from pdf2image import convert_from_path
|
||||||
|
import pytesseract
|
||||||
|
import pandas as pd
|
||||||
|
import re
|
||||||
|
|
||||||
|
def ocr_pdf_extract_orders(pdf_path: str, dpi: int = 150) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
从 PDF 扫描件中 OCR 提取订单信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pdf_path: PDF 文件路径
|
||||||
|
dpi: 图像分辨率,越高识别越准但速度越慢
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DataFrame 包含 order_no, page, tc_no 等字段
|
||||||
|
"""
|
||||||
|
print(f"正在转换 PDF...")
|
||||||
|
images = convert_from_path(pdf_path, dpi=dpi)
|
||||||
|
print(f"共 {len(images)} 页,开始 OCR 识别...")
|
||||||
|
|
||||||
|
all_records = []
|
||||||
|
for i, img in enumerate(images, 1):
|
||||||
|
if i % 10 == 0:
|
||||||
|
print(f" 处理进度: {i}/{len(images)}")
|
||||||
|
|
||||||
|
text = pytesseract.image_to_string(img, lang='chi_sim+eng')
|
||||||
|
|
||||||
|
# 提取多种订单号格式(根据实际业务调整)
|
||||||
|
hm_orders = re.findall(r'(HM\d{12,15})(?:-\d+)?', text) # 华住订单
|
||||||
|
hpo_orders = re.findall(r'(HPO-\d{8}-\d+)', text) # 华住采购订单
|
||||||
|
fy_orders = re.findall(r'(FY\d{9,})', text) # 凤悦订单
|
||||||
|
phgco_orders = re.findall(r'(PHGCO\d{9,})', text) # 锦江订单
|
||||||
|
tc_orders = re.findall(r'(TC\d{8,})', text) # 运单号
|
||||||
|
|
||||||
|
# 提取日期
|
||||||
|
dates = re.findall(r'(\d{4}-\d{2}-\d{2})', text)
|
||||||
|
|
||||||
|
all_orders = hm_orders + hpo_orders + fy_orders + phgco_orders
|
||||||
|
for order in all_orders:
|
||||||
|
all_records.append({
|
||||||
|
'order_no': order,
|
||||||
|
'page': i,
|
||||||
|
'tc_no': tc_orders[0] if tc_orders else None,
|
||||||
|
'date': dates[0] if dates else None,
|
||||||
|
'raw_text': text[:500] # 保留部分原文用于核查
|
||||||
|
})
|
||||||
|
|
||||||
|
df = pd.DataFrame(all_records)
|
||||||
|
df = df.drop_duplicates(subset=['order_no'])
|
||||||
|
print(f"提取完成,共 {len(df)} 条唯一订单")
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def extract_base_order_no(order_no: str) -> str:
|
||||||
|
"""
|
||||||
|
提取基础订单号(移除后缀如 -1, -2)
|
||||||
|
|
||||||
|
例如: HM202505227320-1 -> HM202505227320
|
||||||
|
"""
|
||||||
|
if pd.isna(order_no):
|
||||||
|
return None
|
||||||
|
order_str = str(order_no)
|
||||||
|
match = re.match(r'(HM\d{12,15}|HPO-\d{8}-\d+|FY\d{9,}|PHGCO\d{9,})', order_str)
|
||||||
|
return match.group(1) if match else order_str
|
||||||
|
|
||||||
|
|
||||||
|
def reconcile_scanned_with_excel(
|
||||||
|
scanned_df: pd.DataFrame,
|
||||||
|
excel_path: str,
|
||||||
|
main_sheet: str,
|
||||||
|
pending_sheet: str = None,
|
||||||
|
order_column: str = '华住/锦江订单号'
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
扫描单据与 Excel 对账单匹配
|
||||||
|
|
||||||
|
Args:
|
||||||
|
scanned_df: OCR 提取的订单 DataFrame
|
||||||
|
excel_path: Excel 对账单路径
|
||||||
|
main_sheet: 主数据 Sheet 名称
|
||||||
|
pending_sheet: 待结算/次月结 Sheet 名称(可选)
|
||||||
|
order_column: 订单号列名
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{
|
||||||
|
'matched_main': 在主表中匹配的订单,
|
||||||
|
'matched_pending': 在待结算表中匹配的订单,
|
||||||
|
'not_found': 未找到的订单,
|
||||||
|
'summary': 汇总统计
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# 加载主表
|
||||||
|
df_main = pd.read_excel(excel_path, sheet_name=main_sheet)
|
||||||
|
# 处理表头(如果第一行是表头)
|
||||||
|
if df_main.iloc[0].astype(str).str.contains(order_column).any():
|
||||||
|
df_main.columns = df_main.iloc[0]
|
||||||
|
df_main = df_main.iloc[1:].reset_index(drop=True)
|
||||||
|
|
||||||
|
# 提取基础订单号
|
||||||
|
main_orders = set(df_main[order_column].apply(extract_base_order_no).dropna())
|
||||||
|
|
||||||
|
# 加载待结算表(如有)
|
||||||
|
pending_orders = set()
|
||||||
|
df_pending = None
|
||||||
|
if pending_sheet:
|
||||||
|
try:
|
||||||
|
df_pending = pd.read_excel(excel_path, sheet_name=pending_sheet)
|
||||||
|
pending_orders = set(df_pending[order_column].apply(extract_base_order_no).dropna())
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
all_excel_orders = main_orders | pending_orders
|
||||||
|
|
||||||
|
# 扫描件订单号
|
||||||
|
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
||||||
|
|
||||||
|
# 匹配分析
|
||||||
|
matched_main = scan_orders & main_orders
|
||||||
|
matched_pending = scan_orders & pending_orders
|
||||||
|
not_found = scan_orders - all_excel_orders
|
||||||
|
|
||||||
|
# 汇总
|
||||||
|
summary = {
|
||||||
|
'scan_total': len(scan_orders),
|
||||||
|
'matched_main_count': len(matched_main),
|
||||||
|
'matched_pending_count': len(matched_pending),
|
||||||
|
'not_found_count': len(not_found),
|
||||||
|
'match_rate': (len(matched_main) + len(matched_pending)) / len(scan_orders) * 100 if scan_orders else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'matched_main': matched_main,
|
||||||
|
'matched_pending': matched_pending,
|
||||||
|
'not_found': not_found,
|
||||||
|
'summary': summary,
|
||||||
|
'df_main': df_main,
|
||||||
|
'df_pending': df_pending
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_litigation_reconciliation_report(
|
||||||
|
result: dict,
|
||||||
|
scanned_df: pd.DataFrame,
|
||||||
|
output_path: str
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
生成诉讼对账分析报告
|
||||||
|
"""
|
||||||
|
summary = result['summary']
|
||||||
|
|
||||||
|
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
|
||||||
|
# Sheet1: 汇总
|
||||||
|
summary_df = pd.DataFrame({
|
||||||
|
'项目': [
|
||||||
|
'扫描件订单数',
|
||||||
|
'在主对账单中找到',
|
||||||
|
'在待结算表中找到',
|
||||||
|
'完全未找到',
|
||||||
|
'匹配率'
|
||||||
|
],
|
||||||
|
'数值': [
|
||||||
|
summary['scan_total'],
|
||||||
|
summary['matched_main_count'],
|
||||||
|
summary['matched_pending_count'],
|
||||||
|
summary['not_found_count'],
|
||||||
|
f"{summary['match_rate']:.1f}%"
|
||||||
|
]
|
||||||
|
})
|
||||||
|
summary_df.to_excel(writer, sheet_name='对账汇总', index=False)
|
||||||
|
|
||||||
|
# Sheet2: 未找到的订单
|
||||||
|
if result['not_found']:
|
||||||
|
not_found_df = scanned_df[
|
||||||
|
scanned_df['order_no'].apply(extract_base_order_no).isin(result['not_found'])
|
||||||
|
][['order_no', 'page', 'tc_no', 'date']]
|
||||||
|
not_found_df.columns = ['订单号', 'PDF页码', '运单号', '日期']
|
||||||
|
not_found_df.to_excel(writer, sheet_name='未找到订单', index=False)
|
||||||
|
|
||||||
|
# Sheet3: 待结算订单详情
|
||||||
|
if result['matched_pending'] and result['df_pending'] is not None:
|
||||||
|
pending_detail = result['df_pending'][
|
||||||
|
result['df_pending']['华住/锦江订单号'].apply(extract_base_order_no).isin(result['matched_pending'])
|
||||||
|
]
|
||||||
|
pending_detail.to_excel(writer, sheet_name='待结算订单', index=False)
|
||||||
|
|
||||||
|
print(f"报告已保存: {output_path}")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 1. 从 PDF 扫描件提取订单
|
||||||
|
pdf_path = "承运商证据_6月业务.pdf"
|
||||||
|
scanned_df = ocr_pdf_extract_orders(pdf_path, dpi=150)
|
||||||
|
|
||||||
|
# 2. 与 Excel 对账单匹配
|
||||||
|
result = reconcile_scanned_with_excel(
|
||||||
|
scanned_df=scanned_df,
|
||||||
|
excel_path="对账单_2025年6月.xlsx",
|
||||||
|
main_sheet="2025.6",
|
||||||
|
pending_sheet="次月结",
|
||||||
|
order_column="华住/锦江订单号"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. 查看匹配结果
|
||||||
|
print(f"匹配率: {result['summary']['match_rate']:.1f}%")
|
||||||
|
print(f"未找到: {result['summary']['not_found_count']} 条")
|
||||||
|
|
||||||
|
if result['not_found']:
|
||||||
|
print("\n未找到的订单:")
|
||||||
|
for order in result['not_found']:
|
||||||
|
print(f" {order}")
|
||||||
|
|
||||||
|
# 4. 生成报告
|
||||||
|
generate_litigation_reconciliation_report(
|
||||||
|
result=result,
|
||||||
|
scanned_df=scanned_df,
|
||||||
|
output_path="诉讼对账分析报告.xlsx"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### OCR 识别注意事项
|
||||||
|
|
||||||
|
1. **环境依赖**:
|
||||||
|
```bash
|
||||||
|
# macOS
|
||||||
|
brew install tesseract tesseract-lang poppler
|
||||||
|
pip install pdf2image pytesseract pandas openpyxl
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **识别精度**:
|
||||||
|
- DPI 150 适合快速扫描,DPI 200-250 适合精确识别
|
||||||
|
- 中文识别需要 `chi_sim` 语言包
|
||||||
|
|
||||||
|
3. **订单号格式差异**:
|
||||||
|
- PDF 中可能是 `HM202505227320`
|
||||||
|
- Excel 中可能是 `HM202505227320-1`、`HM202505227320-2`(带后缀)
|
||||||
|
- 匹配时应使用基础订单号
|
||||||
|
|
||||||
|
4. **常见问题**:
|
||||||
|
- 空白页(签收单背面)会被自动跳过
|
||||||
|
- OCR 可能误识别字符(如 5→S, 0→O),建议人工抽查
|
||||||
|
- 大 PDF 处理较慢,可先用低 DPI 预览
|
||||||
|
|
||||||
|
5. **"次月结"/"待结算" 订单**:
|
||||||
|
- 这类订单通常标记为"没有报价"、"未建单"等
|
||||||
|
- 虽然在系统中有记录,但可能未计入正式结算金额
|
||||||
|
- 诉讼中需特别关注这类订单的状态
|
||||||
|
|
||||||
|
6. **多承运商订单归属校验(重要风险点)**:
|
||||||
|
- 承运商可能将其他公司的订单混入证据,企图多收运费
|
||||||
|
- 必须检查PDF订单是否出现在其他承运商的对账单中
|
||||||
|
- 如果订单同时出现在多个承运商对账单中,则为异常订单
|
||||||
|
|
||||||
|
#### 多承运商订单归属校验
|
||||||
|
|
||||||
|
```python
|
||||||
|
def check_order_ownership(
|
||||||
|
scanned_df: pd.DataFrame,
|
||||||
|
my_company_excel: str,
|
||||||
|
other_carrier_excels: list[str],
|
||||||
|
order_column: str = '华住/锦江订单号'
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
检查订单归属,识别是否有其他承运商的订单被混入
|
||||||
|
|
||||||
|
Args:
|
||||||
|
scanned_df: OCR 提取的订单 DataFrame
|
||||||
|
my_company_excel: 我司对账单路径
|
||||||
|
other_carrier_excels: 其他承运商对账单路径列表
|
||||||
|
order_column: 订单号列名
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{
|
||||||
|
'my_orders': 属于我司的订单,
|
||||||
|
'other_carrier_orders': 属于其他承运商的订单(异常),
|
||||||
|
'disputed_orders': 同时出现在多处的争议订单,
|
||||||
|
'summary': 汇总
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# 提取PDF订单
|
||||||
|
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
||||||
|
|
||||||
|
# 加载我司对账单订单
|
||||||
|
my_orders = set()
|
||||||
|
try:
|
||||||
|
xl = pd.ExcelFile(my_company_excel)
|
||||||
|
for sheet in xl.sheet_names:
|
||||||
|
df = pd.read_excel(my_company_excel, sheet_name=sheet)
|
||||||
|
for col in df.columns:
|
||||||
|
if '订单' in str(col):
|
||||||
|
orders = df[col].apply(extract_base_order_no).dropna()
|
||||||
|
my_orders.update(orders)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"加载我司对账单失败: {e}")
|
||||||
|
|
||||||
|
# 加载其他承运商对账单订单
|
||||||
|
other_orders = {} # {carrier_name: set of orders}
|
||||||
|
for excel_path in other_carrier_excels:
|
||||||
|
carrier_name = excel_path.split('/')[-1]
|
||||||
|
carrier_orders = set()
|
||||||
|
try:
|
||||||
|
xl = pd.ExcelFile(excel_path)
|
||||||
|
for sheet in xl.sheet_names:
|
||||||
|
df = pd.read_excel(excel_path, sheet_name=sheet)
|
||||||
|
for col in df.columns:
|
||||||
|
if '订单' in str(col):
|
||||||
|
orders = df[col].apply(extract_base_order_no).dropna()
|
||||||
|
carrier_orders.update(orders)
|
||||||
|
other_orders[carrier_name] = carrier_orders
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 分析订单归属
|
||||||
|
all_other_orders = set()
|
||||||
|
for orders in other_orders.values():
|
||||||
|
all_other_orders.update(orders)
|
||||||
|
|
||||||
|
# 分类
|
||||||
|
only_mine = scan_orders & my_orders - all_other_orders # 仅在我司
|
||||||
|
only_others = scan_orders & all_other_orders - my_orders # 仅在其他承运商(异常!)
|
||||||
|
in_both = scan_orders & my_orders & all_other_orders # 同时出现(争议)
|
||||||
|
nowhere = scan_orders - my_orders - all_other_orders # 哪里都没有
|
||||||
|
|
||||||
|
# 识别异常订单属于哪个承运商
|
||||||
|
abnormal_details = []
|
||||||
|
for order in only_others:
|
||||||
|
for carrier, orders in other_orders.items():
|
||||||
|
if order in orders:
|
||||||
|
page = scanned_df[scanned_df['order_no'].apply(extract_base_order_no) == order]['page'].values
|
||||||
|
abnormal_details.append({
|
||||||
|
'order_no': order,
|
||||||
|
'belongs_to': carrier,
|
||||||
|
'pdf_page': page[0] if len(page) > 0 else None
|
||||||
|
})
|
||||||
|
|
||||||
|
summary = {
|
||||||
|
'scan_total': len(scan_orders),
|
||||||
|
'my_orders_count': len(only_mine),
|
||||||
|
'other_carrier_count': len(only_others), # 异常订单数
|
||||||
|
'disputed_count': len(in_both),
|
||||||
|
'not_found_count': len(nowhere)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'my_orders': only_mine,
|
||||||
|
'other_carrier_orders': only_others,
|
||||||
|
'abnormal_details': abnormal_details,
|
||||||
|
'disputed_orders': in_both,
|
||||||
|
'not_found': nowhere,
|
||||||
|
'summary': summary
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 使用示例(多承运商校验)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 检查订单归属
|
||||||
|
result = check_order_ownership(
|
||||||
|
scanned_df=scanned_df,
|
||||||
|
my_company_excel="智慧云彩对账单_2025年6月.xlsx",
|
||||||
|
other_carrier_excels=[
|
||||||
|
"友利速运对账单_2025年6月.xlsx",
|
||||||
|
"其他承运商对账单.xlsx"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 检查是否有异常订单
|
||||||
|
if result['summary']['other_carrier_count'] > 0:
|
||||||
|
print(f"⚠️ 发现 {result['summary']['other_carrier_count']} 个异常订单!")
|
||||||
|
print("这些订单属于其他承运商,不应由我司支付:")
|
||||||
|
for item in result['abnormal_details']:
|
||||||
|
print(f" {item['order_no']} -> 属于 {item['belongs_to']} (PDF第{item['pdf_page']}页)")
|
||||||
|
else:
|
||||||
|
print("✓ 未发现异常订单,所有订单归属正确")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
@@ -2,11 +2,6 @@
|
|||||||
name: ai-proj
|
name: ai-proj
|
||||||
description: ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。
|
description: ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。
|
||||||
---
|
---
|
||||||
|
|
||||||
# ai-proj MCP 技能
|
|
||||||
|
|
||||||
通过 MCP (Model Context Protocol) 直接调用 ai-proj 服务,管理任务、需求和文档。
|
|
||||||
|
|
||||||
## 功能概述
|
## 功能概述
|
||||||
|
|
||||||
| 模块 | 功能 |
|
| 模块 | 功能 |
|
||||||
@@ -572,102 +567,6 @@ approved → [archive] → archived
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 六、每日聚焦
|
|
||||||
|
|
||||||
### 6.1 查看今日任务
|
|
||||||
|
|
||||||
**MCP 函数**: `mcp__ai-proj__get_daily_focus_tasks`
|
|
||||||
|
|
||||||
**参数**:
|
|
||||||
| 参数 | 类型 | 必填 | 说明 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| date | string | ❌ | 日期 (YYYY-MM-DD) |
|
|
||||||
| status | string | ❌ | 状态筛选: active/completed/removed |
|
|
||||||
| priority | string | ❌ | 优先级筛选 |
|
|
||||||
| include_suggestions | boolean | ❌ | 是否包含智能推荐 |
|
|
||||||
|
|
||||||
### 6.2 添加任务到今日聚焦
|
|
||||||
|
|
||||||
**单个添加**: `mcp__ai-proj__add_daily_focus_task`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- task_id (number) - 任务ID
|
|
||||||
- priority (string) - 优先级: critical/high/medium/low
|
|
||||||
- estimated_duration_minutes (number) - 预估时长
|
|
||||||
- notes (string) - 备注
|
|
||||||
- focus_date (string) - 聚焦日期
|
|
||||||
```
|
|
||||||
|
|
||||||
**批量添加**: `mcp__ai-proj__batch_add_daily_focus_tasks`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- task_ids (array) - 任务ID列表
|
|
||||||
- priority (string) - 统一优先级
|
|
||||||
- notes (string) - 统一备注
|
|
||||||
- focus_date (string)
|
|
||||||
```
|
|
||||||
|
|
||||||
**快速添加当前任务**: `mcp__ai-proj__quick_add_current_task`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- priority (string) - 优先级,默认high
|
|
||||||
- notes (string)
|
|
||||||
功能: 将当前正在进行的任务添加到今日聚焦
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6.3 管理今日任务
|
|
||||||
|
|
||||||
**更新**: `mcp__ai-proj__update_daily_focus_task`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- id (number) - Daily Focus任务ID
|
|
||||||
- priority, estimated_duration_minutes, notes
|
|
||||||
```
|
|
||||||
|
|
||||||
**移除**: `mcp__ai-proj__remove_daily_focus_task`
|
|
||||||
```
|
|
||||||
参数: id (number)
|
|
||||||
```
|
|
||||||
|
|
||||||
**完成**: `mcp__ai-proj__complete_daily_focus_task`
|
|
||||||
```
|
|
||||||
参数: id (number)
|
|
||||||
```
|
|
||||||
|
|
||||||
**聚焦并计时**: `mcp__ai-proj__focus_task_with_timer`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- daily_focus_task_id (number)
|
|
||||||
- timer_description (string)
|
|
||||||
```
|
|
||||||
|
|
||||||
**清理已完成**: `mcp__ai-proj__clear_completed_tasks`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- date (string) - 日期
|
|
||||||
- confirm (boolean) - 必须设为true
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6.4 统计与推荐
|
|
||||||
|
|
||||||
**获取统计**: `mcp__ai-proj__get_daily_focus_stats`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- date (string)
|
|
||||||
- period (string) - 统计周期: daily/weekly/monthly
|
|
||||||
```
|
|
||||||
|
|
||||||
**获取任务推荐**: `mcp__ai-proj__get_task_recommendations`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- date (string)
|
|
||||||
- limit (number) - 推荐数量
|
|
||||||
- exclude_existing (boolean) - 排除已存在任务
|
|
||||||
返回: 基于优先级、截止日期等的智能推荐
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、项目管理
|
## 七、项目管理
|
||||||
|
|
||||||
### 7.1 查看项目列表
|
### 7.1 查看项目列表
|
||||||
@@ -696,90 +595,6 @@ approved → [archive] → archived
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 八、项目手册
|
|
||||||
|
|
||||||
### 8.1 查看手册列表
|
|
||||||
|
|
||||||
**MCP 函数**: `mcp__ai-proj__list_manuals`
|
|
||||||
|
|
||||||
**参数**:
|
|
||||||
| 参数 | 类型 | 必填 | 说明 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| projectId | number | ✅ | 项目ID |
|
|
||||||
| status | string | ❌ | 状态筛选: draft/published/archived |
|
|
||||||
| parentId | number | ❌ | 父级手册ID(获取子级列表) |
|
|
||||||
| search | string | ❌ | 搜索关键词(标题或描述) |
|
|
||||||
|
|
||||||
**自然语言示例**:
|
|
||||||
- "查看项目手册列表"
|
|
||||||
- "列出已发布的手册"
|
|
||||||
- "查看手册 5 的子章节"
|
|
||||||
|
|
||||||
### 8.2 创建手册
|
|
||||||
|
|
||||||
**MCP 函数**: `mcp__ai-proj__create_manual`
|
|
||||||
|
|
||||||
**参数**:
|
|
||||||
| 参数 | 类型 | 必填 | 说明 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| projectId | number | ✅ | 项目ID |
|
|
||||||
| title | string | ✅ | 手册标题 |
|
|
||||||
| content | string | ❌ | 手册内容(Markdown格式) |
|
|
||||||
| description | string | ❌ | 手册简介 |
|
|
||||||
| parentId | number | ❌ | 父级手册ID(用于创建子章节) |
|
|
||||||
| status | string | ❌ | 状态: draft/published,默认draft |
|
|
||||||
|
|
||||||
**自然语言示例**:
|
|
||||||
- "创建项目手册: API 开发规范"
|
|
||||||
- "在手册 5 下创建子章节: 接口设计规范"
|
|
||||||
- "创建并发布手册: 前端开发规范"
|
|
||||||
|
|
||||||
### 8.3 获取手册详情
|
|
||||||
|
|
||||||
**MCP 函数**: `mcp__ai-proj__get_manual`
|
|
||||||
|
|
||||||
**参数**: `id` (number) - 手册ID
|
|
||||||
|
|
||||||
**返回**: 完整的手册内容(Markdown格式)
|
|
||||||
|
|
||||||
**自然语言示例**:
|
|
||||||
- "查看手册 5 的内容"
|
|
||||||
- "显示 API 开发规范手册"
|
|
||||||
|
|
||||||
### 8.4 更新手册
|
|
||||||
|
|
||||||
**MCP 函数**: `mcp__ai-proj__update_manual`
|
|
||||||
|
|
||||||
**参数**:
|
|
||||||
| 参数 | 类型 | 必填 | 说明 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| id | number | ✅ | 手册ID |
|
|
||||||
| title | string | ❌ | 新标题 |
|
|
||||||
| content | string | ❌ | 新内容(Markdown格式) |
|
|
||||||
| description | string | ❌ | 新简介 |
|
|
||||||
| status | string | ❌ | 新状态: draft/published/archived |
|
|
||||||
|
|
||||||
**自然语言示例**:
|
|
||||||
- "更新手册 5 的内容"
|
|
||||||
- "发布手册 5"
|
|
||||||
- "归档手册 5"
|
|
||||||
|
|
||||||
### 8.5 搜索手册
|
|
||||||
|
|
||||||
**MCP 函数**: `mcp__ai-proj__search_manuals`
|
|
||||||
|
|
||||||
**参数**:
|
|
||||||
| 参数 | 类型 | 必填 | 说明 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| projectId | number | ✅ | 项目ID |
|
|
||||||
| query | string | ✅ | 搜索关键词 |
|
|
||||||
|
|
||||||
**自然语言示例**:
|
|
||||||
- "搜索项目手册中包含 API 的文档"
|
|
||||||
- "查找数据库相关的手册"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 九、报告与时间线
|
## 九、报告与时间线
|
||||||
|
|
||||||
### 9.1 获取日报
|
### 9.1 获取日报
|
||||||
@@ -830,83 +645,6 @@ approved → [archive] → archived
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 十一、远程同步
|
|
||||||
|
|
||||||
### 11.1 任务同步
|
|
||||||
|
|
||||||
**同步任务**: `mcp__ai-proj__sync_task`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- taskId (number)
|
|
||||||
- direction (string) - to_remote/from_remote
|
|
||||||
- includeDocument (boolean) - 是否同步文档
|
|
||||||
- includeChildren (boolean) - 是否同步子任务
|
|
||||||
```
|
|
||||||
|
|
||||||
**批量同步到远程**: `mcp__ai-proj__batch_sync_tasks_to_remote`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- taskIds (array)
|
|
||||||
- includeDocuments (boolean)
|
|
||||||
- includeChildren (boolean)
|
|
||||||
```
|
|
||||||
|
|
||||||
**获取同步状态**: `mcp__ai-proj__get_task_sync_status`
|
|
||||||
```
|
|
||||||
参数: taskId (number)
|
|
||||||
```
|
|
||||||
|
|
||||||
**获取远程同步状态**: `mcp__ai-proj__get_remote_sync_status`
|
|
||||||
```
|
|
||||||
参数: taskId (number)
|
|
||||||
返回: 比较本地和远程版本差异
|
|
||||||
```
|
|
||||||
|
|
||||||
**通过UUID获取任务**: `mcp__ai-proj__get_task_by_uuid`
|
|
||||||
```
|
|
||||||
参数: uuid (string)
|
|
||||||
用途: 跨库同步场景
|
|
||||||
```
|
|
||||||
|
|
||||||
### 11.2 需求同步
|
|
||||||
|
|
||||||
**同步需求到远程**: `mcp__ai-proj__sync_requirement_to_remote`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- requirementId (number)
|
|
||||||
- includeHistory (boolean)
|
|
||||||
- includeComments (boolean)
|
|
||||||
```
|
|
||||||
|
|
||||||
**从远程同步需求**: `mcp__ai-proj__sync_requirement_from_remote`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- remoteRequirementId (number)
|
|
||||||
- includeHistory (boolean)
|
|
||||||
- includeComments (boolean)
|
|
||||||
```
|
|
||||||
|
|
||||||
**批量同步需求**: `mcp__ai-proj__batch_sync_requirements_to_remote`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- requirementIds (array)
|
|
||||||
- includeComments (boolean)
|
|
||||||
```
|
|
||||||
|
|
||||||
**同步需求-任务关联**: `mcp__ai-proj__sync_requirement_task_links`
|
|
||||||
```
|
|
||||||
参数:
|
|
||||||
- linkUUIDs (array) - 关联关系的UUID列表
|
|
||||||
- forceUpdate (boolean)
|
|
||||||
```
|
|
||||||
|
|
||||||
**同步任务的需求关联**: `mcp__ai-proj__sync_task_links_to_remote`
|
|
||||||
```
|
|
||||||
参数: taskId (number)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 十二、开发工具
|
## 十二、开发工具
|
||||||
|
|
||||||
### 12.1 快速登录
|
### 12.1 快速登录
|
||||||
@@ -919,52 +657,6 @@ approved → [archive] → archived
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 自然语言操作示例
|
|
||||||
|
|
||||||
### 任务管理
|
|
||||||
|
|
||||||
| 用户说 | 调用函数 |
|
|
||||||
|--------|----------|
|
|
||||||
| "创建一个任务叫做登录功能开发" | `create_task` |
|
|
||||||
| "查看所有进行中的任务" | `list_tasks` |
|
|
||||||
| "开始任务 5069" | `start_task` |
|
|
||||||
| "完成任务 5069" | `complete_task` |
|
|
||||||
| "暂停任务 5069" | `pause_task` |
|
|
||||||
| "把任务 5069 移到项目2" | `move_task` |
|
|
||||||
| "给任务 5069 创建子任务: 数据库设计" | `create_subtask` |
|
|
||||||
|
|
||||||
### 需求管理
|
|
||||||
|
|
||||||
| 用户说 | 调用函数 |
|
|
||||||
|--------|----------|
|
|
||||||
| "创建需求: 用户权限管理" | `create_requirement` |
|
|
||||||
| "查看待审核的需求" | `list_requirements` |
|
|
||||||
| "提交需求 123 评审" | `requirement_action (submit)` |
|
|
||||||
| "批准需求 123" | `requirement_action (approve)` |
|
|
||||||
| "拒绝需求 123,原因是需求不完整" | `requirement_action (reject)` |
|
|
||||||
| "将任务 5069 关联到需求 123" | `link_tasks_to_requirement` |
|
|
||||||
|
|
||||||
### 文档操作
|
|
||||||
|
|
||||||
| 用户说 | 调用函数 |
|
|
||||||
|--------|----------|
|
|
||||||
| "为任务 5069 创建PRD文档" | `create-and-attach` |
|
|
||||||
| "查看任务 5069 的文档" | `get_task_document` |
|
|
||||||
| "更新任务 5069 的文档" | `update_task_document` |
|
|
||||||
| "导出任务 5069 的文档到文件" | `export_task_document_to_file` |
|
|
||||||
|
|
||||||
### 计时与聚焦
|
|
||||||
|
|
||||||
| 用户说 | 调用函数 |
|
|
||||||
|--------|----------|
|
|
||||||
| "开始计时任务 5069" | `start_task_with_timer` |
|
|
||||||
| "停止计时" | `stop_timer` |
|
|
||||||
| "查看今日任务" | `get_daily_focus_tasks` |
|
|
||||||
| "把任务 5069 添加到今日聚焦" | `add_daily_focus_task` |
|
|
||||||
| "生成今日工作报告" | `get_daily_work_report` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **项目ID默认值**: 大多数函数的 `projectId` 默认为 1
|
1. **项目ID默认值**: 大多数函数的 `projectId` 默认为 1
|
||||||
@@ -1013,229 +705,6 @@ go run /tmp/genhash.go
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Google 日历集成配置
|
|
||||||
|
|
||||||
### 环境变量
|
|
||||||
|
|
||||||
生产环境需要配置以下环境变量(`.env` 文件):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOOGLE_CLIENT_ID=<你的Client ID>.apps.googleusercontent.com
|
|
||||||
GOOGLE_CLIENT_SECRET=GOCSPX-<你的Client Secret>
|
|
||||||
GOOGLE_PROJECT_ID=<你的项目ID>
|
|
||||||
GOOGLE_REDIRECT_URL=https://ai.pipexerp.com/api/v1/auth/google/callback
|
|
||||||
GOOGLE_CALENDAR_SCOPES=https://www.googleapis.com/auth/calendar
|
|
||||||
```
|
|
||||||
|
|
||||||
### 获取 Google OAuth 凭据
|
|
||||||
|
|
||||||
#### 1. 创建 Google Cloud 项目
|
|
||||||
|
|
||||||
1. 访问 [Google Cloud Console](https://console.cloud.google.com/)
|
|
||||||
2. 创建新项目或选择现有项目
|
|
||||||
3. 记录项目 ID
|
|
||||||
|
|
||||||
#### 2. 启用 API
|
|
||||||
|
|
||||||
1. 进入 **APIs & Services** → **Library**
|
|
||||||
2. 搜索并启用 **Google Calendar API**
|
|
||||||
|
|
||||||
#### 3. 配置 OAuth 同意屏幕
|
|
||||||
|
|
||||||
1. 进入 **APIs & Services** → **OAuth consent screen**
|
|
||||||
2. 选择 **External** → 创建
|
|
||||||
3. 填写应用名称、支持邮箱
|
|
||||||
4. 添加 Scopes:
|
|
||||||
- `https://www.googleapis.com/auth/calendar`
|
|
||||||
- `https://www.googleapis.com/auth/calendar.events`
|
|
||||||
5. 添加测试用户(开发阶段)
|
|
||||||
|
|
||||||
#### 4. 创建 OAuth 凭据
|
|
||||||
|
|
||||||
1. 进入 **APIs & Services** → **Credentials**
|
|
||||||
2. 点击 **+ CREATE CREDENTIALS** → **OAuth client ID**
|
|
||||||
3. 选择 **Web application**
|
|
||||||
4. 配置:
|
|
||||||
- **Name**: `AI-Proj Web Client`
|
|
||||||
- **Authorized JavaScript origins**: `https://ai.pipexerp.com`
|
|
||||||
- **Authorized redirect URIs**: `https://ai.pipexerp.com/api/v1/auth/google/callback`
|
|
||||||
5. 复制 Client ID 和 Client Secret
|
|
||||||
|
|
||||||
### 部署配置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 更新生产环境配置
|
|
||||||
ssh tools_ai_proj "cd /home/ubuntu/apps/new-ai-proj && \
|
|
||||||
sed -i 's|GOOGLE_CLIENT_ID=.*|GOOGLE_CLIENT_ID=<CLIENT_ID>|' .env && \
|
|
||||||
sed -i 's|GOOGLE_CLIENT_SECRET=.*|GOOGLE_CLIENT_SECRET=<CLIENT_SECRET>|' .env && \
|
|
||||||
sed -i 's|GOOGLE_PROJECT_ID=.*|GOOGLE_PROJECT_ID=<PROJECT_ID>|' .env && \
|
|
||||||
docker restart ai_backend_prod"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 数据库迁移
|
|
||||||
|
|
||||||
Google 日历集成需要以下表:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- OAuth 状态表(CSRF 保护)
|
|
||||||
CREATE TABLE oauth_states (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
state VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
expires_at TIMESTAMP NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 项目日历配置表
|
|
||||||
-- 见 migrations/20260210_01_project_calendar.sql
|
|
||||||
|
|
||||||
-- 日历 Webhook 表
|
|
||||||
-- 见 migrations/20260211_01_calendar_webhooks.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
### 当前配置(2026-02)
|
|
||||||
|
|
||||||
| 配置项 | 值 |
|
|
||||||
|--------|-----|
|
|
||||||
| Project ID | `project-bfa64769-1595-4c41-964` |
|
|
||||||
| Client ID | `78721641513-...apps.googleusercontent.com` |
|
|
||||||
| Redirect URL | `https://ai.pipexerp.com/api/v1/auth/google/callback` |
|
|
||||||
|
|
||||||
> **注意**: Client Secret 不在文档中记录,存储在生产环境 `.env` 文件中
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 十三、最佳实践与故障排查
|
|
||||||
|
|
||||||
### 13.1 创建需求的最佳实践
|
|
||||||
|
|
||||||
#### 问题:description 字段"请求数据格式错误"
|
|
||||||
|
|
||||||
**症状**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": "BAD_REQUEST",
|
|
||||||
"message": "请求数据格式错误"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**常见原因**:
|
|
||||||
|
|
||||||
| 原因 | 解决方案 |
|
|
||||||
|------|----------|
|
|
||||||
| **description 内容过长** | API 可能有字段长度限制,逐步测试找到上限 |
|
|
||||||
| **Markdown 特殊字符** | 检查是否有未转义的特殊字符(如反引号、引号) |
|
|
||||||
| **JSON 转义问题** | 确保 JSON 字符串正确转义(尤其是换行符、引号) |
|
|
||||||
| **多层嵌套的代码块** | Markdown 代码块中的反引号可能导致解析失败 |
|
|
||||||
|
|
||||||
**正确的调试流程**:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# ❌ 错误做法:直接简化内容
|
|
||||||
"创建失败 → 简化 description → 成功"
|
|
||||||
问题:丢失了重要的需求细节
|
|
||||||
|
|
||||||
# ✅ 正确做法:找到根本原因
|
|
||||||
步骤 1: 测试空描述
|
|
||||||
create_requirement(description="")
|
|
||||||
→ 是否成功?
|
|
||||||
|
|
||||||
步骤 2: 测试纯文本
|
|
||||||
create_requirement(description="简单文本")
|
|
||||||
→ 是否成功?
|
|
||||||
|
|
||||||
步骤 3: 测试简单 Markdown
|
|
||||||
create_requirement(description="# 标题\n\n内容")
|
|
||||||
→ 是否成功?
|
|
||||||
|
|
||||||
步骤 4: 逐步增加复杂度
|
|
||||||
create_requirement(description="包含代码块的Markdown")
|
|
||||||
create_requirement(description="包含表格的Markdown")
|
|
||||||
create_requirement(description="完整的技术方案")
|
|
||||||
→ 找到临界点
|
|
||||||
|
|
||||||
步骤 5: 根据根因选择方案
|
|
||||||
- 如果是长度限制 → 分段或使用附件
|
|
||||||
- 如果是特殊字符 → 转义或替换
|
|
||||||
- 如果是 Markdown → 简化格式或转纯文本
|
|
||||||
```
|
|
||||||
|
|
||||||
**推荐方案**:
|
|
||||||
|
|
||||||
**方案 A:完整的需求 + 关联文档**
|
|
||||||
```typescript
|
|
||||||
// 1. 创建需求(简洁描述)
|
|
||||||
const req = await create_requirement({
|
|
||||||
title: "思源笔记增加 MCP 协议层支持",
|
|
||||||
description: "核心方案:创建独立的 siyuan-mcp-bridge 服务...",
|
|
||||||
projectId: 162
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. 创建详细的技术方案文档
|
|
||||||
const doc = await create_requirement_document({
|
|
||||||
requirementId: req.id,
|
|
||||||
title: "技术方案详细设计",
|
|
||||||
content: `
|
|
||||||
# 需求背景
|
|
||||||
...(完整的技术方案,3000+ 字)
|
|
||||||
|
|
||||||
# 架构设计
|
|
||||||
...
|
|
||||||
|
|
||||||
# 实施路线图
|
|
||||||
...
|
|
||||||
`
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**方案 B:分段创建需求描述**
|
|
||||||
```typescript
|
|
||||||
// 1. 创建需求(基本信息)
|
|
||||||
const req = await create_requirement({
|
|
||||||
title: "思源笔记增加 MCP 协议层支持",
|
|
||||||
description: "简短描述",
|
|
||||||
projectId: 162
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. 更新需求(逐步添加内容)
|
|
||||||
await update_requirement({
|
|
||||||
id: req.id,
|
|
||||||
updates: {
|
|
||||||
description: description_part1 // 添加背景
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await update_requirement({
|
|
||||||
id: req.id,
|
|
||||||
updates: {
|
|
||||||
description: description_part1 + description_part2 // 添加架构
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 经验总结
|
|
||||||
|
|
||||||
**原则 1:业务优先于技术**
|
|
||||||
- ❌ 为了创建成功而牺牲需求完整性
|
|
||||||
- ✅ 找到技术方案来满足业务需求
|
|
||||||
|
|
||||||
**原则 2:追根溯源**
|
|
||||||
- ❌ 基于假设快速妥协("可能是太长了")
|
|
||||||
- ✅ 通过调试找到真正的错误原因
|
|
||||||
|
|
||||||
**原则 3:给用户选择权**
|
|
||||||
- ❌ 自作主张简化内容
|
|
||||||
- ✅ 询问用户的偏好和优先级
|
|
||||||
|
|
||||||
**原则 4:保留完整信息**
|
|
||||||
- ❌ 简化后的需求缺少关键信息
|
|
||||||
- ✅ 通过关联文档保留详细内容
|
|
||||||
|
|
||||||
### 13.2 需求描述的格式建议
|
|
||||||
|
|
||||||
**推荐的需求描述结构**(简洁版):
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 核心方案
|
## 核心方案
|
||||||
1. 关键点 1(一句话)
|
1. 关键点 1(一句话)
|
||||||
2. 关键点 2(一句话)
|
2. 关键点 2(一句话)
|
||||||
@@ -1246,139 +715,15 @@ await update_requirement({
|
|||||||
- 第2层:组件名(职责)
|
- 第2层:组件名(职责)
|
||||||
- 第3层:组件名(职责)
|
- 第3层:组件名(职责)
|
||||||
|
|
||||||
## 实施周期
|
|
||||||
X周(阶段1 + 阶段2 + 阶段3)
|
|
||||||
```
|
|
||||||
|
|
||||||
**详细内容放在关联文档**:
|
## 详细内容
|
||||||
- 需求背景与业务价值
|
|
||||||
- 详细的技术方案
|
|
||||||
- 实施路线图与时间规划
|
|
||||||
- 风险评估与应对措施
|
|
||||||
- 验收标准
|
|
||||||
|
|
||||||
**Markdown 格式注意事项**:
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
```markdown
|
- 六、每日聚焦
|
||||||
✅ 安全的格式:
|
- 八、项目手册
|
||||||
- 简单的标题(# ## ###)
|
- 十一、远程同步
|
||||||
- 无序列表(-)
|
- 自然语言操作示例
|
||||||
- 有序列表(1. 2. 3.)
|
- Google 日历集成配置
|
||||||
- 简单的表格
|
- 十三、最佳实践与故障排查
|
||||||
- 加粗(**text**)
|
- 实施周期
|
||||||
|
|
||||||
⚠️ 可能有问题的格式:
|
|
||||||
- 三重反引号代码块(```)
|
|
||||||
- HTML 标签
|
|
||||||
- 复杂的嵌套列表
|
|
||||||
- 大量的特殊字符
|
|
||||||
- 超长的单行文本
|
|
||||||
```
|
|
||||||
|
|
||||||
### 13.3 更新需求的最佳实践
|
|
||||||
|
|
||||||
**使用 update_requirement 补充详细内容**:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// 创建需求后,逐步更新描述
|
|
||||||
await update_requirement({
|
|
||||||
id: 857, // REQ-20260217-0001
|
|
||||||
updates: {
|
|
||||||
description: `
|
|
||||||
## 需求背景
|
|
||||||
|
|
||||||
### 当前问题
|
|
||||||
思源笔记仅提供 REST API 接口,AI 操作复杂...
|
|
||||||
|
|
||||||
### 业务价值
|
|
||||||
为思源笔记添加 MCP 协议层支持,使其能够...
|
|
||||||
|
|
||||||
## 技术方案
|
|
||||||
|
|
||||||
### 架构设计
|
|
||||||
...(详细内容)
|
|
||||||
`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**测试长度限制**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 逐步增加内容,找到上限
|
|
||||||
第1次: 200字 → 成功
|
|
||||||
第2次: 500字 → 成功
|
|
||||||
第3次: 1000字 → 成功
|
|
||||||
第4次: 2000字 → 成功
|
|
||||||
第5次: 5000字 → 失败 ← 找到上限
|
|
||||||
|
|
||||||
结论: description 字段最大约 3000-4000 字
|
|
||||||
```
|
|
||||||
|
|
||||||
### 13.4 故障排查清单
|
|
||||||
|
|
||||||
遇到 API 错误时,按以下步骤排查:
|
|
||||||
|
|
||||||
**步骤 1:分析错误信息**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": "BAD_REQUEST",
|
|
||||||
"message": "请求数据格式错误"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- [ ] 错误码是什么?(BAD_REQUEST / VALIDATION_ERROR / AUTH_FAILED)
|
|
||||||
- [ ] 错误信息是否有具体提示?
|
|
||||||
- [ ] 是哪个字段导致的错误?
|
|
||||||
|
|
||||||
**步骤 2:检查参数格式**
|
|
||||||
- [ ] 必填参数是否都提供了?
|
|
||||||
- [ ] 参数类型是否正确?(number vs string)
|
|
||||||
- [ ] 枚举值是否在允许范围内?
|
|
||||||
- [ ] JSON 格式是否正确?
|
|
||||||
|
|
||||||
**步骤 3:简化测试**
|
|
||||||
- [ ] 尝试最小化的参数集
|
|
||||||
- [ ] 逐个添加可选参数
|
|
||||||
- [ ] 找到导致错误的具体参数
|
|
||||||
|
|
||||||
**步骤 4:查看后端日志**(如有权限)
|
|
||||||
```bash
|
|
||||||
# 生产环境日志
|
|
||||||
ssh tools_ai_proj "docker logs ai_backend_prod --tail 100"
|
|
||||||
|
|
||||||
# 开发环境日志
|
|
||||||
cd ~/coding/new-ai-proj/backend
|
|
||||||
tail -f logs/app.log
|
|
||||||
```
|
|
||||||
|
|
||||||
**步骤 5:参考类似的成功案例**
|
|
||||||
- [ ] 查看 SKILL.md 中的示例
|
|
||||||
- [ ] 查看之前成功的调用记录
|
|
||||||
- [ ] 对比参数差异
|
|
||||||
|
|
||||||
### 13.5 常见错误与解决方案
|
|
||||||
|
|
||||||
| 错误 | 可能原因 | 解决方案 |
|
|
||||||
|------|----------|----------|
|
|
||||||
| `BAD_REQUEST` | 参数格式错误 | 检查参数类型和 JSON 格式 |
|
|
||||||
| `VALIDATION_ERROR` | 参数验证失败 | 检查必填字段和枚举值 |
|
|
||||||
| `AUTH_FAILED` | 认证失败 | 检查 Token 是否过期 |
|
|
||||||
| `PERMISSION_DENIED` | 权限不足 | 检查用户角色和权限 |
|
|
||||||
| `NOT_FOUND` | 资源不存在 | 检查 ID 是否正确 |
|
|
||||||
| `INTERNAL_ERROR` | 服务器错误 | 查看后端日志排查 |
|
|
||||||
|
|
||||||
### 13.6 需求 description 长度测试记录
|
|
||||||
|
|
||||||
**测试日期**: 2026-02-17
|
|
||||||
|
|
||||||
| 长度 | 格式 | 结果 | 备注 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| ~200字 | 简单文本 | ✅ 成功 | 核心方案3点 |
|
|
||||||
| ~3000字 | 完整 Markdown | ❌ 失败 | "请求数据格式错误" |
|
|
||||||
| 待测试 | 1000字 Markdown | ? | 需要进一步测试 |
|
|
||||||
| 待测试 | 2000字 Markdown | ? | 需要进一步测试 |
|
|
||||||
|
|
||||||
**建议**:
|
|
||||||
- 需求描述控制在 1000 字以内
|
|
||||||
- 详细内容使用关联文档
|
|
||||||
- 复杂格式改为纯文本
|
|
||||||
|
|||||||
@@ -0,0 +1,664 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 六、每日聚焦
|
||||||
|
|
||||||
|
### 6.1 查看今日任务
|
||||||
|
|
||||||
|
**MCP 函数**: `mcp__ai-proj__get_daily_focus_tasks`
|
||||||
|
|
||||||
|
**参数**:
|
||||||
|
| 参数 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| date | string | ❌ | 日期 (YYYY-MM-DD) |
|
||||||
|
| status | string | ❌ | 状态筛选: active/completed/removed |
|
||||||
|
| priority | string | ❌ | 优先级筛选 |
|
||||||
|
| include_suggestions | boolean | ❌ | 是否包含智能推荐 |
|
||||||
|
|
||||||
|
### 6.2 添加任务到今日聚焦
|
||||||
|
|
||||||
|
**单个添加**: `mcp__ai-proj__add_daily_focus_task`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- task_id (number) - 任务ID
|
||||||
|
- priority (string) - 优先级: critical/high/medium/low
|
||||||
|
- estimated_duration_minutes (number) - 预估时长
|
||||||
|
- notes (string) - 备注
|
||||||
|
- focus_date (string) - 聚焦日期
|
||||||
|
```
|
||||||
|
|
||||||
|
**批量添加**: `mcp__ai-proj__batch_add_daily_focus_tasks`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- task_ids (array) - 任务ID列表
|
||||||
|
- priority (string) - 统一优先级
|
||||||
|
- notes (string) - 统一备注
|
||||||
|
- focus_date (string)
|
||||||
|
```
|
||||||
|
|
||||||
|
**快速添加当前任务**: `mcp__ai-proj__quick_add_current_task`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- priority (string) - 优先级,默认high
|
||||||
|
- notes (string)
|
||||||
|
功能: 将当前正在进行的任务添加到今日聚焦
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 管理今日任务
|
||||||
|
|
||||||
|
**更新**: `mcp__ai-proj__update_daily_focus_task`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- id (number) - Daily Focus任务ID
|
||||||
|
- priority, estimated_duration_minutes, notes
|
||||||
|
```
|
||||||
|
|
||||||
|
**移除**: `mcp__ai-proj__remove_daily_focus_task`
|
||||||
|
```
|
||||||
|
参数: id (number)
|
||||||
|
```
|
||||||
|
|
||||||
|
**完成**: `mcp__ai-proj__complete_daily_focus_task`
|
||||||
|
```
|
||||||
|
参数: id (number)
|
||||||
|
```
|
||||||
|
|
||||||
|
**聚焦并计时**: `mcp__ai-proj__focus_task_with_timer`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- daily_focus_task_id (number)
|
||||||
|
- timer_description (string)
|
||||||
|
```
|
||||||
|
|
||||||
|
**清理已完成**: `mcp__ai-proj__clear_completed_tasks`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- date (string) - 日期
|
||||||
|
- confirm (boolean) - 必须设为true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 统计与推荐
|
||||||
|
|
||||||
|
**获取统计**: `mcp__ai-proj__get_daily_focus_stats`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- date (string)
|
||||||
|
- period (string) - 统计周期: daily/weekly/monthly
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取任务推荐**: `mcp__ai-proj__get_task_recommendations`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- date (string)
|
||||||
|
- limit (number) - 推荐数量
|
||||||
|
- exclude_existing (boolean) - 排除已存在任务
|
||||||
|
返回: 基于优先级、截止日期等的智能推荐
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、项目手册
|
||||||
|
|
||||||
|
### 8.1 查看手册列表
|
||||||
|
|
||||||
|
**MCP 函数**: `mcp__ai-proj__list_manuals`
|
||||||
|
|
||||||
|
**参数**:
|
||||||
|
| 参数 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| projectId | number | ✅ | 项目ID |
|
||||||
|
| status | string | ❌ | 状态筛选: draft/published/archived |
|
||||||
|
| parentId | number | ❌ | 父级手册ID(获取子级列表) |
|
||||||
|
| search | string | ❌ | 搜索关键词(标题或描述) |
|
||||||
|
|
||||||
|
**自然语言示例**:
|
||||||
|
- "查看项目手册列表"
|
||||||
|
- "列出已发布的手册"
|
||||||
|
- "查看手册 5 的子章节"
|
||||||
|
|
||||||
|
### 8.2 创建手册
|
||||||
|
|
||||||
|
**MCP 函数**: `mcp__ai-proj__create_manual`
|
||||||
|
|
||||||
|
**参数**:
|
||||||
|
| 参数 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| projectId | number | ✅ | 项目ID |
|
||||||
|
| title | string | ✅ | 手册标题 |
|
||||||
|
| content | string | ❌ | 手册内容(Markdown格式) |
|
||||||
|
| description | string | ❌ | 手册简介 |
|
||||||
|
| parentId | number | ❌ | 父级手册ID(用于创建子章节) |
|
||||||
|
| status | string | ❌ | 状态: draft/published,默认draft |
|
||||||
|
|
||||||
|
**自然语言示例**:
|
||||||
|
- "创建项目手册: API 开发规范"
|
||||||
|
- "在手册 5 下创建子章节: 接口设计规范"
|
||||||
|
- "创建并发布手册: 前端开发规范"
|
||||||
|
|
||||||
|
### 8.3 获取手册详情
|
||||||
|
|
||||||
|
**MCP 函数**: `mcp__ai-proj__get_manual`
|
||||||
|
|
||||||
|
**参数**: `id` (number) - 手册ID
|
||||||
|
|
||||||
|
**返回**: 完整的手册内容(Markdown格式)
|
||||||
|
|
||||||
|
**自然语言示例**:
|
||||||
|
- "查看手册 5 的内容"
|
||||||
|
- "显示 API 开发规范手册"
|
||||||
|
|
||||||
|
### 8.4 更新手册
|
||||||
|
|
||||||
|
**MCP 函数**: `mcp__ai-proj__update_manual`
|
||||||
|
|
||||||
|
**参数**:
|
||||||
|
| 参数 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| id | number | ✅ | 手册ID |
|
||||||
|
| title | string | ❌ | 新标题 |
|
||||||
|
| content | string | ❌ | 新内容(Markdown格式) |
|
||||||
|
| description | string | ❌ | 新简介 |
|
||||||
|
| status | string | ❌ | 新状态: draft/published/archived |
|
||||||
|
|
||||||
|
**自然语言示例**:
|
||||||
|
- "更新手册 5 的内容"
|
||||||
|
- "发布手册 5"
|
||||||
|
- "归档手册 5"
|
||||||
|
|
||||||
|
### 8.5 搜索手册
|
||||||
|
|
||||||
|
**MCP 函数**: `mcp__ai-proj__search_manuals`
|
||||||
|
|
||||||
|
**参数**:
|
||||||
|
| 参数 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| projectId | number | ✅ | 项目ID |
|
||||||
|
| query | string | ✅ | 搜索关键词 |
|
||||||
|
|
||||||
|
**自然语言示例**:
|
||||||
|
- "搜索项目手册中包含 API 的文档"
|
||||||
|
- "查找数据库相关的手册"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十一、远程同步
|
||||||
|
|
||||||
|
### 11.1 任务同步
|
||||||
|
|
||||||
|
**同步任务**: `mcp__ai-proj__sync_task`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- taskId (number)
|
||||||
|
- direction (string) - to_remote/from_remote
|
||||||
|
- includeDocument (boolean) - 是否同步文档
|
||||||
|
- includeChildren (boolean) - 是否同步子任务
|
||||||
|
```
|
||||||
|
|
||||||
|
**批量同步到远程**: `mcp__ai-proj__batch_sync_tasks_to_remote`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- taskIds (array)
|
||||||
|
- includeDocuments (boolean)
|
||||||
|
- includeChildren (boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取同步状态**: `mcp__ai-proj__get_task_sync_status`
|
||||||
|
```
|
||||||
|
参数: taskId (number)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取远程同步状态**: `mcp__ai-proj__get_remote_sync_status`
|
||||||
|
```
|
||||||
|
参数: taskId (number)
|
||||||
|
返回: 比较本地和远程版本差异
|
||||||
|
```
|
||||||
|
|
||||||
|
**通过UUID获取任务**: `mcp__ai-proj__get_task_by_uuid`
|
||||||
|
```
|
||||||
|
参数: uuid (string)
|
||||||
|
用途: 跨库同步场景
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.2 需求同步
|
||||||
|
|
||||||
|
**同步需求到远程**: `mcp__ai-proj__sync_requirement_to_remote`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- requirementId (number)
|
||||||
|
- includeHistory (boolean)
|
||||||
|
- includeComments (boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**从远程同步需求**: `mcp__ai-proj__sync_requirement_from_remote`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- remoteRequirementId (number)
|
||||||
|
- includeHistory (boolean)
|
||||||
|
- includeComments (boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**批量同步需求**: `mcp__ai-proj__batch_sync_requirements_to_remote`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- requirementIds (array)
|
||||||
|
- includeComments (boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**同步需求-任务关联**: `mcp__ai-proj__sync_requirement_task_links`
|
||||||
|
```
|
||||||
|
参数:
|
||||||
|
- linkUUIDs (array) - 关联关系的UUID列表
|
||||||
|
- forceUpdate (boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**同步任务的需求关联**: `mcp__ai-proj__sync_task_links_to_remote`
|
||||||
|
```
|
||||||
|
参数: taskId (number)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 自然语言操作示例
|
||||||
|
|
||||||
|
### 任务管理
|
||||||
|
|
||||||
|
| 用户说 | 调用函数 |
|
||||||
|
|--------|----------|
|
||||||
|
| "创建一个任务叫做登录功能开发" | `create_task` |
|
||||||
|
| "查看所有进行中的任务" | `list_tasks` |
|
||||||
|
| "开始任务 5069" | `start_task` |
|
||||||
|
| "完成任务 5069" | `complete_task` |
|
||||||
|
| "暂停任务 5069" | `pause_task` |
|
||||||
|
| "把任务 5069 移到项目2" | `move_task` |
|
||||||
|
| "给任务 5069 创建子任务: 数据库设计" | `create_subtask` |
|
||||||
|
|
||||||
|
### 需求管理
|
||||||
|
|
||||||
|
| 用户说 | 调用函数 |
|
||||||
|
|--------|----------|
|
||||||
|
| "创建需求: 用户权限管理" | `create_requirement` |
|
||||||
|
| "查看待审核的需求" | `list_requirements` |
|
||||||
|
| "提交需求 123 评审" | `requirement_action (submit)` |
|
||||||
|
| "批准需求 123" | `requirement_action (approve)` |
|
||||||
|
| "拒绝需求 123,原因是需求不完整" | `requirement_action (reject)` |
|
||||||
|
| "将任务 5069 关联到需求 123" | `link_tasks_to_requirement` |
|
||||||
|
|
||||||
|
### 文档操作
|
||||||
|
|
||||||
|
| 用户说 | 调用函数 |
|
||||||
|
|--------|----------|
|
||||||
|
| "为任务 5069 创建PRD文档" | `create-and-attach` |
|
||||||
|
| "查看任务 5069 的文档" | `get_task_document` |
|
||||||
|
| "更新任务 5069 的文档" | `update_task_document` |
|
||||||
|
| "导出任务 5069 的文档到文件" | `export_task_document_to_file` |
|
||||||
|
|
||||||
|
### 计时与聚焦
|
||||||
|
|
||||||
|
| 用户说 | 调用函数 |
|
||||||
|
|--------|----------|
|
||||||
|
| "开始计时任务 5069" | `start_task_with_timer` |
|
||||||
|
| "停止计时" | `stop_timer` |
|
||||||
|
| "查看今日任务" | `get_daily_focus_tasks` |
|
||||||
|
| "把任务 5069 添加到今日聚焦" | `add_daily_focus_task` |
|
||||||
|
| "生成今日工作报告" | `get_daily_work_report` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Google 日历集成配置
|
||||||
|
|
||||||
|
### 环境变量
|
||||||
|
|
||||||
|
生产环境需要配置以下环境变量(`.env` 文件):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GOOGLE_CLIENT_ID=<你的Client ID>.apps.googleusercontent.com
|
||||||
|
GOOGLE_CLIENT_SECRET=GOCSPX-<你的Client Secret>
|
||||||
|
GOOGLE_PROJECT_ID=<你的项目ID>
|
||||||
|
GOOGLE_REDIRECT_URL=https://ai.pipexerp.com/api/v1/auth/google/callback
|
||||||
|
GOOGLE_CALENDAR_SCOPES=https://www.googleapis.com/auth/calendar
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取 Google OAuth 凭据
|
||||||
|
|
||||||
|
#### 1. 创建 Google Cloud 项目
|
||||||
|
|
||||||
|
1. 访问 [Google Cloud Console](https://console.cloud.google.com/)
|
||||||
|
2. 创建新项目或选择现有项目
|
||||||
|
3. 记录项目 ID
|
||||||
|
|
||||||
|
#### 2. 启用 API
|
||||||
|
|
||||||
|
1. 进入 **APIs & Services** → **Library**
|
||||||
|
2. 搜索并启用 **Google Calendar API**
|
||||||
|
|
||||||
|
#### 3. 配置 OAuth 同意屏幕
|
||||||
|
|
||||||
|
1. 进入 **APIs & Services** → **OAuth consent screen**
|
||||||
|
2. 选择 **External** → 创建
|
||||||
|
3. 填写应用名称、支持邮箱
|
||||||
|
4. 添加 Scopes:
|
||||||
|
- `https://www.googleapis.com/auth/calendar`
|
||||||
|
- `https://www.googleapis.com/auth/calendar.events`
|
||||||
|
5. 添加测试用户(开发阶段)
|
||||||
|
|
||||||
|
#### 4. 创建 OAuth 凭据
|
||||||
|
|
||||||
|
1. 进入 **APIs & Services** → **Credentials**
|
||||||
|
2. 点击 **+ CREATE CREDENTIALS** → **OAuth client ID**
|
||||||
|
3. 选择 **Web application**
|
||||||
|
4. 配置:
|
||||||
|
- **Name**: `AI-Proj Web Client`
|
||||||
|
- **Authorized JavaScript origins**: `https://ai.pipexerp.com`
|
||||||
|
- **Authorized redirect URIs**: `https://ai.pipexerp.com/api/v1/auth/google/callback`
|
||||||
|
5. 复制 Client ID 和 Client Secret
|
||||||
|
|
||||||
|
### 部署配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 更新生产环境配置
|
||||||
|
ssh tools_ai_proj "cd /home/ubuntu/apps/new-ai-proj && \
|
||||||
|
sed -i 's|GOOGLE_CLIENT_ID=.*|GOOGLE_CLIENT_ID=<CLIENT_ID>|' .env && \
|
||||||
|
sed -i 's|GOOGLE_CLIENT_SECRET=.*|GOOGLE_CLIENT_SECRET=<CLIENT_SECRET>|' .env && \
|
||||||
|
sed -i 's|GOOGLE_PROJECT_ID=.*|GOOGLE_PROJECT_ID=<PROJECT_ID>|' .env && \
|
||||||
|
docker restart ai_backend_prod"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据库迁移
|
||||||
|
|
||||||
|
Google 日历集成需要以下表:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- OAuth 状态表(CSRF 保护)
|
||||||
|
CREATE TABLE oauth_states (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
state VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
expires_at TIMESTAMP NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 项目日历配置表
|
||||||
|
-- 见 migrations/20260210_01_project_calendar.sql
|
||||||
|
|
||||||
|
-- 日历 Webhook 表
|
||||||
|
-- 见 migrations/20260211_01_calendar_webhooks.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 当前配置(2026-02)
|
||||||
|
|
||||||
|
| 配置项 | 值 |
|
||||||
|
|--------|-----|
|
||||||
|
| Project ID | `project-bfa64769-1595-4c41-964` |
|
||||||
|
| Client ID | `78721641513-...apps.googleusercontent.com` |
|
||||||
|
| Redirect URL | `https://ai.pipexerp.com/api/v1/auth/google/callback` |
|
||||||
|
|
||||||
|
> **注意**: Client Secret 不在文档中记录,存储在生产环境 `.env` 文件中
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十三、最佳实践与故障排查
|
||||||
|
|
||||||
|
### 13.1 创建需求的最佳实践
|
||||||
|
|
||||||
|
#### 问题:description 字段"请求数据格式错误"
|
||||||
|
|
||||||
|
**症状**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "BAD_REQUEST",
|
||||||
|
"message": "请求数据格式错误"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**常见原因**:
|
||||||
|
|
||||||
|
| 原因 | 解决方案 |
|
||||||
|
|------|----------|
|
||||||
|
| **description 内容过长** | API 可能有字段长度限制,逐步测试找到上限 |
|
||||||
|
| **Markdown 特殊字符** | 检查是否有未转义的特殊字符(如反引号、引号) |
|
||||||
|
| **JSON 转义问题** | 确保 JSON 字符串正确转义(尤其是换行符、引号) |
|
||||||
|
| **多层嵌套的代码块** | Markdown 代码块中的反引号可能导致解析失败 |
|
||||||
|
|
||||||
|
**正确的调试流程**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ❌ 错误做法:直接简化内容
|
||||||
|
"创建失败 → 简化 description → 成功"
|
||||||
|
问题:丢失了重要的需求细节
|
||||||
|
|
||||||
|
# ✅ 正确做法:找到根本原因
|
||||||
|
步骤 1: 测试空描述
|
||||||
|
create_requirement(description="")
|
||||||
|
→ 是否成功?
|
||||||
|
|
||||||
|
步骤 2: 测试纯文本
|
||||||
|
create_requirement(description="简单文本")
|
||||||
|
→ 是否成功?
|
||||||
|
|
||||||
|
步骤 3: 测试简单 Markdown
|
||||||
|
create_requirement(description="# 标题\n\n内容")
|
||||||
|
→ 是否成功?
|
||||||
|
|
||||||
|
步骤 4: 逐步增加复杂度
|
||||||
|
create_requirement(description="包含代码块的Markdown")
|
||||||
|
create_requirement(description="包含表格的Markdown")
|
||||||
|
create_requirement(description="完整的技术方案")
|
||||||
|
→ 找到临界点
|
||||||
|
|
||||||
|
步骤 5: 根据根因选择方案
|
||||||
|
- 如果是长度限制 → 分段或使用附件
|
||||||
|
- 如果是特殊字符 → 转义或替换
|
||||||
|
- 如果是 Markdown → 简化格式或转纯文本
|
||||||
|
```
|
||||||
|
|
||||||
|
**推荐方案**:
|
||||||
|
|
||||||
|
**方案 A:完整的需求 + 关联文档**
|
||||||
|
```typescript
|
||||||
|
// 1. 创建需求(简洁描述)
|
||||||
|
const req = await create_requirement({
|
||||||
|
title: "思源笔记增加 MCP 协议层支持",
|
||||||
|
description: "核心方案:创建独立的 siyuan-mcp-bridge 服务...",
|
||||||
|
projectId: 162
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 创建详细的技术方案文档
|
||||||
|
const doc = await create_requirement_document({
|
||||||
|
requirementId: req.id,
|
||||||
|
title: "技术方案详细设计",
|
||||||
|
content: `
|
||||||
|
# 需求背景
|
||||||
|
...(完整的技术方案,3000+ 字)
|
||||||
|
|
||||||
|
# 架构设计
|
||||||
|
...
|
||||||
|
|
||||||
|
# 实施路线图
|
||||||
|
...
|
||||||
|
`
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**方案 B:分段创建需求描述**
|
||||||
|
```typescript
|
||||||
|
// 1. 创建需求(基本信息)
|
||||||
|
const req = await create_requirement({
|
||||||
|
title: "思源笔记增加 MCP 协议层支持",
|
||||||
|
description: "简短描述",
|
||||||
|
projectId: 162
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 更新需求(逐步添加内容)
|
||||||
|
await update_requirement({
|
||||||
|
id: req.id,
|
||||||
|
updates: {
|
||||||
|
description: description_part1 // 添加背景
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await update_requirement({
|
||||||
|
id: req.id,
|
||||||
|
updates: {
|
||||||
|
description: description_part1 + description_part2 // 添加架构
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 经验总结
|
||||||
|
|
||||||
|
**原则 1:业务优先于技术**
|
||||||
|
- ❌ 为了创建成功而牺牲需求完整性
|
||||||
|
- ✅ 找到技术方案来满足业务需求
|
||||||
|
|
||||||
|
**原则 2:追根溯源**
|
||||||
|
- ❌ 基于假设快速妥协("可能是太长了")
|
||||||
|
- ✅ 通过调试找到真正的错误原因
|
||||||
|
|
||||||
|
**原则 3:给用户选择权**
|
||||||
|
- ❌ 自作主张简化内容
|
||||||
|
- ✅ 询问用户的偏好和优先级
|
||||||
|
|
||||||
|
**原则 4:保留完整信息**
|
||||||
|
- ❌ 简化后的需求缺少关键信息
|
||||||
|
- ✅ 通过关联文档保留详细内容
|
||||||
|
|
||||||
|
### 13.2 需求描述的格式建议
|
||||||
|
|
||||||
|
**推荐的需求描述结构**(简洁版):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 实施周期
|
||||||
|
X周(阶段1 + 阶段2 + 阶段3)
|
||||||
|
```
|
||||||
|
|
||||||
|
**详细内容放在关联文档**:
|
||||||
|
- 需求背景与业务价值
|
||||||
|
- 详细的技术方案
|
||||||
|
- 实施路线图与时间规划
|
||||||
|
- 风险评估与应对措施
|
||||||
|
- 验收标准
|
||||||
|
|
||||||
|
**Markdown 格式注意事项**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
✅ 安全的格式:
|
||||||
|
- 简单的标题(# ## ###)
|
||||||
|
- 无序列表(-)
|
||||||
|
- 有序列表(1. 2. 3.)
|
||||||
|
- 简单的表格
|
||||||
|
- 加粗(**text**)
|
||||||
|
|
||||||
|
⚠️ 可能有问题的格式:
|
||||||
|
- 三重反引号代码块(```)
|
||||||
|
- HTML 标签
|
||||||
|
- 复杂的嵌套列表
|
||||||
|
- 大量的特殊字符
|
||||||
|
- 超长的单行文本
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.3 更新需求的最佳实践
|
||||||
|
|
||||||
|
**使用 update_requirement 补充详细内容**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 创建需求后,逐步更新描述
|
||||||
|
await update_requirement({
|
||||||
|
id: 857, // REQ-20260217-0001
|
||||||
|
updates: {
|
||||||
|
description: `
|
||||||
|
## 需求背景
|
||||||
|
|
||||||
|
### 当前问题
|
||||||
|
思源笔记仅提供 REST API 接口,AI 操作复杂...
|
||||||
|
|
||||||
|
### 业务价值
|
||||||
|
为思源笔记添加 MCP 协议层支持,使其能够...
|
||||||
|
|
||||||
|
## 技术方案
|
||||||
|
|
||||||
|
### 架构设计
|
||||||
|
...(详细内容)
|
||||||
|
`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**测试长度限制**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 逐步增加内容,找到上限
|
||||||
|
第1次: 200字 → 成功
|
||||||
|
第2次: 500字 → 成功
|
||||||
|
第3次: 1000字 → 成功
|
||||||
|
第4次: 2000字 → 成功
|
||||||
|
第5次: 5000字 → 失败 ← 找到上限
|
||||||
|
|
||||||
|
结论: description 字段最大约 3000-4000 字
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.4 故障排查清单
|
||||||
|
|
||||||
|
遇到 API 错误时,按以下步骤排查:
|
||||||
|
|
||||||
|
**步骤 1:分析错误信息**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "BAD_REQUEST",
|
||||||
|
"message": "请求数据格式错误"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- [ ] 错误码是什么?(BAD_REQUEST / VALIDATION_ERROR / AUTH_FAILED)
|
||||||
|
- [ ] 错误信息是否有具体提示?
|
||||||
|
- [ ] 是哪个字段导致的错误?
|
||||||
|
|
||||||
|
**步骤 2:检查参数格式**
|
||||||
|
- [ ] 必填参数是否都提供了?
|
||||||
|
- [ ] 参数类型是否正确?(number vs string)
|
||||||
|
- [ ] 枚举值是否在允许范围内?
|
||||||
|
- [ ] JSON 格式是否正确?
|
||||||
|
|
||||||
|
**步骤 3:简化测试**
|
||||||
|
- [ ] 尝试最小化的参数集
|
||||||
|
- [ ] 逐个添加可选参数
|
||||||
|
- [ ] 找到导致错误的具体参数
|
||||||
|
|
||||||
|
**步骤 4:查看后端日志**(如有权限)
|
||||||
|
```bash
|
||||||
|
# 生产环境日志
|
||||||
|
ssh tools_ai_proj "docker logs ai_backend_prod --tail 100"
|
||||||
|
|
||||||
|
# 开发环境日志
|
||||||
|
cd ~/coding/new-ai-proj/backend
|
||||||
|
tail -f logs/app.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**步骤 5:参考类似的成功案例**
|
||||||
|
- [ ] 查看 SKILL.md 中的示例
|
||||||
|
- [ ] 查看之前成功的调用记录
|
||||||
|
- [ ] 对比参数差异
|
||||||
|
|
||||||
|
### 13.5 常见错误与解决方案
|
||||||
|
|
||||||
|
| 错误 | 可能原因 | 解决方案 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| `BAD_REQUEST` | 参数格式错误 | 检查参数类型和 JSON 格式 |
|
||||||
|
| `VALIDATION_ERROR` | 参数验证失败 | 检查必填字段和枚举值 |
|
||||||
|
| `AUTH_FAILED` | 认证失败 | 检查 Token 是否过期 |
|
||||||
|
| `PERMISSION_DENIED` | 权限不足 | 检查用户角色和权限 |
|
||||||
|
| `NOT_FOUND` | 资源不存在 | 检查 ID 是否正确 |
|
||||||
|
| `INTERNAL_ERROR` | 服务器错误 | 查看后端日志排查 |
|
||||||
|
|
||||||
|
### 13.6 需求 description 长度测试记录
|
||||||
|
|
||||||
|
**测试日期**: 2026-02-17
|
||||||
|
|
||||||
|
| 长度 | 格式 | 结果 | 备注 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| ~200字 | 简单文本 | ✅ 成功 | 核心方案3点 |
|
||||||
|
| ~3000字 | 完整 Markdown | ❌ 失败 | "请求数据格式错误" |
|
||||||
|
| 待测试 | 1000字 Markdown | ? | 需要进一步测试 |
|
||||||
|
| 待测试 | 2000字 Markdown | ? | 需要进一步测试 |
|
||||||
|
|
||||||
|
**建议**:
|
||||||
|
- 需求描述控制在 1000 字以内
|
||||||
|
- 详细内容使用关联文档
|
||||||
|
- 复杂格式改为纯文本
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: publish
|
name: publish
|
||||||
description: 发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。
|
description: 发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。
|
||||||
user_invocable: true
|
|
||||||
metadata:
|
metadata:
|
||||||
|
user_invocable: true
|
||||||
invocation: user-invoked
|
invocation: user-invoked
|
||||||
disable-model-invocation: true
|
disable-model-invocation: true
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ Agent 之间的控制权转移机制:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
/swarm start "优化数据库查询性能" \
|
/swarm start "优化数据库查询性能" \
|
||||||
--context project=/Users/coolbuy-dev/coding/new-ai-proj \
|
--context project=~/coding/new-ai-proj \
|
||||||
--context stack=Go,PostgreSQL,Redis \
|
--context stack=Go,PostgreSQL,Redis \
|
||||||
--context module=backend/services
|
--context module=backend/services
|
||||||
```
|
```
|
||||||
@@ -223,7 +223,7 @@ agents:
|
|||||||
max_turns: 5
|
max_turns: 5
|
||||||
|
|
||||||
context_variables:
|
context_variables:
|
||||||
project_root: /Users/coolbuy-dev/coding/new-ai-proj
|
project_root: ~/coding/new-ai-proj
|
||||||
backend_lang: Go 1.21
|
backend_lang: Go 1.21
|
||||||
frontend_framework: Vue 3
|
frontend_framework: Vue 3
|
||||||
database: PostgreSQL 15
|
database: PostgreSQL 15
|
||||||
@@ -288,7 +288,7 @@ handoff_rules:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# OpenClaw 调用 Melbourne Claude Code
|
# OpenClaw 调用 Melbourne Claude Code
|
||||||
ssh melbourne "cd /Users/coolbuy-dev/coding/new-ai-proj && \
|
ssh melbourne "cd ~/coding/new-ai-proj && \
|
||||||
/opt/homebrew/bin/claude --dangerously-skip-permissions \
|
/opt/homebrew/bin/claude --dangerously-skip-permissions \
|
||||||
-p '/swarm start 实现任务批量删除功能'"
|
-p '/swarm start 实现任务批量删除功能'"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: ai-chat
|
name: ai-chat
|
||||||
description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。
|
description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。
|
||||||
arguments: <subcommand> [args]
|
metadata:
|
||||||
|
arguments: <subcommand> [args]
|
||||||
---
|
---
|
||||||
|
|
||||||
# AI Chat Skill
|
# AI Chat Skill
|
||||||
|
|||||||
@@ -629,8 +629,8 @@ fi
|
|||||||
### 正确做法(两行 export,别多一步)
|
### 正确做法(两行 export,别多一步)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export HOME="/Users/johnq" # 必须补:git 等直接读 $HOME
|
export HOME="~" # 必须补:git 等直接读 $HOME
|
||||||
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:$PATH" # node/npm/pnpm
|
export PATH="~/.local/node-v22.22.3-darwin-arm64/bin:$PATH" # node/npm/pnpm
|
||||||
cd <项目> && npm install && npm run build
|
cd <项目> && npm install && npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -643,11 +643,11 @@ cd <项目> && npm install && npm run build
|
|||||||
→ **「node 能跑」≠ 环境完整**,别据此跳过 `HOME`。
|
→ **「node 能跑」≠ 环境完整**,别据此跳过 `HOME`。
|
||||||
|
|
||||||
- **本机 node 必须用这一支**(定义在 `~/.zprofile`,对齐项目 `engines.node`):
|
- **本机 node 必须用这一支**(定义在 `~/.zprofile`,对齐项目 `engines.node`):
|
||||||
`/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin/node` = v22.22.3,npm 10.9.8。
|
`~/.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` 不存在。
|
- `/opt/homebrew/bin/node` 是**另一支 v26**,不要混用;`/usr/local/bin/node` 不存在。
|
||||||
- 备选①:`/bin/zsh -lic '<命令>'`(登录 zsh 会读 `~/.zprofile`)。
|
- 备选①:`/bin/zsh -lic '<命令>'`(登录 zsh 会读 `~/.zprofile`)。
|
||||||
- 备选②:绝对路径直调 `/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin/node build.mjs`。
|
- 备选②:绝对路径直调 `~/.local/node-v22.22.3-darwin-arm64/bin/node build.mjs`。
|
||||||
- 路径变了先 `ls -d /Users/johnq/.local/node-v*-darwin-arm64/bin` 重新取**真实版本号**,不要猜。
|
- 路径变了先 `ls -d ~/.local/node-v*-darwin-arm64/bin` 重新取**真实版本号**,不要猜。
|
||||||
|
|
||||||
### 交付纪律(配合「Step 0:验证优先」)
|
### 交付纪律(配合「Step 0:验证优先」)
|
||||||
|
|
||||||
@@ -657,6 +657,6 @@ cd <项目> && npm install && npm run build
|
|||||||
- 校验环境的最快方式(输出直接贴进交付):
|
- 校验环境的最快方式(输出直接贴进交付):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export HOME="/Users/johnq"; export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:$PATH"
|
export HOME="~"; export PATH="~/.local/node-v22.22.3-darwin-arm64/bin:$PATH"
|
||||||
node -v && npm -v && git status --short | head -3
|
node -v && npm -v && git status --short | head -3
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
name: dev-deploy
|
name: dev-deploy
|
||||||
description: 应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。
|
description: 应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。
|
||||||
---
|
---
|
||||||
|
|
||||||
# 应用部署 Skill (dev-deploy)
|
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
管理应用从构建到发布的完整部署流程,支持多平台:
|
管理应用从构建到发布的完整部署流程,支持多平台:
|
||||||
@@ -558,186 +555,6 @@ preflight_docker() {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 回滚策略
|
|
||||||
|
|
||||||
### iOS TestFlight 回滚
|
|
||||||
|
|
||||||
TestFlight **无法真正回滚**已安装的版本,但有以下应急手段:
|
|
||||||
|
|
||||||
| 手段 | 说明 | API |
|
|
||||||
|------|------|-----|
|
|
||||||
| 停止分发 | 将 build 从测试中移除,用户不再收到更新 | `PATCH /v1/builds/{id}` 设置 `expired: true` |
|
|
||||||
| 过期 build | 强制过期有问题的 build | 同上 |
|
|
||||||
| 紧急热修 | 构建新版本覆盖上线 | 常规部署流程 |
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 通过 ASC API 停止分发某个 build
|
|
||||||
curl -X PATCH "https://api.appstoreconnect.apple.com/v1/builds/$BUILD_ID" \
|
|
||||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"data":{"type":"builds","id":"'$BUILD_ID'","attributes":{"expired":true}}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker 回滚
|
|
||||||
|
|
||||||
Docker 回滚相对简单,拉取上一个正常版本的镜像重新部署即可:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. 确定上一个正常的 tag
|
|
||||||
PREVIOUS_TAG=<previous-good-tag>
|
|
||||||
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
|
||||||
|
|
||||||
# 2. 在服务器上回滚
|
|
||||||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
|
||||||
cd /opt/xiaoqu/production # 或 /opt/xiaoqu/staging
|
|
||||||
export IMAGE_TAG=$PREVIOUS_TAG
|
|
||||||
docker compose pull
|
|
||||||
docker compose up -d
|
|
||||||
"
|
|
||||||
|
|
||||||
# 3. 验证回滚成功
|
|
||||||
curl -sf http://39.104.87.246/health && echo 'Rollback OK'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 数据库回滚注意事项
|
|
||||||
|
|
||||||
| 场景 | 策略 |
|
|
||||||
|------|------|
|
|
||||||
| 可逆 migration(加列、加表) | 部署回滚后数据库无需回滚,旧代码忽略新列 |
|
|
||||||
| 不可逆 migration(删列、改类型) | **必须先回滚 migration 再回滚代码**,否则旧代码报错 |
|
|
||||||
| 数据 migration | 评估是否需要补偿脚本,建议 migration 前做备份快照 |
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 数据库 migration 回滚示例(如果使用 golang-migrate)
|
|
||||||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
|
||||||
docker compose exec gateway migrate -path /migrations -database \$DATABASE_URL down 1
|
|
||||||
"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 部署监控
|
|
||||||
|
|
||||||
### Post-deploy 健康检查模式
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 通用部署后验证函数
|
|
||||||
post_deploy_verify() {
|
|
||||||
local url=$1
|
|
||||||
local max_retries=${2:-5}
|
|
||||||
local interval=${3:-10}
|
|
||||||
|
|
||||||
echo "Verifying deployment at $url ..."
|
|
||||||
for i in $(seq 1 $max_retries); do
|
|
||||||
local status
|
|
||||||
status=$(curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
|
||||||
if [ "$status" = "200" ]; then
|
|
||||||
echo "Health check passed (attempt $i/$max_retries)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
echo "Attempt $i/$max_retries: status=$status, retrying in ${interval}s..."
|
|
||||||
sleep $interval
|
|
||||||
done
|
|
||||||
echo "Health check FAILED after $max_retries attempts"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
post_deploy_verify "http://39.104.87.246/health" 5 10
|
|
||||||
```
|
|
||||||
|
|
||||||
### 飞书通知模板
|
|
||||||
|
|
||||||
部署完成后通过飞书 Webhook 发送通知:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 部署成功通知
|
|
||||||
send_feishu_deploy_notification() {
|
|
||||||
local env=$1 # staging / production
|
|
||||||
local version=$2 # 版本号或 tag
|
|
||||||
local status=$3 # success / failure
|
|
||||||
local detail=$4 # 额外说明
|
|
||||||
|
|
||||||
local WEBHOOK_URL="<飞书群 Webhook 地址>"
|
|
||||||
|
|
||||||
if [ "$status" = "success" ]; then
|
|
||||||
local color="green"
|
|
||||||
local emoji="✅"
|
|
||||||
else
|
|
||||||
local color="red"
|
|
||||||
local emoji="❌"
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -s -X POST "$WEBHOOK_URL" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"msg_type": "interactive",
|
|
||||||
"card": {
|
|
||||||
"header": {
|
|
||||||
"title": {"tag": "plain_text", "content": "'"$emoji"' 部署通知 - '"$env"'"},
|
|
||||||
"template": "'"$color"'"
|
|
||||||
},
|
|
||||||
"elements": [
|
|
||||||
{"tag": "div", "text": {"tag": "lark_md", "content": "**环境**: '"$env"'\n**版本**: '"$version"'\n**状态**: '"$status"'\n**时间**: '"$(date '+%Y-%m-%d %H:%M:%S')"'\n**详情**: '"$detail"'"}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
}
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
send_feishu_deploy_notification "production" "v1.2.3" "success" "Gateway + Web 部署完成"
|
|
||||||
send_feishu_deploy_notification "staging" "abc12345" "failure" "Health check 超时"
|
|
||||||
```
|
|
||||||
|
|
||||||
### iOS TestFlight 构建状态监控
|
|
||||||
|
|
||||||
通过 ASC API 持续监控 build 处理状态:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 监控 TestFlight build 处理状态
|
|
||||||
monitor_testflight_build() {
|
|
||||||
local build_id=$1
|
|
||||||
local jwt_token=$2
|
|
||||||
local max_wait=600 # 最长等待 10 分钟
|
|
||||||
local elapsed=0
|
|
||||||
|
|
||||||
while [ $elapsed -lt $max_wait ]; do
|
|
||||||
local response
|
|
||||||
response=$(curl -s "https://api.appstoreconnect.apple.com/v1/builds/$build_id" \
|
|
||||||
-H "Authorization: Bearer $jwt_token")
|
|
||||||
|
|
||||||
local state
|
|
||||||
state=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['attributes']['processingState'])" 2>/dev/null)
|
|
||||||
|
|
||||||
echo "[$(date '+%H:%M:%S')] Build $build_id: $state"
|
|
||||||
|
|
||||||
case "$state" in
|
|
||||||
VALID)
|
|
||||||
echo "Build 处理完成,可用于测试"
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
FAILED|INVALID)
|
|
||||||
echo "Build 处理失败: $state"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
PROCESSING)
|
|
||||||
sleep 30
|
|
||||||
((elapsed+=30))
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
sleep 15
|
|
||||||
((elapsed+=15))
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Build 处理超时(${max_wait}s)"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 与需求工作流集成
|
## 与需求工作流集成
|
||||||
|
|
||||||
部署完成后更新需求状态:
|
部署完成后更新需求状态:
|
||||||
@@ -774,3 +591,10 @@ ai-proj task append-doc --id <task_id> --content "部署记录..."
|
|||||||
| 10 | App Store 图标为空 | 需将 build 关联到 App Store 版本(PATCH relationships/build) |
|
| 10 | App Store 图标为空 | 需将 build 关联到 App Store 版本(PATCH relationships/build) |
|
||||||
| 11 | SSH 长连接断开 | xcodebuild 3-4 分钟无输出,Tailscale 断连。用 nohup 后台执行 |
|
| 11 | SSH 长连接断开 | xcodebuild 3-4 分钟无输出,Tailscale 断连。用 nohup 后台执行 |
|
||||||
| 12 | xcodegen 后 cwd 错乱 | `cd ios && xcodegen && cd ..` 失败时不回退。用 subshell `(cd ios && xcodegen)` |
|
| 12 | xcodegen 后 cwd 错乱 | `cd ios && xcodegen && cd ..` 失败时不回退。用 subshell `(cd ios && xcodegen)` |
|
||||||
|
|
||||||
|
## 详细内容
|
||||||
|
|
||||||
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
|
- 回滚策略
|
||||||
|
- 部署监控
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 回滚策略
|
||||||
|
|
||||||
|
### iOS TestFlight 回滚
|
||||||
|
|
||||||
|
TestFlight **无法真正回滚**已安装的版本,但有以下应急手段:
|
||||||
|
|
||||||
|
| 手段 | 说明 | API |
|
||||||
|
|------|------|-----|
|
||||||
|
| 停止分发 | 将 build 从测试中移除,用户不再收到更新 | `PATCH /v1/builds/{id}` 设置 `expired: true` |
|
||||||
|
| 过期 build | 强制过期有问题的 build | 同上 |
|
||||||
|
| 紧急热修 | 构建新版本覆盖上线 | 常规部署流程 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 通过 ASC API 停止分发某个 build
|
||||||
|
curl -X PATCH "https://api.appstoreconnect.apple.com/v1/builds/$BUILD_ID" \
|
||||||
|
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"data":{"type":"builds","id":"'$BUILD_ID'","attributes":{"expired":true}}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker 回滚
|
||||||
|
|
||||||
|
Docker 回滚相对简单,拉取上一个正常版本的镜像重新部署即可:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 确定上一个正常的 tag
|
||||||
|
PREVIOUS_TAG=<previous-good-tag>
|
||||||
|
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
||||||
|
|
||||||
|
# 2. 在服务器上回滚
|
||||||
|
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||||||
|
cd /opt/xiaoqu/production # 或 /opt/xiaoqu/staging
|
||||||
|
export IMAGE_TAG=$PREVIOUS_TAG
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
"
|
||||||
|
|
||||||
|
# 3. 验证回滚成功
|
||||||
|
curl -sf http://39.104.87.246/health && echo 'Rollback OK'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据库回滚注意事项
|
||||||
|
|
||||||
|
| 场景 | 策略 |
|
||||||
|
|------|------|
|
||||||
|
| 可逆 migration(加列、加表) | 部署回滚后数据库无需回滚,旧代码忽略新列 |
|
||||||
|
| 不可逆 migration(删列、改类型) | **必须先回滚 migration 再回滚代码**,否则旧代码报错 |
|
||||||
|
| 数据 migration | 评估是否需要补偿脚本,建议 migration 前做备份快照 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 数据库 migration 回滚示例(如果使用 golang-migrate)
|
||||||
|
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||||||
|
docker compose exec gateway migrate -path /migrations -database \$DATABASE_URL down 1
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 部署监控
|
||||||
|
|
||||||
|
### Post-deploy 健康检查模式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 通用部署后验证函数
|
||||||
|
post_deploy_verify() {
|
||||||
|
local url=$1
|
||||||
|
local max_retries=${2:-5}
|
||||||
|
local interval=${3:-10}
|
||||||
|
|
||||||
|
echo "Verifying deployment at $url ..."
|
||||||
|
for i in $(seq 1 $max_retries); do
|
||||||
|
local status
|
||||||
|
status=$(curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
||||||
|
if [ "$status" = "200" ]; then
|
||||||
|
echo "Health check passed (attempt $i/$max_retries)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "Attempt $i/$max_retries: status=$status, retrying in ${interval}s..."
|
||||||
|
sleep $interval
|
||||||
|
done
|
||||||
|
echo "Health check FAILED after $max_retries attempts"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 使用示例
|
||||||
|
post_deploy_verify "http://39.104.87.246/health" 5 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### 飞书通知模板
|
||||||
|
|
||||||
|
部署完成后通过飞书 Webhook 发送通知:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 部署成功通知
|
||||||
|
send_feishu_deploy_notification() {
|
||||||
|
local env=$1 # staging / production
|
||||||
|
local version=$2 # 版本号或 tag
|
||||||
|
local status=$3 # success / failure
|
||||||
|
local detail=$4 # 额外说明
|
||||||
|
|
||||||
|
local WEBHOOK_URL="<飞书群 Webhook 地址>"
|
||||||
|
|
||||||
|
if [ "$status" = "success" ]; then
|
||||||
|
local color="green"
|
||||||
|
local emoji="✅"
|
||||||
|
else
|
||||||
|
local color="red"
|
||||||
|
local emoji="❌"
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl -s -X POST "$WEBHOOK_URL" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"msg_type": "interactive",
|
||||||
|
"card": {
|
||||||
|
"header": {
|
||||||
|
"title": {"tag": "plain_text", "content": "'"$emoji"' 部署通知 - '"$env"'"},
|
||||||
|
"template": "'"$color"'"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{"tag": "div", "text": {"tag": "lark_md", "content": "**环境**: '"$env"'\n**版本**: '"$version"'\n**状态**: '"$status"'\n**时间**: '"$(date '+%Y-%m-%d %H:%M:%S')"'\n**详情**: '"$detail"'"}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 使用示例
|
||||||
|
send_feishu_deploy_notification "production" "v1.2.3" "success" "Gateway + Web 部署完成"
|
||||||
|
send_feishu_deploy_notification "staging" "abc12345" "failure" "Health check 超时"
|
||||||
|
```
|
||||||
|
|
||||||
|
### iOS TestFlight 构建状态监控
|
||||||
|
|
||||||
|
通过 ASC API 持续监控 build 处理状态:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 监控 TestFlight build 处理状态
|
||||||
|
monitor_testflight_build() {
|
||||||
|
local build_id=$1
|
||||||
|
local jwt_token=$2
|
||||||
|
local max_wait=600 # 最长等待 10 分钟
|
||||||
|
local elapsed=0
|
||||||
|
|
||||||
|
while [ $elapsed -lt $max_wait ]; do
|
||||||
|
local response
|
||||||
|
response=$(curl -s "https://api.appstoreconnect.apple.com/v1/builds/$build_id" \
|
||||||
|
-H "Authorization: Bearer $jwt_token")
|
||||||
|
|
||||||
|
local state
|
||||||
|
state=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['attributes']['processingState'])" 2>/dev/null)
|
||||||
|
|
||||||
|
echo "[$(date '+%H:%M:%S')] Build $build_id: $state"
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
VALID)
|
||||||
|
echo "Build 处理完成,可用于测试"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
FAILED|INVALID)
|
||||||
|
echo "Build 处理失败: $state"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
PROCESSING)
|
||||||
|
sleep 30
|
||||||
|
((elapsed+=30))
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
sleep 15
|
||||||
|
((elapsed+=15))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Build 处理超时(${max_wait}s)"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: frontend-design
|
name: frontend-design
|
||||||
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
|
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
|
||||||
arguments: "[component|page|storybook] <description>"
|
metadata:
|
||||||
|
arguments: "[component|page|storybook] <description>"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Frontend Design 前端设计技能
|
# Frontend Design 前端设计技能
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/C
|
|||||||
驱动 Slark 从改代码到上线的端到端流程。权威流程与完整细节见仓库文档
|
驱动 Slark 从改代码到上线的端到端流程。权威流程与完整细节见仓库文档
|
||||||
`docs/cicd-staging-production.md`(本技能命令与之一致);本文件是可直接照做的分阶段清单。
|
`docs/cicd-staging-production.md`(本技能命令与之一致);本文件是可直接照做的分阶段清单。
|
||||||
|
|
||||||
仓库:`qiudl/qiu-slark`(本机克隆通常在 `/Users/donglinlai/coding/slark`)。所有命令在**仓库根**执行。
|
仓库:`qiudl/qiu-slark`(本机克隆通常在 `~/coding/slark`)。所有命令在**仓库根**执行。
|
||||||
`ai-proj` 相关命令加 `GODEBUG=netdns=go` 前缀。
|
`ai-proj` 相关命令加 `GODEBUG=netdns=go` 前缀。
|
||||||
|
|
||||||
## 铁律(先记死)
|
## 铁律(先记死)
|
||||||
@@ -192,9 +192,9 @@ REQ-20260819-0005 / Desktop 0.3.1:
|
|||||||
- m5max 原生构建、Developer ID 签名并公证/staple macOS DMG/ZIP;交叉构建 Windows x64 NSIS EXE。
|
- m5max 原生构建、Developer ID 签名并公证/staple macOS DMG/ZIP;交叉构建 Windows x64 NSIS EXE。
|
||||||
- Darwin 不能执行 Windows 原生 `smoke:windows`。只有发布负责人明确接手 Windows 原生验收时才能继续交叉打包,交付证据必须标注该边界。
|
- Darwin 不能执行 Windows 原生 `smoke:windows`。只有发布负责人明确接手 Windows 原生验收时才能继续交叉打包,交付证据必须标注该边界。
|
||||||
- 资产不回传开发机;由 m5max 直接上传 `xiaoqu-public-file/slark/desktop/releases/v<version>/`,manifest 最后更新。
|
- 资产不回传开发机;由 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 本地配置;只有该文件缺失、权限不符或标签解析为空时才停止并报告。
|
- `xiaoqu-public-file` 的 canonical 凭证源是发布控制机上的 `~/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 不持久化 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 非交互 SSH 必须显式补 `PATH=/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$PATH`;已验证 `ossutil` 位于 `~/.local/bin/ossutil`。执行前检查 Node、三个安装包、精确 main SHA 和干净工作树,避免因交互 shell 环境差异重新猜路径。
|
||||||
- m5max 默认出口若走 `utun`,只为当次 `ossutil` 使用 `--bind-address "$(ipconfig getifaddr en0)"`,不得修改整机 Tailscale/默认路由。
|
- m5max 默认出口若走 `utun`,只为当次 `ossutil` 使用 `--bind-address "$(ipconfig getifaddr en0)"`,不得修改整机 Tailscale/默认路由。
|
||||||
- 完成前核对三份资产 SHA256/大小、macOS 签名与公证、公开 URL/manifest,并回写 ai-proj 证据。
|
- 完成前核对三份资产 SHA256/大小、macOS 签名与公证、公开 URL/manifest,并回写 ai-proj 证据。
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ gh api repos/<owner>/<repo> --jq '.permissions' # 关键:{"pull":true,"push":
|
|||||||
## 阶段 1:发布线 A — DSH 插件(有上游写权限)
|
## 阶段 1:发布线 A — DSH 插件(有上游写权限)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export HOME="/Users/johnq"
|
export HOME="~"
|
||||||
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
|
export PATH="~/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
|
||||||
cd <repo>/<pkg>
|
cd <repo>/<pkg>
|
||||||
npm run check && npm run test:gate && npm run pack
|
npm run check && npm run test:gate && npm run pack
|
||||||
shasum -a 256 dist/v<ver>/*.tgz # 两个文件必须同 sha
|
shasum -a 256 dist/v<ver>/*.tgz # 两个文件必须同 sha
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
## 0. 变量与前置
|
## 0. 变量与前置
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export HOME="/Users/johnq"
|
export HOME="~"
|
||||||
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
|
export PATH="~/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
|
||||||
|
|
||||||
UP=<upstream-owner>/<repo> # 例如 Aisland-SJL/dsh-worktable(外部作者,无写权限)
|
UP=<upstream-owner>/<repo> # 例如 Aisland-SJL/dsh-worktable(外部作者,无写权限)
|
||||||
ME=<your-account> # 例如 qiudl
|
ME=<your-account> # 例如 qiudl
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ git status # 应该看不到 setup_env.local.sh
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 确保您创建了本地配置文件
|
# 1. 确保您创建了本地配置文件
|
||||||
cd /Users/junhuang/coolbuy/claude-marketplace/plugins/doubao-voice-plugin/scripts
|
cd ~/coolbuy/claude-marketplace/plugins/doubao-voice-plugin/scripts
|
||||||
cp setup_env.local.sh.example setup_env.local.sh
|
cp setup_env.local.sh.example setup_env.local.sh
|
||||||
# 编辑 setup_env.local.sh,填入您的凭证
|
# 编辑 setup_env.local.sh,填入您的凭证
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ ZHIYUN_APP_SECRET = "sDfhjG7QT1S4gfHiMVYSygmPQPN1R2Ho"
|
|||||||
BASE_URL = "https://open.feishu.cn/open-apis"
|
BASE_URL = "https://open.feishu.cn/open-apis"
|
||||||
|
|
||||||
# 源文件
|
# 源文件
|
||||||
SOURCE_FILE = "/Users/donglinlai/Downloads/酷采团购系统优化进度表.dbt.xlsx"
|
SOURCE_FILE = "$HOME/Downloads/酷采团购系统优化进度表.dbt.xlsx"
|
||||||
|
|
||||||
|
|
||||||
class FeishuBitable:
|
class FeishuBitable:
|
||||||
|
|||||||
@@ -1,212 +1,212 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
上传用户指定的图片到飞书云文档
|
上传用户指定的图片到飞书云文档
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
ZHIYUN_APP_ID = "cli_a9f29dca82b9dbef"
|
ZHIYUN_APP_ID = "cli_a9f29dca82b9dbef"
|
||||||
ZHIYUN_APP_SECRET = "sDfhjG7QT1S4gfHiMVYSygmPQPN1R2Ho"
|
ZHIYUN_APP_SECRET = "sDfhjG7QT1S4gfHiMVYSygmPQPN1R2Ho"
|
||||||
BASE_URL = "https://open.feishu.cn/open-apis"
|
BASE_URL = "https://open.feishu.cn/open-apis"
|
||||||
|
|
||||||
# 用户指定的图片
|
# 用户指定的图片
|
||||||
IMAGE_PATH = "/Users/donglinlai/Downloads/u274.png"
|
IMAGE_PATH = "$HOME/Downloads/u274.png"
|
||||||
|
|
||||||
_token = None
|
_token = None
|
||||||
_token_expires = None
|
_token_expires = None
|
||||||
|
|
||||||
|
|
||||||
def get_token():
|
def get_token():
|
||||||
global _token, _token_expires
|
global _token, _token_expires
|
||||||
if _token and _token_expires and datetime.now() < _token_expires:
|
if _token and _token_expires and datetime.now() < _token_expires:
|
||||||
return _token
|
return _token
|
||||||
|
|
||||||
url = f"{BASE_URL}/auth/v3/tenant_access_token/internal"
|
url = f"{BASE_URL}/auth/v3/tenant_access_token/internal"
|
||||||
response = requests.post(url, json={
|
response = requests.post(url, json={
|
||||||
"app_id": ZHIYUN_APP_ID,
|
"app_id": ZHIYUN_APP_ID,
|
||||||
"app_secret": ZHIYUN_APP_SECRET
|
"app_secret": ZHIYUN_APP_SECRET
|
||||||
})
|
})
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if data.get("code") != 0:
|
if data.get("code") != 0:
|
||||||
raise Exception(f"获取 token 失败: {data}")
|
raise Exception(f"获取 token 失败: {data}")
|
||||||
|
|
||||||
_token = data["tenant_access_token"]
|
_token = data["tenant_access_token"]
|
||||||
_token_expires = datetime.now() + timedelta(seconds=data.get("expire", 7200) - 60)
|
_token_expires = datetime.now() + timedelta(seconds=data.get("expire", 7200) - 60)
|
||||||
return _token
|
return _token
|
||||||
|
|
||||||
|
|
||||||
def headers():
|
def headers():
|
||||||
return {
|
return {
|
||||||
"Authorization": f"Bearer {get_token()}",
|
"Authorization": f"Bearer {get_token()}",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def set_document_permission(document_id: str, editable: bool = True):
|
def set_document_permission(document_id: str, editable: bool = True):
|
||||||
"""
|
"""
|
||||||
设置文档权限
|
设置文档权限
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
document_id: 文档ID
|
document_id: 文档ID
|
||||||
editable: True=组织内可编辑, False=组织内只读
|
editable: True=组织内可编辑, False=组织内只读
|
||||||
"""
|
"""
|
||||||
url = f"{BASE_URL}/drive/v1/permissions/{document_id}/public"
|
url = f"{BASE_URL}/drive/v1/permissions/{document_id}/public"
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"external_access_entity": "open",
|
"external_access_entity": "open",
|
||||||
"security_entity": "anyone_can_view",
|
"security_entity": "anyone_can_view",
|
||||||
"comment_entity": "anyone_can_view",
|
"comment_entity": "anyone_can_view",
|
||||||
"share_entity": "anyone",
|
"share_entity": "anyone",
|
||||||
"link_share_entity": "tenant_editable" if editable else "tenant_readable",
|
"link_share_entity": "tenant_editable" if editable else "tenant_readable",
|
||||||
"invite_external": False
|
"invite_external": False
|
||||||
}
|
}
|
||||||
|
|
||||||
response = requests.patch(url, headers=headers(), params={"type": "docx"}, json=payload)
|
response = requests.patch(url, headers=headers(), params={"type": "docx"}, json=payload)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
if data.get("code") == 0:
|
if data.get("code") == 0:
|
||||||
print(f" 权限设置成功: {'组织内可编辑' if editable else '组织内只读'}")
|
print(f" 权限设置成功: {'组织内可编辑' if editable else '组织内只读'}")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print(f" [WARN] 权限设置失败: {data.get('msg')}")
|
print(f" [WARN] 权限设置失败: {data.get('msg')}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def create_document(title: str, editable: bool = True):
|
def create_document(title: str, editable: bool = True):
|
||||||
"""创建文档并设置权限"""
|
"""创建文档并设置权限"""
|
||||||
url = f"{BASE_URL}/docx/v1/documents"
|
url = f"{BASE_URL}/docx/v1/documents"
|
||||||
response = requests.post(url, headers=headers(), json={"title": title})
|
response = requests.post(url, headers=headers(), json={"title": title})
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if data.get("code") != 0:
|
if data.get("code") != 0:
|
||||||
raise Exception(f"创建文档失败: {data}")
|
raise Exception(f"创建文档失败: {data}")
|
||||||
|
|
||||||
doc = data["data"]["document"]
|
doc = data["data"]["document"]
|
||||||
document_id = doc["document_id"]
|
document_id = doc["document_id"]
|
||||||
|
|
||||||
# 自动设置权限
|
# 自动设置权限
|
||||||
if editable:
|
if editable:
|
||||||
set_document_permission(document_id, editable=True)
|
set_document_permission(document_id, editable=True)
|
||||||
|
|
||||||
return document_id
|
return document_id
|
||||||
|
|
||||||
|
|
||||||
def create_image_block(document_id: str):
|
def create_image_block(document_id: str):
|
||||||
"""创建空图片块"""
|
"""创建空图片块"""
|
||||||
url = f"{BASE_URL}/docx/v1/documents/{document_id}/blocks/{document_id}/children"
|
url = f"{BASE_URL}/docx/v1/documents/{document_id}/blocks/{document_id}/children"
|
||||||
payload = {
|
payload = {
|
||||||
"children": [{
|
"children": [{
|
||||||
"block_type": 27,
|
"block_type": 27,
|
||||||
"image": {}
|
"image": {}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
response = requests.post(url, headers=headers(), json=payload)
|
response = requests.post(url, headers=headers(), json=payload)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if data.get("code") != 0:
|
if data.get("code") != 0:
|
||||||
raise Exception(f"创建图片块失败: {data}")
|
raise Exception(f"创建图片块失败: {data}")
|
||||||
return data["data"]["children"][0]["block_id"]
|
return data["data"]["children"][0]["block_id"]
|
||||||
|
|
||||||
|
|
||||||
def upload_image(file_path: str, block_id: str):
|
def upload_image(file_path: str, block_id: str):
|
||||||
"""上传图片"""
|
"""上传图片"""
|
||||||
url = f"{BASE_URL}/drive/v1/medias/upload_all"
|
url = f"{BASE_URL}/drive/v1/medias/upload_all"
|
||||||
|
|
||||||
file_name = os.path.basename(file_path)
|
file_name = os.path.basename(file_path)
|
||||||
file_size = os.path.getsize(file_path)
|
file_size = os.path.getsize(file_path)
|
||||||
|
|
||||||
# 根据扩展名设置 MIME 类型
|
# 根据扩展名设置 MIME 类型
|
||||||
ext = file_name.lower().split('.')[-1]
|
ext = file_name.lower().split('.')[-1]
|
||||||
mime_types = {
|
mime_types = {
|
||||||
'png': 'image/png',
|
'png': 'image/png',
|
||||||
'jpg': 'image/jpeg',
|
'jpg': 'image/jpeg',
|
||||||
'jpeg': 'image/jpeg',
|
'jpeg': 'image/jpeg',
|
||||||
'gif': 'image/gif',
|
'gif': 'image/gif',
|
||||||
'webp': 'image/webp'
|
'webp': 'image/webp'
|
||||||
}
|
}
|
||||||
mime_type = mime_types.get(ext, 'image/png')
|
mime_type = mime_types.get(ext, 'image/png')
|
||||||
|
|
||||||
with open(file_path, 'rb') as f:
|
with open(file_path, 'rb') as f:
|
||||||
files = {'file': (file_name, f, mime_type)}
|
files = {'file': (file_name, f, mime_type)}
|
||||||
data = {
|
data = {
|
||||||
'file_name': file_name,
|
'file_name': file_name,
|
||||||
'parent_type': 'docx_image',
|
'parent_type': 'docx_image',
|
||||||
'parent_node': block_id,
|
'parent_node': block_id,
|
||||||
'size': str(file_size)
|
'size': str(file_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url,
|
url,
|
||||||
headers={"Authorization": f"Bearer {get_token()}"},
|
headers={"Authorization": f"Bearer {get_token()}"},
|
||||||
files=files,
|
files=files,
|
||||||
data=data
|
data=data
|
||||||
)
|
)
|
||||||
|
|
||||||
result = response.json()
|
result = response.json()
|
||||||
if result.get("code") != 0:
|
if result.get("code") != 0:
|
||||||
raise Exception(f"上传失败: {result}")
|
raise Exception(f"上传失败: {result}")
|
||||||
|
|
||||||
return result["data"]["file_token"]
|
return result["data"]["file_token"]
|
||||||
|
|
||||||
|
|
||||||
def bind_image(document_id: str, block_id: str, file_token: str):
|
def bind_image(document_id: str, block_id: str, file_token: str):
|
||||||
"""绑定图片到图片块"""
|
"""绑定图片到图片块"""
|
||||||
url = f"{BASE_URL}/docx/v1/documents/{document_id}/blocks/{block_id}"
|
url = f"{BASE_URL}/docx/v1/documents/{document_id}/blocks/{block_id}"
|
||||||
payload = {
|
payload = {
|
||||||
"replace_image": {
|
"replace_image": {
|
||||||
"token": file_token
|
"token": file_token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = requests.patch(
|
response = requests.patch(
|
||||||
url,
|
url,
|
||||||
headers=headers(),
|
headers=headers(),
|
||||||
params={"document_revision_id": -1},
|
params={"document_revision_id": -1},
|
||||||
json=payload
|
json=payload
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if data.get("code") != 0:
|
if data.get("code") != 0:
|
||||||
raise Exception(f"绑定失败: {data}")
|
raise Exception(f"绑定失败: {data}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print(f"\n上传图片: {IMAGE_PATH}")
|
print(f"\n上传图片: {IMAGE_PATH}")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
# 检查文件
|
# 检查文件
|
||||||
if not os.path.exists(IMAGE_PATH):
|
if not os.path.exists(IMAGE_PATH):
|
||||||
print(f"[ERROR] 文件不存在: {IMAGE_PATH}")
|
print(f"[ERROR] 文件不存在: {IMAGE_PATH}")
|
||||||
return
|
return
|
||||||
|
|
||||||
file_size = os.path.getsize(IMAGE_PATH)
|
file_size = os.path.getsize(IMAGE_PATH)
|
||||||
print(f"文件大小: {file_size / 1024:.1f} KB")
|
print(f"文件大小: {file_size / 1024:.1f} KB")
|
||||||
|
|
||||||
# Step 1: 创建文档
|
# Step 1: 创建文档
|
||||||
print("\n[1/4] 创建飞书文档...")
|
print("\n[1/4] 创建飞书文档...")
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||||
doc_id = create_document(f"火把图片 - {timestamp}")
|
doc_id = create_document(f"火把图片 - {timestamp}")
|
||||||
print(f" 文档ID: {doc_id}")
|
print(f" 文档ID: {doc_id}")
|
||||||
|
|
||||||
# Step 2: 创建图片块
|
# Step 2: 创建图片块
|
||||||
print("[2/4] 创建图片块...")
|
print("[2/4] 创建图片块...")
|
||||||
block_id = create_image_block(doc_id)
|
block_id = create_image_block(doc_id)
|
||||||
print(f" 块ID: {block_id}")
|
print(f" 块ID: {block_id}")
|
||||||
|
|
||||||
# Step 3: 上传图片
|
# Step 3: 上传图片
|
||||||
print("[3/4] 上传图片...")
|
print("[3/4] 上传图片...")
|
||||||
file_token = upload_image(IMAGE_PATH, block_id)
|
file_token = upload_image(IMAGE_PATH, block_id)
|
||||||
print(f" file_token: {file_token}")
|
print(f" file_token: {file_token}")
|
||||||
|
|
||||||
# Step 4: 绑定图片
|
# Step 4: 绑定图片
|
||||||
print("[4/4] 绑定图片...")
|
print("[4/4] 绑定图片...")
|
||||||
bind_image(doc_id, block_id, file_token)
|
bind_image(doc_id, block_id, file_token)
|
||||||
print(" 绑定成功!")
|
print(" 绑定成功!")
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print("上传完成!")
|
print("上传完成!")
|
||||||
print(f"\n文档地址: https://feishu.cn/docx/{doc_id}")
|
print(f"\n文档地址: https://feishu.cn/docx/{doc_id}")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ metadata:
|
|||||||
invocation: user-invoked
|
invocation: user-invoked
|
||||||
disable-model-invocation: true
|
disable-model-invocation: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 思源笔记 API 集成 Skill
|
|
||||||
|
|
||||||
## 环境变量配置(必须)
|
## 环境变量配置(必须)
|
||||||
|
|
||||||
本 skill 通过环境变量读取思源笔记连接信息,不同用户/组织配置不同的值。
|
本 skill 通过环境变量读取思源笔记连接信息,不同用户/组织配置不同的值。
|
||||||
@@ -520,127 +517,6 @@ markdown = f"""# {today} 工作日志
|
|||||||
|
|
||||||
## 会议记录
|
## 会议记录
|
||||||
|
|
||||||
## 备注
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 获取或创建日记笔记本
|
|
||||||
notebooks = api.list_notebooks()
|
|
||||||
diary_nb = next((nb for nb in notebooks if nb["name"] == "日记"), None)
|
|
||||||
if not diary_nb:
|
|
||||||
diary_nb = api.create_notebook("日记")["notebook"]
|
|
||||||
|
|
||||||
# 使用 upsert_doc 避免重复创建
|
|
||||||
doc_id = api.upsert_doc(diary_nb["id"], f"/{today}", markdown)
|
|
||||||
print(f"每日笔记: {doc_id}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景 2: 批量导入 Markdown 文件
|
|
||||||
|
|
||||||
```python
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
api = SiYuanAPI()
|
|
||||||
notebook_id = "目标笔记本ID"
|
|
||||||
md_folder = "/path/to/markdown/files"
|
|
||||||
|
|
||||||
for md_file in Path(md_folder).glob("*.md"):
|
|
||||||
with open(md_file, "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
doc_name = md_file.stem
|
|
||||||
# 使用 upsert_doc 支持增量导入,避免重复
|
|
||||||
doc_id = api.upsert_doc(notebook_id, f"/{doc_name}", content)
|
|
||||||
print(f"导入: {doc_name} -> {doc_id}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景 3: 搜索并汇总
|
|
||||||
|
|
||||||
```python
|
|
||||||
api = SiYuanAPI()
|
|
||||||
|
|
||||||
# 搜索所有包含"会议"的内容
|
|
||||||
results = api.search("会议")
|
|
||||||
blocks = results.get("blocks", [])
|
|
||||||
|
|
||||||
print(f"找到 {len(blocks)} 条相关内容:")
|
|
||||||
for block in blocks[:10]:
|
|
||||||
print(f"- {block.get('content', '')[:50]}...")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景 4: 发布合同到飞书云文档
|
|
||||||
|
|
||||||
```python
|
|
||||||
from siyuan import SiYuanAPI
|
|
||||||
|
|
||||||
api = SiYuanAPI()
|
|
||||||
|
|
||||||
# 1. 从思源笔记导出合同内容
|
|
||||||
contract_doc_id = "20260202080355-2c0vgac" # 合同文档 ID
|
|
||||||
markdown_content = api.export_md(contract_doc_id)
|
|
||||||
|
|
||||||
# 2. 使用飞书 MCP 创建云文档
|
|
||||||
# 调用 feishu-doc MCP 的 create_and_write_document 工具
|
|
||||||
# - title: "物流服务合同-妗晨与名风"
|
|
||||||
# - content: markdown_content
|
|
||||||
# - folder_token: (可选,指定目标文件夹)
|
|
||||||
|
|
||||||
# 3. 完整的发布函数
|
|
||||||
def publish_to_feishu(doc_id: str, doc_title: str = None) -> dict:
|
|
||||||
"""
|
|
||||||
将思源笔记文档发布到飞书云文档
|
|
||||||
|
|
||||||
Args:
|
|
||||||
doc_id: 思源笔记文档 ID
|
|
||||||
doc_title: 飞书文档标题(可选,默认使用原文档标题)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {"siyuan_doc_id": str, "content": str, "feishu_url": str}
|
|
||||||
"""
|
|
||||||
api = SiYuanAPI()
|
|
||||||
|
|
||||||
# 导出 Markdown 内容
|
|
||||||
content = api.export_md(doc_id)
|
|
||||||
|
|
||||||
# 获取原文档标题(如果未指定)
|
|
||||||
if not doc_title:
|
|
||||||
doc_info = api.get_block(doc_id)
|
|
||||||
doc_title = doc_info.get("content", "未命名文档")
|
|
||||||
|
|
||||||
# 返回准备好的内容,由调用者使用 feishu-doc MCP 创建
|
|
||||||
return {
|
|
||||||
"siyuan_doc_id": doc_id,
|
|
||||||
"doc_title": doc_title,
|
|
||||||
"content": content
|
|
||||||
}
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
result = publish_to_feishu("20260202080355-2c0vgac", "物流服务合同-妗晨与名风-2026")
|
|
||||||
print(f"准备发布: {result['doc_title']}")
|
|
||||||
print(f"内容长度: {len(result['content'])} 字符")
|
|
||||||
|
|
||||||
# 然后调用 feishu-doc MCP:
|
|
||||||
# mcp__feishu-doc__create_and_write_document(
|
|
||||||
# title=result["doc_title"],
|
|
||||||
# content=result["content"]
|
|
||||||
# )
|
|
||||||
```
|
|
||||||
|
|
||||||
**工作流说明**:
|
|
||||||
|
|
||||||
1. **创建合同** (biz-contract 技能) → 生成合同 Markdown 文本
|
|
||||||
2. **存储到思源** (siyuan 技能) → 保存到"商务合同"笔记本,便于预览和修改
|
|
||||||
3. **发布到飞书** (feishu-doc MCP) → 导出并创建飞书云文档
|
|
||||||
|
|
||||||
**商务合同笔记本配置**:
|
|
||||||
|
|
||||||
| 配置项 | 值 |
|
|
||||||
|--------|-----|
|
|
||||||
| 笔记本名称 | 商务合同 |
|
|
||||||
| 笔记本 ID | `20260202080313-kjtgg1j` |
|
|
||||||
| 路径规范 | `/{合同类型}/{甲方简称}-{乙方简称}-{年份}` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
- API Token 需要保密,不要提交到代码仓库
|
- API Token 需要保密,不要提交到代码仓库
|
||||||
@@ -668,72 +544,6 @@ print(f"内容长度: {len(result['content'])} 字符")
|
|||||||
- **最后更新**: 2026-01-15 07:35:00 ACDT
|
- **最后更新**: 2026-01-15 07:35:00 ACDT
|
||||||
- **验证时间**: 2026-01-15 08:00:00 CST
|
- **验证时间**: 2026-01-15 08:00:00 CST
|
||||||
|
|
||||||
## 配置变更
|
|
||||||
|
|
||||||
**变更时间**: 2026-01-15 01:12:00 CST
|
|
||||||
**生效时间**: 2026-01-15 01:15:00 CST
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ❌ 错误示例
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
- **发生时间**: 2026-01-15
|
|
||||||
- **创建时间**: 2026-01-15
|
|
||||||
- **最后更新**: 今天
|
|
||||||
- **验证时间**: 早上
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 时间格式规范
|
|
||||||
|
|
||||||
| 场景 | 格式 | 示例 |
|
|
||||||
|------|------|------|
|
|
||||||
| 精确时间点 | `YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 02:00:01 CST` |
|
|
||||||
| 时间范围 | `YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ` | `2026-01-15 00:00:00 - 00:46:00 CST` |
|
|
||||||
| 跨天时间范围 | `YYYY-MM-DD HH:MM:SS - YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 23:00:00 - 2026-01-16 01:00:00 CST` |
|
|
||||||
|
|
||||||
**时区标注**:
|
|
||||||
- CST: 中国标准时间 (服务器时间、生产环境)
|
|
||||||
- ACDT: 澳大利亚中部夏令时 (au-dev 本地开发时间)
|
|
||||||
- UTC: 协调世界时 (国际标准)
|
|
||||||
|
|
||||||
### 文档元信息要求
|
|
||||||
|
|
||||||
所有运维、技术类笔记文档必须在开头包含元信息:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# 文档标题
|
|
||||||
|
|
||||||
**创建时间**: YYYY-MM-DD HH:MM:SS TZ
|
|
||||||
**创建人**: qiudl / Claude
|
|
||||||
**最后更新**: YYYY-MM-DD HH:MM:SS TZ
|
|
||||||
**文档状态**: ✅ 已完成 / 🔄 进行中 / ⚠️ 待验证 / ❌ 已废弃
|
|
||||||
**相关项目**: 项目名称
|
|
||||||
**标签**: #运维 #数据库 #备份
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[正文内容...]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 事件记录规范
|
|
||||||
|
|
||||||
记录重大事件或故障时必须包含:
|
|
||||||
|
|
||||||
1. **事件时间**: 完整的开始和结束时间(精确到秒)
|
|
||||||
2. **严重程度**: P0/P1/P2/P3
|
|
||||||
3. **影响范围**: 具体说明受影响的系统和用户
|
|
||||||
4. **事件描述**: 清晰简洁的事件说明
|
|
||||||
5. **恢复过程**: 详细的恢复步骤和时间点
|
|
||||||
6. **恢复完成时间**: 精确的恢复完成时间
|
|
||||||
7. **根本原因**: RCA (Root Cause Analysis)
|
|
||||||
8. **预防措施**: 包含完成时间的预防措施
|
|
||||||
9. **相关文档**: 链接到相关技术文档或代码仓库
|
|
||||||
|
|
||||||
**模板**:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# YYYY-MM-DD 事件标题
|
|
||||||
|
|
||||||
## 事件概述
|
## 事件概述
|
||||||
|
|
||||||
- **发生时间**: YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ
|
- **发生时间**: YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ
|
||||||
@@ -893,83 +703,11 @@ YYYY-MM-DD HH:MM:SS - 验证通过
|
|||||||
记录手动运维操作时应包含:
|
记录手动运维操作时应包含:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## YYYY-MM-DD HH:MM 操作记录
|
|
||||||
|
|
||||||
**操作时间**: YYYY-MM-DD HH:MM:SS TZ
|
## 详细内容
|
||||||
**操作人**: qiudl / Claude
|
|
||||||
**操作类型**: 数据库操作 / 服务重启 / 配置变更 / 部署发布
|
|
||||||
**操作内容**: 简短描述
|
|
||||||
|
|
||||||
### 详细步骤
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
1. **步骤 1** (HH:MM:SS)
|
- 备注
|
||||||
```bash
|
- 配置变更
|
||||||
具体命令
|
- YYYY-MM-DD HH:MM 操作记录
|
||||||
```
|
|
||||||
结果: [...]
|
|
||||||
|
|
||||||
2. **步骤 2** (HH:MM:SS)
|
|
||||||
```bash
|
|
||||||
具体命令
|
|
||||||
```
|
|
||||||
结果: [...]
|
|
||||||
|
|
||||||
### 验证结果
|
|
||||||
|
|
||||||
**验证时间**: YYYY-MM-DD HH:MM:SS TZ
|
|
||||||
**验证结果**: ✅ 成功 / ❌ 失败 / ⚠️ 部分成功
|
|
||||||
|
|
||||||
[验证详情...]
|
|
||||||
|
|
||||||
### 回滚方案
|
|
||||||
|
|
||||||
如需回滚,执行以下步骤:
|
|
||||||
```bash
|
|
||||||
回滚命令
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**记录时间**: YYYY-MM-DD HH:MM:SS TZ
|
|
||||||
```
|
|
||||||
|
|
||||||
### 检查清单
|
|
||||||
|
|
||||||
在创建或更新思源笔记文档时,请检查:
|
|
||||||
|
|
||||||
- [ ] 所有时间记录都包含完整时间戳(精确到秒)
|
|
||||||
- [ ] 所有时间戳都标注了时区(CST/ACDT/UTC)
|
|
||||||
- [ ] 文档包含元信息(创建时间、创建人、状态)
|
|
||||||
- [ ] 事件记录包含完整的时间范围和时间线
|
|
||||||
- [ ] 技术方案包含各阶段的时间规划
|
|
||||||
- [ ] 会议记录包含会议时间和记录完成时间
|
|
||||||
- [ ] 操作日志包含操作时间、验证时间
|
|
||||||
- [ ] 所有时间使用 24 小时制
|
|
||||||
- [ ] 时间格式一致(YYYY-MM-DD HH:MM:SS TZ)
|
|
||||||
- [ ] 跨天操作明确标注日期变化
|
|
||||||
|
|
||||||
### 为什么需要完整时间戳
|
|
||||||
|
|
||||||
1. **故障排查**: 精确时间有助于关联服务器日志和追踪问题根源
|
|
||||||
2. **时间顺序**: 明确事件的先后关系,构建完整的时间线
|
|
||||||
3. **性能分析**: 精确计算操作耗时,识别性能瓶颈
|
|
||||||
4. **审计追踪**: 完整的操作记录,满足合规要求
|
|
||||||
5. **跨时区协作**: 明确时区避免混淆(澳洲团队 vs 中国服务器)
|
|
||||||
6. **自动化集成**: 便于后续工具提取时间信息进行分析
|
|
||||||
7. **知识沉淀**: 便于后续查阅和学习,理解决策的时间背景
|
|
||||||
|
|
||||||
### 常见错误和修正
|
|
||||||
|
|
||||||
| 错误写法 | 正确写法 | 说明 |
|
|
||||||
|---------|---------|------|
|
|
||||||
| 今天下午 | 2026-01-15 14:30:00 CST | 避免模糊表述 |
|
|
||||||
| 凌晨 | 2026-01-15 02:00:00 CST | 明确具体时间 |
|
|
||||||
| 2026-01-15 | 2026-01-15 14:30:00 CST | 补充时分秒 |
|
|
||||||
| 14:30 | 2026-01-15 14:30:00 CST | 补充日期和时区 |
|
|
||||||
| 昨天 | 2026-01-14 HH:MM:SS CST | 使用具体日期 |
|
|
||||||
| 刚才 | 2026-01-15 14:28:00 CST | 记录精确时间 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**规范创建时间**: 2026-01-15 07:45:00 ACDT
|
|
||||||
**最后更新时间**: 2026-01-15 07:45:00 ACDT
|
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 备注
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 获取或创建日记笔记本
|
||||||
|
notebooks = api.list_notebooks()
|
||||||
|
diary_nb = next((nb for nb in notebooks if nb["name"] == "日记"), None)
|
||||||
|
if not diary_nb:
|
||||||
|
diary_nb = api.create_notebook("日记")["notebook"]
|
||||||
|
|
||||||
|
# 使用 upsert_doc 避免重复创建
|
||||||
|
doc_id = api.upsert_doc(diary_nb["id"], f"/{today}", markdown)
|
||||||
|
print(f"每日笔记: {doc_id}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景 2: 批量导入 Markdown 文件
|
||||||
|
|
||||||
|
```python
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
api = SiYuanAPI()
|
||||||
|
notebook_id = "目标笔记本ID"
|
||||||
|
md_folder = "/path/to/markdown/files"
|
||||||
|
|
||||||
|
for md_file in Path(md_folder).glob("*.md"):
|
||||||
|
with open(md_file, "r", encoding="utf-8") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
doc_name = md_file.stem
|
||||||
|
# 使用 upsert_doc 支持增量导入,避免重复
|
||||||
|
doc_id = api.upsert_doc(notebook_id, f"/{doc_name}", content)
|
||||||
|
print(f"导入: {doc_name} -> {doc_id}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景 3: 搜索并汇总
|
||||||
|
|
||||||
|
```python
|
||||||
|
api = SiYuanAPI()
|
||||||
|
|
||||||
|
# 搜索所有包含"会议"的内容
|
||||||
|
results = api.search("会议")
|
||||||
|
blocks = results.get("blocks", [])
|
||||||
|
|
||||||
|
print(f"找到 {len(blocks)} 条相关内容:")
|
||||||
|
for block in blocks[:10]:
|
||||||
|
print(f"- {block.get('content', '')[:50]}...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景 4: 发布合同到飞书云文档
|
||||||
|
|
||||||
|
```python
|
||||||
|
from siyuan import SiYuanAPI
|
||||||
|
|
||||||
|
api = SiYuanAPI()
|
||||||
|
|
||||||
|
# 1. 从思源笔记导出合同内容
|
||||||
|
contract_doc_id = "20260202080355-2c0vgac" # 合同文档 ID
|
||||||
|
markdown_content = api.export_md(contract_doc_id)
|
||||||
|
|
||||||
|
# 2. 使用飞书 MCP 创建云文档
|
||||||
|
# 调用 feishu-doc MCP 的 create_and_write_document 工具
|
||||||
|
# - title: "物流服务合同-妗晨与名风"
|
||||||
|
# - content: markdown_content
|
||||||
|
# - folder_token: (可选,指定目标文件夹)
|
||||||
|
|
||||||
|
# 3. 完整的发布函数
|
||||||
|
def publish_to_feishu(doc_id: str, doc_title: str = None) -> dict:
|
||||||
|
"""
|
||||||
|
将思源笔记文档发布到飞书云文档
|
||||||
|
|
||||||
|
Args:
|
||||||
|
doc_id: 思源笔记文档 ID
|
||||||
|
doc_title: 飞书文档标题(可选,默认使用原文档标题)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {"siyuan_doc_id": str, "content": str, "feishu_url": str}
|
||||||
|
"""
|
||||||
|
api = SiYuanAPI()
|
||||||
|
|
||||||
|
# 导出 Markdown 内容
|
||||||
|
content = api.export_md(doc_id)
|
||||||
|
|
||||||
|
# 获取原文档标题(如果未指定)
|
||||||
|
if not doc_title:
|
||||||
|
doc_info = api.get_block(doc_id)
|
||||||
|
doc_title = doc_info.get("content", "未命名文档")
|
||||||
|
|
||||||
|
# 返回准备好的内容,由调用者使用 feishu-doc MCP 创建
|
||||||
|
return {
|
||||||
|
"siyuan_doc_id": doc_id,
|
||||||
|
"doc_title": doc_title,
|
||||||
|
"content": content
|
||||||
|
}
|
||||||
|
|
||||||
|
# 使用示例
|
||||||
|
result = publish_to_feishu("20260202080355-2c0vgac", "物流服务合同-妗晨与名风-2026")
|
||||||
|
print(f"准备发布: {result['doc_title']}")
|
||||||
|
print(f"内容长度: {len(result['content'])} 字符")
|
||||||
|
|
||||||
|
# 然后调用 feishu-doc MCP:
|
||||||
|
# mcp__feishu-doc__create_and_write_document(
|
||||||
|
# title=result["doc_title"],
|
||||||
|
# content=result["content"]
|
||||||
|
# )
|
||||||
|
```
|
||||||
|
|
||||||
|
**工作流说明**:
|
||||||
|
|
||||||
|
1. **创建合同** (biz-contract 技能) → 生成合同 Markdown 文本
|
||||||
|
2. **存储到思源** (siyuan 技能) → 保存到"商务合同"笔记本,便于预览和修改
|
||||||
|
3. **发布到飞书** (feishu-doc MCP) → 导出并创建飞书云文档
|
||||||
|
|
||||||
|
**商务合同笔记本配置**:
|
||||||
|
|
||||||
|
| 配置项 | 值 |
|
||||||
|
|--------|-----|
|
||||||
|
| 笔记本名称 | 商务合同 |
|
||||||
|
| 笔记本 ID | `20260202080313-kjtgg1j` |
|
||||||
|
| 路径规范 | `/{合同类型}/{甲方简称}-{乙方简称}-{年份}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置变更
|
||||||
|
|
||||||
|
**变更时间**: 2026-01-15 01:12:00 CST
|
||||||
|
**生效时间**: 2026-01-15 01:15:00 CST
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ❌ 错误示例
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
- **发生时间**: 2026-01-15
|
||||||
|
- **创建时间**: 2026-01-15
|
||||||
|
- **最后更新**: 今天
|
||||||
|
- **验证时间**: 早上
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 时间格式规范
|
||||||
|
|
||||||
|
| 场景 | 格式 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| 精确时间点 | `YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 02:00:01 CST` |
|
||||||
|
| 时间范围 | `YYYY-MM-DD HH:MM:SS - HH:MM:SS TZ` | `2026-01-15 00:00:00 - 00:46:00 CST` |
|
||||||
|
| 跨天时间范围 | `YYYY-MM-DD HH:MM:SS - YYYY-MM-DD HH:MM:SS TZ` | `2026-01-15 23:00:00 - 2026-01-16 01:00:00 CST` |
|
||||||
|
|
||||||
|
**时区标注**:
|
||||||
|
- CST: 中国标准时间 (服务器时间、生产环境)
|
||||||
|
- ACDT: 澳大利亚中部夏令时 (au-dev 本地开发时间)
|
||||||
|
- UTC: 协调世界时 (国际标准)
|
||||||
|
|
||||||
|
### 文档元信息要求
|
||||||
|
|
||||||
|
所有运维、技术类笔记文档必须在开头包含元信息:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 文档标题
|
||||||
|
|
||||||
|
**创建时间**: YYYY-MM-DD HH:MM:SS TZ
|
||||||
|
**创建人**: qiudl / Claude
|
||||||
|
**最后更新**: YYYY-MM-DD HH:MM:SS TZ
|
||||||
|
**文档状态**: ✅ 已完成 / 🔄 进行中 / ⚠️ 待验证 / ❌ 已废弃
|
||||||
|
**相关项目**: 项目名称
|
||||||
|
**标签**: #运维 #数据库 #备份
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[正文内容...]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 事件记录规范
|
||||||
|
|
||||||
|
记录重大事件或故障时必须包含:
|
||||||
|
|
||||||
|
1. **事件时间**: 完整的开始和结束时间(精确到秒)
|
||||||
|
2. **严重程度**: P0/P1/P2/P3
|
||||||
|
3. **影响范围**: 具体说明受影响的系统和用户
|
||||||
|
4. **事件描述**: 清晰简洁的事件说明
|
||||||
|
5. **恢复过程**: 详细的恢复步骤和时间点
|
||||||
|
6. **恢复完成时间**: 精确的恢复完成时间
|
||||||
|
7. **根本原因**: RCA (Root Cause Analysis)
|
||||||
|
8. **预防措施**: 包含完成时间的预防措施
|
||||||
|
9. **相关文档**: 链接到相关技术文档或代码仓库
|
||||||
|
|
||||||
|
**模板**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# YYYY-MM-DD 事件标题
|
||||||
|
|
||||||
|
## YYYY-MM-DD HH:MM 操作记录
|
||||||
|
|
||||||
|
**操作时间**: YYYY-MM-DD HH:MM:SS TZ
|
||||||
|
**操作人**: qiudl / Claude
|
||||||
|
**操作类型**: 数据库操作 / 服务重启 / 配置变更 / 部署发布
|
||||||
|
**操作内容**: 简短描述
|
||||||
|
|
||||||
|
### 详细步骤
|
||||||
|
|
||||||
|
1. **步骤 1** (HH:MM:SS)
|
||||||
|
```bash
|
||||||
|
具体命令
|
||||||
|
```
|
||||||
|
结果: [...]
|
||||||
|
|
||||||
|
2. **步骤 2** (HH:MM:SS)
|
||||||
|
```bash
|
||||||
|
具体命令
|
||||||
|
```
|
||||||
|
结果: [...]
|
||||||
|
|
||||||
|
### 验证结果
|
||||||
|
|
||||||
|
**验证时间**: YYYY-MM-DD HH:MM:SS TZ
|
||||||
|
**验证结果**: ✅ 成功 / ❌ 失败 / ⚠️ 部分成功
|
||||||
|
|
||||||
|
[验证详情...]
|
||||||
|
|
||||||
|
### 回滚方案
|
||||||
|
|
||||||
|
如需回滚,执行以下步骤:
|
||||||
|
```bash
|
||||||
|
回滚命令
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**记录时间**: YYYY-MM-DD HH:MM:SS TZ
|
||||||
|
```
|
||||||
|
|
||||||
|
### 检查清单
|
||||||
|
|
||||||
|
在创建或更新思源笔记文档时,请检查:
|
||||||
|
|
||||||
|
- [ ] 所有时间记录都包含完整时间戳(精确到秒)
|
||||||
|
- [ ] 所有时间戳都标注了时区(CST/ACDT/UTC)
|
||||||
|
- [ ] 文档包含元信息(创建时间、创建人、状态)
|
||||||
|
- [ ] 事件记录包含完整的时间范围和时间线
|
||||||
|
- [ ] 技术方案包含各阶段的时间规划
|
||||||
|
- [ ] 会议记录包含会议时间和记录完成时间
|
||||||
|
- [ ] 操作日志包含操作时间、验证时间
|
||||||
|
- [ ] 所有时间使用 24 小时制
|
||||||
|
- [ ] 时间格式一致(YYYY-MM-DD HH:MM:SS TZ)
|
||||||
|
- [ ] 跨天操作明确标注日期变化
|
||||||
|
|
||||||
|
### 为什么需要完整时间戳
|
||||||
|
|
||||||
|
1. **故障排查**: 精确时间有助于关联服务器日志和追踪问题根源
|
||||||
|
2. **时间顺序**: 明确事件的先后关系,构建完整的时间线
|
||||||
|
3. **性能分析**: 精确计算操作耗时,识别性能瓶颈
|
||||||
|
4. **审计追踪**: 完整的操作记录,满足合规要求
|
||||||
|
5. **跨时区协作**: 明确时区避免混淆(澳洲团队 vs 中国服务器)
|
||||||
|
6. **自动化集成**: 便于后续工具提取时间信息进行分析
|
||||||
|
7. **知识沉淀**: 便于后续查阅和学习,理解决策的时间背景
|
||||||
|
|
||||||
|
### 常见错误和修正
|
||||||
|
|
||||||
|
| 错误写法 | 正确写法 | 说明 |
|
||||||
|
|---------|---------|------|
|
||||||
|
| 今天下午 | 2026-01-15 14:30:00 CST | 避免模糊表述 |
|
||||||
|
| 凌晨 | 2026-01-15 02:00:00 CST | 明确具体时间 |
|
||||||
|
| 2026-01-15 | 2026-01-15 14:30:00 CST | 补充时分秒 |
|
||||||
|
| 14:30 | 2026-01-15 14:30:00 CST | 补充日期和时区 |
|
||||||
|
| 昨天 | 2026-01-14 HH:MM:SS CST | 使用具体日期 |
|
||||||
|
| 刚才 | 2026-01-15 14:28:00 CST | 记录精确时间 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**规范创建时间**: 2026-01-15 07:45:00 ACDT
|
||||||
|
**最后更新时间**: 2026-01-15 07:45:00 ACDT
|
||||||
@@ -5,9 +5,6 @@ metadata:
|
|||||||
invocation: user-invoked
|
invocation: user-invoked
|
||||||
disable-model-invocation: true
|
disable-model-invocation: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 企业微信集成技能
|
|
||||||
|
|
||||||
## 功能概述
|
## 功能概述
|
||||||
|
|
||||||
### 消息推送
|
### 消息推送
|
||||||
@@ -484,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']}")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 常见错误码
|
## 常见错误码
|
||||||
|
|
||||||
| 错误码 | 说明 | 解决方案 |
|
| 错误码 | 说明 | 解决方案 |
|
||||||
@@ -809,3 +505,9 @@ print(f"文档链接: {result['url']}")
|
|||||||
| API 文档 | https://developer.work.weixin.qq.com/document/ |
|
| API 文档 | https://developer.work.weixin.qq.com/document/ |
|
||||||
| 调试工具 | https://developer.work.weixin.qq.com/devtool/interface |
|
| 调试工具 | https://developer.work.weixin.qq.com/devtool/interface |
|
||||||
| 管理后台 | https://work.weixin.qq.com/wework_admin/ |
|
| 管理后台 | https://work.weixin.qq.com/wework_admin/ |
|
||||||
|
|
||||||
|
## 详细内容
|
||||||
|
|
||||||
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
|
- 通用函数封装
|
||||||
|
|||||||
@@ -0,0 +1,303 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 通用函数封装
|
||||||
|
|
||||||
|
### 简单文本文档
|
||||||
|
|
||||||
|
适用于报告、记录、会议纪要等。使用符号标记章节。
|
||||||
|
|
||||||
|
```python
|
||||||
|
def create_simple_doc(doc_name: str, content: str) -> dict:
|
||||||
|
"""
|
||||||
|
创建简单文本文档
|
||||||
|
|
||||||
|
Args:
|
||||||
|
doc_name: 文档名称
|
||||||
|
content: 文档内容(使用【】标记章节)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {"docid": "xxx", "url": "https://..."}
|
||||||
|
|
||||||
|
Example:
|
||||||
|
content = '''项目周报
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
【本周完成】
|
||||||
|
1. 完成用户模块开发
|
||||||
|
2. 修复登录bug
|
||||||
|
|
||||||
|
【下周计划】
|
||||||
|
1. 开始订单模块
|
||||||
|
2. 编写测试用例
|
||||||
|
'''
|
||||||
|
result = create_simple_doc("2026年第5周周报", content)
|
||||||
|
"""
|
||||||
|
token = get_access_token()
|
||||||
|
|
||||||
|
# 1. 创建文档
|
||||||
|
create_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={token}"
|
||||||
|
create_resp = requests.post(create_url, json={
|
||||||
|
"doc_type": 3,
|
||||||
|
"doc_name": doc_name
|
||||||
|
}).json()
|
||||||
|
|
||||||
|
if create_resp.get("errcode") != 0:
|
||||||
|
return create_resp
|
||||||
|
|
||||||
|
docid = create_resp["docid"]
|
||||||
|
doc_url = create_resp["url"]
|
||||||
|
|
||||||
|
# 2. 写入内容
|
||||||
|
update_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"
|
||||||
|
update_resp = requests.post(update_url, json={
|
||||||
|
"docid": docid,
|
||||||
|
"requests": [{
|
||||||
|
"insert_text": {
|
||||||
|
"text": content,
|
||||||
|
"location": {"index": 0}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}).json()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"errcode": update_resp.get("errcode", 0),
|
||||||
|
"docid": docid,
|
||||||
|
"url": doc_url
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 合同类文档(带标题样式)
|
||||||
|
|
||||||
|
适用于合同、协议等需要标题层级的正式文档。
|
||||||
|
|
||||||
|
```python
|
||||||
|
import time
|
||||||
|
|
||||||
|
def create_contract_doc(doc_name: str, sections: list) -> dict:
|
||||||
|
"""
|
||||||
|
创建带标题样式的合同文档
|
||||||
|
|
||||||
|
Args:
|
||||||
|
doc_name: 文档名称
|
||||||
|
sections: 内容列表,格式 [(文本, 标题级别), ...]
|
||||||
|
标题级别: 1=主标题, 2=章节标题, 0=正文
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {"docid": "xxx", "url": "https://..."}
|
||||||
|
|
||||||
|
Example:
|
||||||
|
sections = [
|
||||||
|
("物流服务合同", 1),
|
||||||
|
("合同编号: WL-2026-0201", 0),
|
||||||
|
("", 0),
|
||||||
|
("甲方(托运方)", 2),
|
||||||
|
("公司名称: xxx公司", 0),
|
||||||
|
("", 0),
|
||||||
|
("第一条 服务内容", 2),
|
||||||
|
("1.1 服务类型:货物运输", 0),
|
||||||
|
("1.2 货物类型:xxx", 0),
|
||||||
|
]
|
||||||
|
result = create_contract_doc("物流服务合同-甲方与乙方", sections)
|
||||||
|
"""
|
||||||
|
token = get_access_token()
|
||||||
|
|
||||||
|
# 1. 创建文档
|
||||||
|
create_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={token}"
|
||||||
|
create_resp = requests.post(create_url, json={
|
||||||
|
"doc_type": 3,
|
||||||
|
"doc_name": doc_name
|
||||||
|
}).json()
|
||||||
|
|
||||||
|
if create_resp.get("errcode") != 0:
|
||||||
|
return create_resp
|
||||||
|
|
||||||
|
docid = create_resp["docid"]
|
||||||
|
doc_url = create_resp["url"]
|
||||||
|
|
||||||
|
# 2. 倒序内容(因为每次在位置0插入)
|
||||||
|
reversed_sections = list(reversed(sections))
|
||||||
|
|
||||||
|
# 3. 分批插入(每批最多25个,留余量)
|
||||||
|
update_url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"
|
||||||
|
batch_size = 25
|
||||||
|
|
||||||
|
for i in range(0, len(reversed_sections), batch_size):
|
||||||
|
batch = reversed_sections[i:i+batch_size]
|
||||||
|
requests_list = []
|
||||||
|
|
||||||
|
for text, level in batch:
|
||||||
|
para = {"elements": [{"text_run": {"content": text}}]}
|
||||||
|
if level > 0:
|
||||||
|
para["paragraph_style"] = {"heading_level": level}
|
||||||
|
|
||||||
|
requests_list.append({
|
||||||
|
"insert_paragraph": {
|
||||||
|
"location": {"index": 0},
|
||||||
|
"paragraph": para
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
resp = requests.post(update_url, json={
|
||||||
|
"docid": docid,
|
||||||
|
"requests": requests_list
|
||||||
|
}).json()
|
||||||
|
|
||||||
|
if resp.get("errcode") != 0:
|
||||||
|
return {
|
||||||
|
"errcode": resp.get("errcode"),
|
||||||
|
"errmsg": resp.get("errmsg"),
|
||||||
|
"docid": docid,
|
||||||
|
"url": doc_url
|
||||||
|
}
|
||||||
|
|
||||||
|
time.sleep(0.3) # 避免频率限制
|
||||||
|
|
||||||
|
return {
|
||||||
|
"errcode": 0,
|
||||||
|
"docid": docid,
|
||||||
|
"url": doc_url
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_contract_sections(
|
||||||
|
title: str,
|
||||||
|
party_a: dict,
|
||||||
|
party_b: dict,
|
||||||
|
clauses: list,
|
||||||
|
signature: bool = True
|
||||||
|
) -> list:
|
||||||
|
"""
|
||||||
|
构建合同内容结构
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title: 合同标题
|
||||||
|
party_a: 甲方信息 {"name": "", "code": "", "address": "", "phone": "", "legal_rep": ""}
|
||||||
|
party_b: 乙方信息,格式同上
|
||||||
|
clauses: 条款列表 [{"title": "第一条 xxx", "items": ["1.1 xxx", "1.2 xxx"]}, ...]
|
||||||
|
signature: 是否包含签章栏
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: 可直接传给 create_contract_doc 的 sections
|
||||||
|
"""
|
||||||
|
sections = []
|
||||||
|
|
||||||
|
# 标题
|
||||||
|
sections.append((title, 1))
|
||||||
|
sections.append(("", 0))
|
||||||
|
|
||||||
|
# 甲方
|
||||||
|
sections.append(("甲方", 2))
|
||||||
|
if party_a.get("name"):
|
||||||
|
sections.append((f"公司名称: {party_a['name']}", 0))
|
||||||
|
if party_a.get("code"):
|
||||||
|
sections.append((f"统一社会信用代码: {party_a['code']}", 0))
|
||||||
|
if party_a.get("address"):
|
||||||
|
sections.append((f"地址: {party_a['address']}", 0))
|
||||||
|
if party_a.get("phone"):
|
||||||
|
sections.append((f"联系电话: {party_a['phone']}", 0))
|
||||||
|
if party_a.get("legal_rep"):
|
||||||
|
sections.append((f"法定代表人: {party_a['legal_rep']}", 0))
|
||||||
|
sections.append(("", 0))
|
||||||
|
|
||||||
|
# 乙方
|
||||||
|
sections.append(("乙方", 2))
|
||||||
|
if party_b.get("name"):
|
||||||
|
sections.append((f"公司名称: {party_b['name']}", 0))
|
||||||
|
if party_b.get("code"):
|
||||||
|
sections.append((f"统一社会信用代码: {party_b['code']}", 0))
|
||||||
|
if party_b.get("address"):
|
||||||
|
sections.append((f"地址: {party_b['address']}", 0))
|
||||||
|
if party_b.get("phone"):
|
||||||
|
sections.append((f"联系电话: {party_b['phone']}", 0))
|
||||||
|
if party_b.get("legal_rep"):
|
||||||
|
sections.append((f"法定代表人: {party_b['legal_rep']}", 0))
|
||||||
|
sections.append(("", 0))
|
||||||
|
|
||||||
|
# 条款
|
||||||
|
for clause in clauses:
|
||||||
|
sections.append((clause["title"], 2))
|
||||||
|
for item in clause.get("items", []):
|
||||||
|
sections.append((item, 0))
|
||||||
|
sections.append(("", 0))
|
||||||
|
|
||||||
|
# 签章
|
||||||
|
if signature:
|
||||||
|
sections.append(("签章", 2))
|
||||||
|
sections.append(("", 0))
|
||||||
|
sections.append((f"甲方(盖章): {party_a.get('name', '')}", 0))
|
||||||
|
sections.append(("法定代表人/授权代表:________________", 0))
|
||||||
|
sections.append(("日期:________________", 0))
|
||||||
|
sections.append(("", 0))
|
||||||
|
sections.append((f"乙方(盖章): {party_b.get('name', '')}", 0))
|
||||||
|
sections.append(("法定代表人/授权代表:________________", 0))
|
||||||
|
sections.append(("日期:________________", 0))
|
||||||
|
|
||||||
|
return sections
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
|
||||||
|
#### 简化示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 简化示例:使用占位符
|
||||||
|
party_a = {"name": "甲方公司名称", "code": "甲方税号"}
|
||||||
|
party_b = {"name": "乙方公司名称", "code": "乙方税号"}
|
||||||
|
clauses = [
|
||||||
|
{"title": "第一条 服务内容", "items": ["1.1 xxx", "1.2 xxx"]},
|
||||||
|
{"title": "第二条 服务期限", "items": ["2.1 xxx"]},
|
||||||
|
]
|
||||||
|
|
||||||
|
sections = build_contract_sections("合同标题", party_a, party_b, clauses)
|
||||||
|
result = create_contract_doc("合同文档名称", sections)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 完整示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 完整示例:创建物流合同
|
||||||
|
party_a = {
|
||||||
|
"name": "重庆妗晨工贸有限公司",
|
||||||
|
"code": "91500104MA7EJTPA6D",
|
||||||
|
"address": "重庆市大渡口区跳磴镇海康路106号1-1",
|
||||||
|
"phone": "15213397998"
|
||||||
|
}
|
||||||
|
|
||||||
|
party_b = {
|
||||||
|
"name": "北京名风新能源科技有限公司",
|
||||||
|
"code": "91110106092440790K",
|
||||||
|
"legal_rep": "魏小健"
|
||||||
|
}
|
||||||
|
|
||||||
|
clauses = [
|
||||||
|
{
|
||||||
|
"title": "第一条 服务内容",
|
||||||
|
"items": [
|
||||||
|
"1.1 服务类型:货物运输配送服务",
|
||||||
|
"1.2 货物类型:今麦郎系列产品",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "第二条 服务期限",
|
||||||
|
"items": [
|
||||||
|
"2.1 合同有效期:自 2026年2月1日 至 2027年1月31日",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "第三条 运费标准",
|
||||||
|
"items": [
|
||||||
|
"3.1 运费标准:今麦郎产品 2.50 元/件",
|
||||||
|
"3.2 结算周期:月结",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
# 构建并创建文档
|
||||||
|
sections = build_contract_sections("物流服务合同", party_a, party_b, clauses)
|
||||||
|
result = create_contract_doc("物流服务合同-妗晨与名风", sections)
|
||||||
|
print(f"文档链接: {result['url']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: req-design
|
name: req-design
|
||||||
description: 需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。
|
description: 需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。
|
||||||
arguments: <REQ-ID>
|
metadata:
|
||||||
|
arguments: <REQ-ID>
|
||||||
---
|
---
|
||||||
|
|
||||||
# 需求开发设计 Skill (req-design)
|
# 需求开发设计 Skill (req-design)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: req
|
name: req
|
||||||
description: 需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。
|
description: 需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。
|
||||||
arguments: <subcommand> [args]
|
metadata:
|
||||||
|
arguments: <subcommand> [args]
|
||||||
---
|
---
|
||||||
|
|
||||||
# req - 需求工作流管理
|
|
||||||
|
|
||||||
需求全流程工作流管理,与 ai-proj CLI 深度集成。
|
|
||||||
|
|
||||||
## 需求生命周期
|
## 需求生命周期
|
||||||
|
|
||||||
**审批状态**(status):
|
**审批状态**(status):
|
||||||
@@ -254,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 |
|
|
||||||
|
|
||||||
## 测试环境流程
|
## 测试环境流程
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -506,3 +317,9 @@ ai-proj req tasks --id <id> # 查看关联任务
|
|||||||
| `req-prototype` | Stitch 原型生成 + 迭代 |
|
| `req-prototype` | Stitch 原型生成 + 迭代 |
|
||||||
| `dev-test` | 测试 + 质量门禁 |
|
| `dev-test` | 测试 + 质量门禁 |
|
||||||
| `req-test-gate` (harness) | 工程约束方法论。Gate 0-2 的约定检查由项目本地脚本定义,方法论见 `/harness` 命令 |
|
| `req-test-gate` (harness) | 工程约束方法论。Gate 0-2 的约定检查由项目本地脚本定义,方法论见 `/harness` 命令 |
|
||||||
|
|
||||||
|
## 详细内容
|
||||||
|
|
||||||
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
|
- 命令参考
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 命令参考
|
||||||
|
|
||||||
|
### 撰写命令
|
||||||
|
|
||||||
|
**`/req new [标题]`** — 对话式创建需求(仅 draft)
|
||||||
|
- 参数:`--priority=high|medium|low`,`--project=<name>`,`--category=feature|bug|improvement`
|
||||||
|
- 流程:描述→提取关键信息→用户故事→验收标准→`ai-proj req create`→ Gate 1 需求讨论
|
||||||
|
|
||||||
|
**`/req draft <描述>`** — 一句话快速创建 draft,AI 自动补全
|
||||||
|
|
||||||
|
**`/req edit [REQ-ID]`** — 编辑需求:`ai-proj req get` → 询问修改点 → `ai-proj req update` → 显示变更摘要
|
||||||
|
|
||||||
|
**`/req check [REQ-ID]`** — 完整性检查(Gate 4):
|
||||||
|
```
|
||||||
|
✅ 三段式结构完整 / ✅ 验收标准: 4 条 / ✅ 讨论结论已记录 / ❌ PRD 文档缺失
|
||||||
|
```
|
||||||
|
|
||||||
|
**`/req split [REQ-ID]`** — 拆分为开发任务(后端/前端/测试)并关联
|
||||||
|
|
||||||
|
### 原型设计
|
||||||
|
|
||||||
|
**`/req prototype [REQ-ID]`** — 基于 PRD 生成 Stitch 原型:
|
||||||
|
1. 读取 PRD 文档,提取 UI 相关描述
|
||||||
|
2. 创建 Stitch 项目 + 生成页面(调用 req-prototype 子技能)
|
||||||
|
3. 截图回填 PRD「4.2 界面原型」章节
|
||||||
|
- 参数:`--device [desktop|mobile|tablet]`,`--model [pro|flash]`
|
||||||
|
|
||||||
|
**`/req prototype edit [REQ-ID] --prompt "..."`** — 修改已有原型
|
||||||
|
|
||||||
|
**`/req prototype variant [REQ-ID]`** — 生成设计变体供选择
|
||||||
|
|
||||||
|
### 提交评审
|
||||||
|
|
||||||
|
**`/req submit [REQ-ID]`** ⭐ — 从 draft 到 pending 的唯一合法路径:
|
||||||
|
1. Gate 1: description 末尾有「## 讨论结论」
|
||||||
|
2. Gate 2: 讨论结论经过用户确认
|
||||||
|
3. Gate 3: 有 linkRole=prd 任务且附有文档
|
||||||
|
4. Gate 4: 执行 `/req check` 全部通过
|
||||||
|
5. → `ai-proj req submit --id <id>` → 展示 PRD 摘要 → 等待评审
|
||||||
|
|
||||||
|
**`/req review pass [REQ-ID]`** — 评审通过(必须用户明确确认后才调用 `ai-proj req approve --id <id>`)
|
||||||
|
|
||||||
|
**`/req review reject [REQ-ID] [原因]`** — 评审驳回,必须提供原因
|
||||||
|
|
||||||
|
### 阶段管理
|
||||||
|
|
||||||
|
**`/req` 或 `/req list`** — 按状态分组列出所有需求(`ai-proj req list`)
|
||||||
|
|
||||||
|
**`/req status [REQ-ID]`** — 查看需求详情(`ai-proj req get --id <id>`)、进度、关联任务
|
||||||
|
|
||||||
|
**`/req next [REQ-ID]`** — 推进到下一阶段:
|
||||||
|
1. 检查当前阶段任务完成度
|
||||||
|
2. 智能跳阶段检测(无 implementation 任务时建议跳过 review/testing)
|
||||||
|
3. `ai-proj req advance --id <id> --to <stage>`(门禁检查,未通过→AskUserQuestion,**禁止 AI 自动 force**)
|
||||||
|
4. ⭐ 内容门禁检查(review/testing/staging 阶段)
|
||||||
|
5. 自动创建目标阶段建议任务
|
||||||
|
6. 展示:「✅ 已创建: #XXXX {任务名}。如需撤销请说明」
|
||||||
|
|
||||||
|
**dev → review 门禁**:
|
||||||
|
- `pr_created`: PR 已创建
|
||||||
|
- `quality_gate`: CI 质量门禁通过记录(质量门禁未通过时提示:「CI 质量门禁未通过,请先运行 `/req ci`」)
|
||||||
|
|
||||||
|
**阶段顺序**:`analysis → design → dev → review → testing → [待部署池] → released`
|
||||||
|
|
||||||
|
**`/req resume [REQ-ID]`** — 会话断点恢复:`ai-proj req get --id <id>` 获取 delivery_stage + 任务状态 + 建议操作
|
||||||
|
|
||||||
|
### 开发命令
|
||||||
|
|
||||||
|
**`/req dev [REQ-ID]`** — 启动开发:
|
||||||
|
1. `ai-proj req tasks --id <id>` 找 implementation 任务
|
||||||
|
2. `ai-proj task start --id <task_id>` 更新任务状态为开发中
|
||||||
|
3. `/pr start` 从 origin/main 建分支
|
||||||
|
|
||||||
|
**`/req cr [REQ-ID]`** — 代码评审(前置:开发任务完成):
|
||||||
|
1. `ai-proj req tasks --id <id>` 确认所有 implementation 任务已完成
|
||||||
|
2. `git diff` / `find` 确定变更范围(文件数、行数)
|
||||||
|
3. 读取所有变更文件源码(非 test 文件)
|
||||||
|
4. **五视角扫描**:逐一用攻击者/泄露者/并发者/边界者/依赖者视角审查
|
||||||
|
5. **约定检查**:运行所有 `scripts/check-*.sh --ci`,将结果写入 CR 报告
|
||||||
|
6. **⭐ Bug 约定建议**(bug 类需求自动触发:category=bug,或标题含 fix/修复/bug,或描述含根因/复现步骤):
|
||||||
|
- 分析本次 bug 的根因模式
|
||||||
|
- 判断可检测性:能写 grep 找到同类问题?(规则见 convention-flow.md「可检测性判断」)
|
||||||
|
- 不可检测 → 仅在 CR 报告记录根因,跳过
|
||||||
|
- 可检测 → 检查已有 `scripts/check-*.sh` 是否已覆盖 → 已覆盖则跳过
|
||||||
|
- 扫描代码库统计同类模式数量 N
|
||||||
|
- AskUserQuestion(N=0: 防未来复发;N>0: 防恶化+逐步清理)
|
||||||
|
- 是 → 执行 convention flow → 产出物**单独 commit**(`chore: 建立 {name} 约定`)
|
||||||
|
7. `ai-proj task create` 创建【代码评审】任务并关联需求(linkRole=code_review)
|
||||||
|
8. `ai-proj create-and-attach` 附加 CR 报告文档(必须含五视角扫描结果 + 约定检查结果)
|
||||||
|
9. 展示发现摘要,AskUserQuestion 确认是否创建 bug 修复任务
|
||||||
|
10. 若有 High/Critical 发现 → `ai-proj task create` 创建关联修复任务
|
||||||
|
|
||||||
|
**`/req ci [REQ-ID]`** — CI 质量门禁检查与自动修复:
|
||||||
|
1. `check-ci-status.sh` 检查当前分支 CI 状态
|
||||||
|
- 通过 → 写入 `quality_gate` 门禁证据(`gate-callback.sh`)→ 完成
|
||||||
|
- 运行中 → `--wait` 等待最多 5 分钟
|
||||||
|
- 失败 → 进入自动修复循环
|
||||||
|
2. 自动修复循环(最多 3 轮):
|
||||||
|
a. `fetch-ci-logs.sh --failed-only` 获取失败 job 日志
|
||||||
|
b. AI 诊断根因(见错误模式目录)
|
||||||
|
c. 自动修复代码
|
||||||
|
d. 本地验证(`go vet ./...` / `npx tsc --noEmit`)
|
||||||
|
e. `git commit && git push`
|
||||||
|
f. `check-ci-status.sh --trigger --wait` 等待新 CI
|
||||||
|
g. 通过 → 写入门禁证据 → 完成
|
||||||
|
h. 仍失败 → 下一轮(超过 3 轮 → AskUserQuestion 是否继续)
|
||||||
|
- 参数:`--trigger` 手动触发 CI,`--wait` 等待完成,`--no-fix` 仅诊断不修复
|
||||||
|
|
||||||
|
**错误模式目录**:
|
||||||
|
|
||||||
|
| 失败 Job | 错误模式 | 修复策略 |
|
||||||
|
|----------|---------|---------|
|
||||||
|
| architecture-gate | 架构 ratchet 违规 | 读取违规文件,用 service 层替代直接 database import |
|
||||||
|
| backend-lint (go vet) | `file.go:42: unreachable code` | 读取文件,修复具体问题 |
|
||||||
|
| backend-lint (gofmt) | `Files need gofmt:` | 运行 `gofmt -s -w` |
|
||||||
|
| backend-lint (go test) | `--- FAIL: TestXxx` | 读取测试和实现,修复根因 |
|
||||||
|
| frontend-lint (ESLint) | `warnings N > 4600` | 对比 main,只修复新增警告 |
|
||||||
|
| frontend-lint (Prettier) | `unformatted N > 1000` | `npx prettier --write` 变更文件 |
|
||||||
|
| frontend-lint (TS) | `errors N > 60` | 对比 main,只修复新增类型错误 |
|
||||||
|
|
||||||
|
**`/req test [REQ-ID]`** — 测试(前置:代码评审通过),遵循 dev-test 技能的 5-Gate 流程
|
||||||
|
|
||||||
|
**`/req deploy [--project <name>] [--env production]`** — 项目级批量部署(Staging 已自动化,push develop 即触发):
|
||||||
|
1. 收集待部署需求(delivery_stage=testing + 全 test 任务 completed)
|
||||||
|
2. AskUserQuestion 确认范围
|
||||||
|
3. `ai-proj task create` 创建【部署】批次任务(linkRole=deploy)
|
||||||
|
4. 部署前检查(变更检测、数据库迁移)
|
||||||
|
5. 执行 `./scripts/build-and-push.sh prod --detect --deploy --wait --verify`
|
||||||
|
6. **Deploy Gate 1: 健康检查** — `/health` 返回 200
|
||||||
|
7. **Deploy Gate 2+3: PDV 验收**(为每个需求创建独立验收任务):
|
||||||
|
a. `ai-proj task create --title "【验收】PDV: {需求标题}"` 创建验收任务
|
||||||
|
b. `ai-proj req link --id <req_id> --task-ids <task_id> --link-role verification` 关联需求
|
||||||
|
c. 从需求关联任务中提取前端变更范围(页面路由、菜单项、关键 API)
|
||||||
|
d. 生成验收检查清单(页面可达 + 菜单可见 + 基础渲染 + API 连通)
|
||||||
|
e. 用 Playwright 对部署环境执行冒烟测试:`E2E_BASE_URL=<部署环境URL> npx playwright test e2e/pdv/ --project=chromium`
|
||||||
|
f. 收集截图证据
|
||||||
|
g. `ai-proj task append-doc --id <task_id>` 附加验收文档(检查项表格 + 截图 + 结论)
|
||||||
|
h. **全部通过** → `ai-proj task complete --id <task_id>` → Gate 2+3 放行
|
||||||
|
i. **任一失败** → 任务保持 in_progress,**阻断推进**,报告失败项
|
||||||
|
8. `ai-proj task append-doc` 记录【部署】任务文档(构建日志 + 健康检查 + PDV 结果摘要)
|
||||||
|
9. `ai-proj req advance --id <id> --to released` 批量推进(仅 Gate 1-3 全部通过的需求)
|
||||||
|
|
||||||
|
**`/req done [REQ-ID]`** — 类型化归档门禁 + git commit + push + `ai-proj req archive --id <id>`:
|
||||||
|
|
||||||
|
**Gate 0(强制):AC 验收逐条确认** — 在所有类型检查之前执行:
|
||||||
|
1. `ai-proj req get --id <id>` 获取需求描述
|
||||||
|
2. 提取 `## 验收标准` 章节下的所有 `- [ ]` / `- [x]` 条目
|
||||||
|
3. **无验收标准**:AskUserQuestion 警告并要求用户确认为何无 AC,必须说明跳过原因才能继续
|
||||||
|
4. **有未勾选项(`- [ ]`)**:逐条列出,AskUserQuestion 请用户对每条提供验证证据(截图/curl 输出/日志)或明确标记为 N/A + 说明原因
|
||||||
|
5. **全部已勾选(`- [x]`)**:展示 AC 摘要,直接继续
|
||||||
|
6. AC 确认通过后,将每条 AC 的验证结果追加到需求描述(`ai-proj req update`),格式:`- [x] {原AC内容}(验证于 YYYY-MM-DD)`
|
||||||
|
|
||||||
|
**类型检查(Gate 0 通过后)**:
|
||||||
|
- **推断类型**:有 implementation → code;无 implementation 有 prd/test → skill;仅 deploy → ops
|
||||||
|
- **code 检查**:delivery_stage=released + deploy 任务完成 + verification 任务完成(PDV 通过) + 部署文档 + 所有任务完成
|
||||||
|
- **skill 检查**:delivery_stage≥testing + 所有任务完成
|
||||||
|
- **ops 检查**:deploy 任务完成 + 所有任务完成
|
||||||
|
|
||||||
|
### PRD 文档创建(通过任务中转)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create-and-attach 只支持任务 ID(task_documents 外键约束)
|
||||||
|
# 1. 创建任务
|
||||||
|
ai-proj task create --title "【评审】PRD: 用户认证功能"
|
||||||
|
|
||||||
|
# 2. 关联需求
|
||||||
|
ai-proj req link --id 711 --task-ids 5214
|
||||||
|
|
||||||
|
# 3. 附加文档到任务
|
||||||
|
ai-proj task append-doc --id 5214 --content "# PRD: 用户认证功能\n..."
|
||||||
|
```
|
||||||
|
|
||||||
|
### 阶段任务 Checklist
|
||||||
|
|
||||||
|
| 阶段 | 建议任务 | linkRole |
|
||||||
|
|------|----------|----------|
|
||||||
|
| analysis | 【评审】PRD: {需求标题} | prd |
|
||||||
|
| design | 【评审】技术设计: {需求标题} | design |
|
||||||
|
| dev | 【开发-后端】{需求标题}、【开发-前端】{需求标题} | implementation |
|
||||||
|
| review | 【代码评审】{需求标题} | code_review |
|
||||||
|
| review | 【约定】{约定名称}(bug 类需求,convention flow 自动创建) | documentation |
|
||||||
|
| testing | 【测试】集成测试: {需求标题} | test |
|
||||||
|
| deploy | 由 `/req deploy` 批量创建 | deploy |
|
||||||
|
| released | 【验收】PDV: {需求标题}(`/req deploy` 自动创建) | verification |
|
||||||
|
|
||||||
@@ -2,9 +2,6 @@
|
|||||||
name: req-prd
|
name: req-prd
|
||||||
description: 产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。
|
description: 产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。
|
||||||
---
|
---
|
||||||
|
|
||||||
# 产品需求设计 Skill (req-prd)
|
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
本技能用于辅助产品设计和需求管理工作,包括:
|
本技能用于辅助产品设计和需求管理工作,包括:
|
||||||
@@ -469,104 +466,6 @@ mcp__ai-proj__export_task_document_to_file
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 功能设计流程
|
|
||||||
|
|
||||||
### 1. 需求收集
|
|
||||||
|
|
||||||
```
|
|
||||||
输入:
|
|
||||||
- 用户反馈
|
|
||||||
- 业务需求
|
|
||||||
- 数据分析
|
|
||||||
- 竞品分析
|
|
||||||
|
|
||||||
输出:
|
|
||||||
- 需求池(ai-proj 需求列表)
|
|
||||||
```
|
|
||||||
|
|
||||||
若命中“模块设计访谈模式”,本阶段改为执行访谈协议并持续写入 ai-proj 讨论文档;访谈未确认前不进入 PRD 定稿。
|
|
||||||
|
|
||||||
### 2. 需求分析
|
|
||||||
|
|
||||||
```
|
|
||||||
方法:
|
|
||||||
- 5W1H 分析法
|
|
||||||
- 用户访谈
|
|
||||||
- 数据验证
|
|
||||||
|
|
||||||
输出:
|
|
||||||
- 需求文档
|
|
||||||
- 优先级排序
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 方案设计
|
|
||||||
|
|
||||||
```
|
|
||||||
内容:
|
|
||||||
- 功能架构
|
|
||||||
- 交互流程
|
|
||||||
- 界面原型
|
|
||||||
- 技术方案
|
|
||||||
|
|
||||||
输出:
|
|
||||||
- PRD 文档
|
|
||||||
- 可生成原型的界面状态与交互规格
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 缺陷收敛
|
|
||||||
|
|
||||||
```
|
|
||||||
输入:
|
|
||||||
- 已确认讨论结论
|
|
||||||
- 最新版完整 PRD
|
|
||||||
|
|
||||||
执行:
|
|
||||||
- defect-analysis 全维度审计
|
|
||||||
- 接受项修订 PRD
|
|
||||||
- 对修订后的完整 PRD 重新审计,直至一轮 0 个新缺陷
|
|
||||||
|
|
||||||
输出:
|
|
||||||
- 已收敛 PRD
|
|
||||||
- 缺陷处置记录
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. HTML 原型与反馈闭环
|
|
||||||
|
|
||||||
```
|
|
||||||
适用:
|
|
||||||
- 所有包含界面、用户操作或可视状态的模块
|
|
||||||
|
|
||||||
执行:
|
|
||||||
- 调用 req-prototype 的 upload 模式生成独立 HTML
|
|
||||||
- 上传并关联 Requirement
|
|
||||||
- iframe 回填 PRD,验证访问和关键交互
|
|
||||||
- 请用户评审;行为性反馈回到问答 → PRD → defect-analysis → 新原型版本
|
|
||||||
|
|
||||||
输出:
|
|
||||||
- 已验证、已关联的 HTML 原型
|
|
||||||
- PRD 与讨论文档中的版本/反馈/确认记录
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. 评审验证
|
|
||||||
|
|
||||||
```
|
|
||||||
评审维度:
|
|
||||||
- 业务价值
|
|
||||||
- 技术可行性
|
|
||||||
- 资源评估
|
|
||||||
- 风险评估
|
|
||||||
|
|
||||||
输出:
|
|
||||||
- 评审结论
|
|
||||||
- 修改意见
|
|
||||||
```
|
|
||||||
|
|
||||||
模块设计访谈模式下,本阶段必须调用 `defect-analysis`,并按访谈协议将每轮发现、处置、PRD 修订和收敛结论回写到同一讨论文档。
|
|
||||||
|
|
||||||
UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用户对二者的联合确认;无 UI 模块则核对已记录的不适用理由和用户确认。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 竞品分析
|
## 竞品分析
|
||||||
|
|
||||||
> 竞品分析模板已移至 `req-compare` 插件。涉及竞品对比时自动激活。
|
> 竞品分析模板已移至 `req-compare` 插件。涉及竞品对比时自动激活。
|
||||||
@@ -605,51 +504,6 @@ UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 设计检查清单
|
|
||||||
|
|
||||||
### PRD 完整性检查
|
|
||||||
|
|
||||||
- [ ] 模块/系统设计已完成单轮单问访谈,且全过程已写入 ai-proj 讨论文档
|
|
||||||
- [ ] 讨论记录与 PRD 均已保存到仓库本地 Markdown 和对应 ai-proj Task Document
|
|
||||||
- [ ] 两端复读正文一致,交付说明包含本地路径、task/document 标识和远程版本
|
|
||||||
- [ ] 讨论结论已由用户明确确认
|
|
||||||
- [ ] `defect-analysis` 已基于最新版 PRD 收敛到一轮 0 个新缺陷
|
|
||||||
- [ ] 无未处置的致命/高严重度缺陷
|
|
||||||
- [ ] UI 模块 HTML 原型已生成、上传并关联 Requirement;无 UI 模块已记录不适用理由和用户确认
|
|
||||||
- [ ] 原型基线指向最新版 PRD,PRD `4.2` 已回填 iframe、版本说明和验证结果
|
|
||||||
- [ ] 原型反馈导致的行为变更已回到问答、PRD 和 defect 全量重审,并生成新原型版本
|
|
||||||
- [ ] 用户已联合确认最终 PRD 与 HTML 原型
|
|
||||||
- [ ] 背景与目标明确
|
|
||||||
- [ ] 用户群体定义清晰
|
|
||||||
- [ ] 功能需求完整
|
|
||||||
- [ ] 验收标准可测试(每条 AC 附带 VP-Data / VP-Steps / VP-Pass)
|
|
||||||
- [ ] 异常情况已考虑
|
|
||||||
- [ ] 性能要求已定义
|
|
||||||
- [ ] 上线计划合理
|
|
||||||
- [ ] 风险已评估
|
|
||||||
|
|
||||||
### 交互设计检查
|
|
||||||
|
|
||||||
- [ ] 用户流程完整
|
|
||||||
- [ ] HTML 原型覆盖核心入口、主流程及 PRD 指定的关键状态
|
|
||||||
- [ ] 原型 URL 可访问,Requirement 关联可读取,iframe 可展示,核心交互可操作
|
|
||||||
- [ ] 边界情况处理
|
|
||||||
- [ ] 错误提示友好
|
|
||||||
- [ ] 反馈及时
|
|
||||||
- [ ] 操作可撤销
|
|
||||||
- [ ] 符合用户习惯
|
|
||||||
|
|
||||||
### 非功能需求检查
|
|
||||||
|
|
||||||
- [ ] 性能要求已量化(响应时间、并发量)
|
|
||||||
- [ ] 安全需求已明确(权限、数据保护)
|
|
||||||
- [ ] 兼容性要求已定义(浏览器、设备)
|
|
||||||
- [ ] 可用性目标已设定
|
|
||||||
|
|
||||||
> **技术方案可行性检查**在 design 阶段由 `req-design` 技能完成。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 常用工具
|
## 常用工具
|
||||||
|
|
||||||
### 原型设计
|
### 原型设计
|
||||||
@@ -696,31 +550,11 @@ UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
|
||||||
|
|
||||||
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。**
|
## 详细内容
|
||||||
|
|
||||||
### 禁止行为
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
|
|
||||||
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
|
|
||||||
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
|
|
||||||
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
|
|
||||||
|
|
||||||
### 允许行为
|
- 功能设计流程
|
||||||
- memory 可影响**交互风格**(提问详略、确认节奏、语气)
|
- 设计检查清单
|
||||||
- memory 可指导**命令选择**(如根据用户习惯推荐先走 req-compare 还是 req-prd)
|
- Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||||
- memory 可影响**非产出文本**(如对话中的说明)
|
|
||||||
|
|
||||||
### Why
|
|
||||||
auto-memory 设计用于跨会话建立用户画像。但 PRD/需求文档是正式产出物,必须由**模板结构 + 当前输入**决定,不能因 memory 中的偏好自作主张调整结构,否则会导致:
|
|
||||||
- 模板章节漂移(用户不知道为什么这次少了一章)
|
|
||||||
- 历史项目内容污染(张冠李戴)
|
|
||||||
- 产出不可复现
|
|
||||||
|
|
||||||
### How to apply
|
|
||||||
执行 `/req prd` / PRD 编写 / 需求描述生成等命令时:
|
|
||||||
- 仅读取:模板文件、用户当前输入、引用的已有需求文档
|
|
||||||
- 不读取:memory 目录下的任何文件
|
|
||||||
- 产出前自检:章节数量和顺序与模板完全一致
|
|
||||||
|
|
||||||
**参考**:devflow-claude 的 `plugins/req/commands/_common.md` 同名规则。
|
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 功能设计流程
|
||||||
|
|
||||||
|
### 1. 需求收集
|
||||||
|
|
||||||
|
```
|
||||||
|
输入:
|
||||||
|
- 用户反馈
|
||||||
|
- 业务需求
|
||||||
|
- 数据分析
|
||||||
|
- 竞品分析
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 需求池(ai-proj 需求列表)
|
||||||
|
```
|
||||||
|
|
||||||
|
若命中“模块设计访谈模式”,本阶段改为执行访谈协议并持续写入 ai-proj 讨论文档;访谈未确认前不进入 PRD 定稿。
|
||||||
|
|
||||||
|
### 2. 需求分析
|
||||||
|
|
||||||
|
```
|
||||||
|
方法:
|
||||||
|
- 5W1H 分析法
|
||||||
|
- 用户访谈
|
||||||
|
- 数据验证
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 需求文档
|
||||||
|
- 优先级排序
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 方案设计
|
||||||
|
|
||||||
|
```
|
||||||
|
内容:
|
||||||
|
- 功能架构
|
||||||
|
- 交互流程
|
||||||
|
- 界面原型
|
||||||
|
- 技术方案
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- PRD 文档
|
||||||
|
- 可生成原型的界面状态与交互规格
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 缺陷收敛
|
||||||
|
|
||||||
|
```
|
||||||
|
输入:
|
||||||
|
- 已确认讨论结论
|
||||||
|
- 最新版完整 PRD
|
||||||
|
|
||||||
|
执行:
|
||||||
|
- defect-analysis 全维度审计
|
||||||
|
- 接受项修订 PRD
|
||||||
|
- 对修订后的完整 PRD 重新审计,直至一轮 0 个新缺陷
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 已收敛 PRD
|
||||||
|
- 缺陷处置记录
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. HTML 原型与反馈闭环
|
||||||
|
|
||||||
|
```
|
||||||
|
适用:
|
||||||
|
- 所有包含界面、用户操作或可视状态的模块
|
||||||
|
|
||||||
|
执行:
|
||||||
|
- 调用 req-prototype 的 upload 模式生成独立 HTML
|
||||||
|
- 上传并关联 Requirement
|
||||||
|
- iframe 回填 PRD,验证访问和关键交互
|
||||||
|
- 请用户评审;行为性反馈回到问答 → PRD → defect-analysis → 新原型版本
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 已验证、已关联的 HTML 原型
|
||||||
|
- PRD 与讨论文档中的版本/反馈/确认记录
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 评审验证
|
||||||
|
|
||||||
|
```
|
||||||
|
评审维度:
|
||||||
|
- 业务价值
|
||||||
|
- 技术可行性
|
||||||
|
- 资源评估
|
||||||
|
- 风险评估
|
||||||
|
|
||||||
|
输出:
|
||||||
|
- 评审结论
|
||||||
|
- 修改意见
|
||||||
|
```
|
||||||
|
|
||||||
|
模块设计访谈模式下,本阶段必须调用 `defect-analysis`,并按访谈协议将每轮发现、处置、PRD 修订和收敛结论回写到同一讨论文档。
|
||||||
|
|
||||||
|
UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用户对二者的联合确认;无 UI 模块则核对已记录的不适用理由和用户确认。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 设计检查清单
|
||||||
|
|
||||||
|
### PRD 完整性检查
|
||||||
|
|
||||||
|
- [ ] 模块/系统设计已完成单轮单问访谈,且全过程已写入 ai-proj 讨论文档
|
||||||
|
- [ ] 讨论记录与 PRD 均已保存到仓库本地 Markdown 和对应 ai-proj Task Document
|
||||||
|
- [ ] 两端复读正文一致,交付说明包含本地路径、task/document 标识和远程版本
|
||||||
|
- [ ] 讨论结论已由用户明确确认
|
||||||
|
- [ ] `defect-analysis` 已基于最新版 PRD 收敛到一轮 0 个新缺陷
|
||||||
|
- [ ] 无未处置的致命/高严重度缺陷
|
||||||
|
- [ ] UI 模块 HTML 原型已生成、上传并关联 Requirement;无 UI 模块已记录不适用理由和用户确认
|
||||||
|
- [ ] 原型基线指向最新版 PRD,PRD `4.2` 已回填 iframe、版本说明和验证结果
|
||||||
|
- [ ] 原型反馈导致的行为变更已回到问答、PRD 和 defect 全量重审,并生成新原型版本
|
||||||
|
- [ ] 用户已联合确认最终 PRD 与 HTML 原型
|
||||||
|
- [ ] 背景与目标明确
|
||||||
|
- [ ] 用户群体定义清晰
|
||||||
|
- [ ] 功能需求完整
|
||||||
|
- [ ] 验收标准可测试(每条 AC 附带 VP-Data / VP-Steps / VP-Pass)
|
||||||
|
- [ ] 异常情况已考虑
|
||||||
|
- [ ] 性能要求已定义
|
||||||
|
- [ ] 上线计划合理
|
||||||
|
- [ ] 风险已评估
|
||||||
|
|
||||||
|
### 交互设计检查
|
||||||
|
|
||||||
|
- [ ] 用户流程完整
|
||||||
|
- [ ] HTML 原型覆盖核心入口、主流程及 PRD 指定的关键状态
|
||||||
|
- [ ] 原型 URL 可访问,Requirement 关联可读取,iframe 可展示,核心交互可操作
|
||||||
|
- [ ] 边界情况处理
|
||||||
|
- [ ] 错误提示友好
|
||||||
|
- [ ] 反馈及时
|
||||||
|
- [ ] 操作可撤销
|
||||||
|
- [ ] 符合用户习惯
|
||||||
|
|
||||||
|
### 非功能需求检查
|
||||||
|
|
||||||
|
- [ ] 性能要求已量化(响应时间、并发量)
|
||||||
|
- [ ] 安全需求已明确(权限、数据保护)
|
||||||
|
- [ ] 兼容性要求已定义(浏览器、设备)
|
||||||
|
- [ ] 可用性目标已设定
|
||||||
|
|
||||||
|
> **技术方案可行性检查**在 design 阶段由 `req-design` 技能完成。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||||
|
|
||||||
|
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。**
|
||||||
|
|
||||||
|
### 禁止行为
|
||||||
|
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
|
||||||
|
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
|
||||||
|
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
|
||||||
|
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
|
||||||
|
|
||||||
|
### 允许行为
|
||||||
|
- memory 可影响**交互风格**(提问详略、确认节奏、语气)
|
||||||
|
- memory 可指导**命令选择**(如根据用户习惯推荐先走 req-compare 还是 req-prd)
|
||||||
|
- memory 可影响**非产出文本**(如对话中的说明)
|
||||||
|
|
||||||
|
### Why
|
||||||
|
auto-memory 设计用于跨会话建立用户画像。但 PRD/需求文档是正式产出物,必须由**模板结构 + 当前输入**决定,不能因 memory 中的偏好自作主张调整结构,否则会导致:
|
||||||
|
- 模板章节漂移(用户不知道为什么这次少了一章)
|
||||||
|
- 历史项目内容污染(张冠李戴)
|
||||||
|
- 产出不可复现
|
||||||
|
|
||||||
|
### How to apply
|
||||||
|
执行 `/req prd` / PRD 编写 / 需求描述生成等命令时:
|
||||||
|
- 仅读取:模板文件、用户当前输入、引用的已有需求文档
|
||||||
|
- 不读取:memory 目录下的任何文件
|
||||||
|
- 产出前自检:章节数量和顺序与模板完全一致
|
||||||
|
|
||||||
|
**参考**:devflow-claude 的 `plugins/req/commands/_common.md` 同名规则。
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: req-test-gate
|
name: req-test-gate
|
||||||
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。
|
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。
|
||||||
arguments: <subcommand> [args]
|
metadata:
|
||||||
|
arguments: <subcommand> [args]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 测试与质量门禁制度 (req-test-gate) v2.1
|
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
三层质量门禁架构,确保从需求到上线每个阶段都有结构化验证。
|
三层质量门禁架构,确保从需求到上线每个阶段都有结构化验证。
|
||||||
@@ -348,48 +346,6 @@ Task({
|
|||||||
### 完整模板(scope = backend / fullstack)
|
### 完整模板(scope = backend / fullstack)
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## 测试报告 - {需求标题}
|
|
||||||
|
|
||||||
| 字段 | 值 |
|
|
||||||
|------|-----|
|
|
||||||
| 需求 | REQ-XXXX |
|
|
||||||
| 类型 | {code/skill/ops} |
|
|
||||||
| 时间 | {YYYY-MM-DD HH:MM} |
|
|
||||||
| 门禁版本 | test-gate v2 |
|
|
||||||
|
|
||||||
### 前置条件 (Gate 1)
|
|
||||||
- CR 状态: ✅ 已通过
|
|
||||||
- 开发任务: ✅ 全部完成 ({N}/{N})
|
|
||||||
|
|
||||||
### 测试执行 (Gate 2)
|
|
||||||
| 序号 | 阶段 | 测试项 | 命令/方法 | 结果 | 备注 |
|
|
||||||
|------|------|--------|----------|------|------|
|
|
||||||
| T1 | 2A | 后端编译 | ... | ✅/❌/⏭️ | ... |
|
|
||||||
| IT1 | 2C | 前端代理连通 | ... | ✅/❌/⏭️ | ... |
|
|
||||||
| SM1 | 2D | 健康检查 | ... | ✅/❌/⏭️ | ... |
|
|
||||||
| PC1 | 2E | 结构完整性 | ... | ✅/❌/⏭️ | ... |
|
|
||||||
|
|
||||||
### 跳过的测试
|
|
||||||
⏭️ {测试项}: {原因}
|
|
||||||
(无则写「无」)
|
|
||||||
|
|
||||||
### 质量验证 (Gate 3)
|
|
||||||
{子代理结果摘要,或「未触发(ops 类型)」}
|
|
||||||
|
|
||||||
### 回归贡献 (Gate 5)
|
|
||||||
{贡献的回归用例数,或「无需贡献」或「⏭️ 跳过: 过渡期」}
|
|
||||||
|
|
||||||
### 总结
|
|
||||||
通过: {N}/{M} | 跳过: {K} | 结论: ✅ 通过 / ❌ 未通过
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文档质量门禁(被 `/req next` 检查)
|
|
||||||
|
|
||||||
- 含测试项表格(`|` 分隔符 ≥ 3 行)
|
|
||||||
- 含通过/失败结论(`✅ 通过` 或 `❌ 未通过`)
|
|
||||||
- 总字符数 ≥ 200
|
|
||||||
- 门禁版本标记(`test-gate v2` 或 `test-gate v2.1`)
|
|
||||||
|
|
||||||
## Gate 5: 回归贡献检查
|
## Gate 5: 回归贡献检查
|
||||||
|
|
||||||
**确保 code 类型需求为回归池贡献测试用例。按 scope 区分贡献形式。**
|
**确保 code 类型需求为回归池贡献测试用例。按 scope 区分贡献形式。**
|
||||||
@@ -504,49 +460,10 @@ v3: Gate 0A(scope) + 0B(约定检查) → Gate 1 → Gate 2(按scope裁剪)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Harness Engineering — 工程约束方法论
|
|
||||||
|
|
||||||
**项目级基础设施**的建立和维护方法论,与上述需求级门禁互补。
|
## 详细内容
|
||||||
|
|
||||||
> 详见 [harness-engineering.md](harness-engineering.md) 完整文档。
|
以下章节已移到 [references/details.md](references/details.md):
|
||||||
|
|
||||||
### 核心理念
|
- 测试报告 - {需求标题}
|
||||||
|
- Harness Engineering — 工程约束方法论
|
||||||
**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题。**
|
|
||||||
|
|
||||||
三大支柱:
|
|
||||||
1. **Ratchet** — 遗留违规数只能减不能增 → [ratchet-pattern.md](ratchet-pattern.md)
|
|
||||||
2. **约定建立** — 每个 bug 修复后立即建立约定和检测 → [convention-flow.md](convention-flow.md)
|
|
||||||
3. **门禁层级** — 按项目成熟度逐级引入 → [project-bootstrap.md](project-bootstrap.md)
|
|
||||||
|
|
||||||
### `/harness` 命令集(手动可用 + 自动嵌入)
|
|
||||||
|
|
||||||
| 命令 | 手动 | 自动触发 |
|
|
||||||
|------|------|---------|
|
|
||||||
| `/harness assess` | 随时 | 首次 `/req dev` |
|
|
||||||
| `/harness init [level]` | 随时 | — |
|
|
||||||
| `/harness convention [name]` | 随时 | `/req cr` + bug 类型需求 |
|
|
||||||
| `/harness report` | 随时 | `/req test` Gate 0B |
|
|
||||||
|
|
||||||
### 模板
|
|
||||||
|
|
||||||
`templates/` 目录提供可复用的脚本模板:
|
|
||||||
- [ratchet-script.md](templates/ratchet-script.md) — Ratchet 脚本模板(check/baseline/report 三命令)
|
|
||||||
- [convention-script.md](templates/convention-script.md) — 约定检测脚本模板(grep 模式匹配)
|
|
||||||
- [gates-doc.md](templates/gates-doc.md) — GATES.md 文档模板
|
|
||||||
- [pre-commit-config.md](templates/pre-commit-config.md) — husky + lint-staged 配置(Node.js/Go/Monorepo)
|
|
||||||
|
|
||||||
### 与需求级门禁的关系
|
|
||||||
|
|
||||||
```
|
|
||||||
Harness(项目级护栏) req-test-gate(需求级门禁)
|
|
||||||
├── .husky/ pre-commit ├── Gate 0A: scope 分级
|
|
||||||
├── CI lint workflow ├── Gate 0B: 运行 harness 的 check-*.sh ←─ 连接点
|
|
||||||
├── scripts/check-*.sh ├── Gate 1: 前置条件
|
|
||||||
├── CLAUDE.md 约定 ├── Gate 2: 测试执行
|
|
||||||
│ ├── Gate 3: 质量验证
|
|
||||||
│ ├── Gate 4: 文档持久化
|
|
||||||
│ └── Gate 5: 回归贡献
|
|
||||||
```
|
|
||||||
|
|
||||||
> Harness 不使用 Gate 编号。它的产出物(check-*.sh 脚本)通过 Gate 0B 接入 req-test-gate 流程。
|
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# skills 详细内容
|
||||||
|
|
||||||
|
## 测试报告 - {需求标题}
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| 需求 | REQ-XXXX |
|
||||||
|
| 类型 | {code/skill/ops} |
|
||||||
|
| 时间 | {YYYY-MM-DD HH:MM} |
|
||||||
|
| 门禁版本 | test-gate v2 |
|
||||||
|
|
||||||
|
### 前置条件 (Gate 1)
|
||||||
|
- CR 状态: ✅ 已通过
|
||||||
|
- 开发任务: ✅ 全部完成 ({N}/{N})
|
||||||
|
|
||||||
|
### 测试执行 (Gate 2)
|
||||||
|
| 序号 | 阶段 | 测试项 | 命令/方法 | 结果 | 备注 |
|
||||||
|
|------|------|--------|----------|------|------|
|
||||||
|
| T1 | 2A | 后端编译 | ... | ✅/❌/⏭️ | ... |
|
||||||
|
| IT1 | 2C | 前端代理连通 | ... | ✅/❌/⏭️ | ... |
|
||||||
|
| SM1 | 2D | 健康检查 | ... | ✅/❌/⏭️ | ... |
|
||||||
|
| PC1 | 2E | 结构完整性 | ... | ✅/❌/⏭️ | ... |
|
||||||
|
|
||||||
|
### 跳过的测试
|
||||||
|
⏭️ {测试项}: {原因}
|
||||||
|
(无则写「无」)
|
||||||
|
|
||||||
|
### 质量验证 (Gate 3)
|
||||||
|
{子代理结果摘要,或「未触发(ops 类型)」}
|
||||||
|
|
||||||
|
### 回归贡献 (Gate 5)
|
||||||
|
{贡献的回归用例数,或「无需贡献」或「⏭️ 跳过: 过渡期」}
|
||||||
|
|
||||||
|
### 总结
|
||||||
|
通过: {N}/{M} | 跳过: {K} | 结论: ✅ 通过 / ❌ 未通过
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文档质量门禁(被 `/req next` 检查)
|
||||||
|
|
||||||
|
- 含测试项表格(`|` 分隔符 ≥ 3 行)
|
||||||
|
- 含通过/失败结论(`✅ 通过` 或 `❌ 未通过`)
|
||||||
|
- 总字符数 ≥ 200
|
||||||
|
- 门禁版本标记(`test-gate v2` 或 `test-gate v2.1`)
|
||||||
|
|
||||||
|
## Harness Engineering — 工程约束方法论
|
||||||
|
|
||||||
|
**项目级基础设施**的建立和维护方法论,与上述需求级门禁互补。
|
||||||
|
|
||||||
|
> 详见 [harness-engineering.md](harness-engineering.md) 完整文档。
|
||||||
|
|
||||||
|
### 核心理念
|
||||||
|
|
||||||
|
**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题。**
|
||||||
|
|
||||||
|
三大支柱:
|
||||||
|
1. **Ratchet** — 遗留违规数只能减不能增 → [ratchet-pattern.md](ratchet-pattern.md)
|
||||||
|
2. **约定建立** — 每个 bug 修复后立即建立约定和检测 → [convention-flow.md](convention-flow.md)
|
||||||
|
3. **门禁层级** — 按项目成熟度逐级引入 → [project-bootstrap.md](project-bootstrap.md)
|
||||||
|
|
||||||
|
### `/harness` 命令集(手动可用 + 自动嵌入)
|
||||||
|
|
||||||
|
| 命令 | 手动 | 自动触发 |
|
||||||
|
|------|------|---------|
|
||||||
|
| `/harness assess` | 随时 | 首次 `/req dev` |
|
||||||
|
| `/harness init [level]` | 随时 | — |
|
||||||
|
| `/harness convention [name]` | 随时 | `/req cr` + bug 类型需求 |
|
||||||
|
| `/harness report` | 随时 | `/req test` Gate 0B |
|
||||||
|
|
||||||
|
### 模板
|
||||||
|
|
||||||
|
`templates/` 目录提供可复用的脚本模板:
|
||||||
|
- [ratchet-script.md](templates/ratchet-script.md) — Ratchet 脚本模板(check/baseline/report 三命令)
|
||||||
|
- [convention-script.md](templates/convention-script.md) — 约定检测脚本模板(grep 模式匹配)
|
||||||
|
- [gates-doc.md](templates/gates-doc.md) — GATES.md 文档模板
|
||||||
|
- [pre-commit-config.md](templates/pre-commit-config.md) — husky + lint-staged 配置(Node.js/Go/Monorepo)
|
||||||
|
|
||||||
|
### 与需求级门禁的关系
|
||||||
|
|
||||||
|
```
|
||||||
|
Harness(项目级护栏) req-test-gate(需求级门禁)
|
||||||
|
├── .husky/ pre-commit ├── Gate 0A: scope 分级
|
||||||
|
├── CI lint workflow ├── Gate 0B: 运行 harness 的 check-*.sh ←─ 连接点
|
||||||
|
├── scripts/check-*.sh ├── Gate 1: 前置条件
|
||||||
|
├── CLAUDE.md 约定 ├── Gate 2: 测试执行
|
||||||
|
│ ├── Gate 3: 质量验证
|
||||||
|
│ ├── Gate 4: 文档持久化
|
||||||
|
│ └── Gate 5: 回归贡献
|
||||||
|
```
|
||||||
|
|
||||||
|
> Harness 不使用 Gate 编号。它的产出物(check-*.sh 脚本)通过 Gate 0B 接入 req-test-gate 流程。
|
||||||
Reference in New Issue
Block a user