Compare commits
60
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6ef6820e4 | ||
|
|
175c612d35 | ||
|
|
1566e2d3eb | ||
|
|
f7810ec3d9 | ||
|
|
6b338895d7 | ||
|
|
7b221cc767 | ||
|
|
8bc3300928 | ||
|
|
6f5151fcb5 | ||
|
|
e6ebfd6b9f | ||
|
|
26b8fd04b9 | ||
|
|
04e20babc1 | ||
|
|
748ea68aa0 | ||
|
|
26352c4c69 | ||
|
|
6321a8528a | ||
|
|
b390c29af3 | ||
|
|
f41b14ad0a | ||
|
|
237631147a | ||
|
|
7d47762303 | ||
|
|
a7a753b833 | ||
|
|
aecd2ce521 | ||
|
|
41a1032ccf | ||
|
|
d5bdfcfb7c | ||
|
|
d43b414fc9 | ||
|
|
dde146995e | ||
|
|
d5550d62c5 | ||
|
|
c74fe71f0a | ||
|
|
2e0f7fe68c | ||
|
|
3ffee89840 | ||
|
|
998b22e905 | ||
|
|
826bba8485 | ||
|
|
f5c5bd3f40 | ||
|
|
4e6ae9036a | ||
|
|
3b38deb078 | ||
|
|
16e58047a0 | ||
|
|
dec25562a4 | ||
|
|
d630b374a3 | ||
|
|
8fc1cd05b7 | ||
|
|
b859a84455 | ||
|
|
9a1400758e | ||
|
|
a58dd1aff3 | ||
|
|
bb5e6be73e | ||
|
|
ef0e9ca1f0 | ||
|
|
ad4e2b16a8 | ||
|
|
ba107a54a8 | ||
|
|
4ea6920a13 | ||
|
|
039a749e3e | ||
|
|
daca19a262 | ||
|
|
ddb5d5cb06 | ||
|
|
da57cccf96 | ||
|
|
7eed2b8f10 | ||
|
|
5a45916b2c | ||
|
|
bcea648e3c | ||
|
|
e3513f137b | ||
|
|
011916ceb9 | ||
|
|
48b792fb5a | ||
|
|
2ab0a61eb9 | ||
|
|
de25f096e7 | ||
|
|
84d4e35a42 | ||
|
|
e5805cbb51 | ||
|
|
79c4e55719 |
+207
-69
@@ -23,6 +23,30 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "pm-ask-plugin",
|
||||
"source": "./skills-core/pm-ask-plugin",
|
||||
"description": "基于真实数据的项目问答 /ask。必须引用 MCP/git 真实数据,禁止编造",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "pm-risk-plugin",
|
||||
"source": "./skills-core/pm-risk-plugin",
|
||||
"description": "三维度项目风险扫描:需求层/代码层/流程层。当用户说'/risk'、'风险扫描'、'有什么风险'时自动激活",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "publish-plugin",
|
||||
"source": "./skills-core/publish-plugin",
|
||||
@@ -47,6 +71,56 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "agent-swarm-plugin",
|
||||
"source": "./skills-dev/agent-swarm-plugin",
|
||||
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "ai-chat-plugin",
|
||||
"source": "./skills-dev/ai-chat-plugin",
|
||||
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "ai-proj-cicd-release-plugin",
|
||||
"source": "./skills-dev/ai-proj-cicd-release-plugin",
|
||||
"description": "执行和审计 AI-Proj 服务从 Gitea 门禁、不可变镜像、预发验证到生产发布和回滚的 CI/CD 流程。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "ai-proj-macos-release-plugin",
|
||||
"source": "./skills-dev/ai-proj-macos-release-plugin",
|
||||
"description": "构建、签名、公证、发布并验证 AI-Proj macOS Apple Silicon 安装包。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "db-migration-plugin",
|
||||
"source": "./skills-dev/db-migration-plugin",
|
||||
@@ -59,6 +133,18 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "defect-analysis-plugin",
|
||||
"source": "./skills-dev/defect-analysis-plugin",
|
||||
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
||||
"version": "1.1.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "deploy-rollback-plugin",
|
||||
"source": "./skills-dev/deploy-rollback-plugin",
|
||||
@@ -241,6 +327,55 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "executing-plans-plugin",
|
||||
"source": "./skills-dev/executing-plans-plugin",
|
||||
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints.",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "finishing-branch-plugin",
|
||||
"source": "./skills-dev/finishing-branch-plugin",
|
||||
"description": "Use when implementation is complete and all tests pass - verifies and creates PR.",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "frontend-design-plugin",
|
||||
"source": "./skills-dev/frontend-design-plugin",
|
||||
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
|
||||
"version": "1.0.1",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "karpathy-guidelines-plugin",
|
||||
"source": "./skills-dev/karpathy-guidelines-plugin",
|
||||
"description": "Karpathy 四原则编码行为守则(Think Before Coding / Simplicity First / Surgical Changes / Goal-Driven Execution)。已深度融合到 req 技能工作流各阶段,可独立激活用于任意编码场景。",
|
||||
"version": "1.0.1",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "pull-request-plugin",
|
||||
"source": "./skills-dev/pull-request-plugin",
|
||||
@@ -258,7 +393,7 @@
|
||||
"name": "review-checklist-plugin",
|
||||
"source": "./skills-dev/review-checklist-plugin",
|
||||
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
@@ -266,6 +401,45 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "slark-cicd-plugin",
|
||||
"source": "./skills-dev/slark-cicd-plugin",
|
||||
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
|
||||
"version": "1.1.0",
|
||||
"category": "devops",
|
||||
"keywords": [
|
||||
"devops",
|
||||
"deployment",
|
||||
"operations"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "slark-dsh-cicd-plugin",
|
||||
"source": "./skills-dev/slark-dsh-cicd-plugin",
|
||||
"description": "DSH 插件包与 Slark 宿主补丁的发布实战技能:npm run pack 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、draft Release、发布后 SHA 核对、asar 完整性与 ai-proj 状态对齐。",
|
||||
"version": "1.0.0",
|
||||
"category": "devops",
|
||||
"keywords": [
|
||||
"devops",
|
||||
"deployment",
|
||||
"operations"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-audit-plugin",
|
||||
"source": "./skills-req/req-audit-plugin",
|
||||
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-compare-plugin",
|
||||
"source": "./skills-req/req-compare-plugin",
|
||||
@@ -305,11 +479,24 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-lookback-plugin",
|
||||
"source": "./skills-req/req-lookback-plugin",
|
||||
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-plugin",
|
||||
"source": "./skills-req/req-plugin",
|
||||
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
@@ -321,8 +508,8 @@
|
||||
{
|
||||
"name": "req-prd-plugin",
|
||||
"source": "./skills-req/req-prd-plugin",
|
||||
"description": "产品需求设计技能。PRD 文档编写、需求分析、用户故事、对比式分析。纯产品视角,不含技术实现。",
|
||||
"version": "2.0.0",
|
||||
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
|
||||
"version": "2.3.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
@@ -334,8 +521,8 @@
|
||||
{
|
||||
"name": "req-prototype-plugin",
|
||||
"source": "./skills-req/req-prototype-plugin",
|
||||
"description": "Stitch 原型生成与迭代。基于 PRD 文档自动生成 UI 原型。",
|
||||
"version": "1.0.0",
|
||||
"description": "原型生成与关联。支持 HTML 本地留源、OSS 正式交付、Requirement/iframe 验证及 Stitch AI。",
|
||||
"version": "2.2.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
@@ -357,6 +544,19 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-retro-plugin",
|
||||
"source": "./skills-req/req-retro-plugin",
|
||||
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-review-plugin",
|
||||
"source": "./skills-req/req-review-plugin",
|
||||
@@ -412,7 +612,7 @@
|
||||
"name": "doubao-voice-plugin",
|
||||
"source": "./skills-integration/doubao-voice-plugin",
|
||||
"description": "Doubao (豆包) Voice API integration for TTS and ASR",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
@@ -548,68 +748,6 @@
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "gitea-plugin",
|
||||
"source": "./skills-personal/gitea-plugin",
|
||||
"description": "Gitea 代码托管与 CI/CD 管理。用于 Gitea Actions workflow 管理、Runner 管理、PR 操作、仓库配置。",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "openclaw-plugin",
|
||||
"source": "./skills-personal/openclaw-plugin",
|
||||
"description": "OpenClaw (龙虾) 远程 AI 计算调度系统 - 概念设计与运维管理",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "ops-tools-plugin",
|
||||
"source": "./skills-personal/ops-tools-plugin",
|
||||
"description": "Plugin for ops-tools",
|
||||
"version": "1.0.0",
|
||||
"category": "devops",
|
||||
"keywords": [
|
||||
"devops",
|
||||
"deployment",
|
||||
"operations"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "qiudl-personal-plugin",
|
||||
"source": "./skills-personal/qiudl-personal-plugin",
|
||||
"description": "Plugin for qiudl-personal",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-deploy-plugin",
|
||||
"source": "./skills-personal/req-deploy-plugin",
|
||||
"description": "Plugin for req-deploy",
|
||||
"version": "1.0.0",
|
||||
"category": "devops",
|
||||
"keywords": [
|
||||
"devops",
|
||||
"deployment",
|
||||
"operations"
|
||||
],
|
||||
"strict": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links: []
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: 新技能提案
|
||||
about: 提新技能或技能改动前先过 SCOPE.md 门槛(默认拒绝,能组合就不新增)
|
||||
title: "[技能] "
|
||||
labels: skill
|
||||
---
|
||||
|
||||
## 1. 真实失败(必填)
|
||||
|
||||
在哪个真实任务/会话里出了问题?跑了什么、期望什么、实际什么。
|
||||
|
||||
> 假设性的「这样会更好」不算。说不清就说明还不到提的时候。
|
||||
|
||||
## 2. 为什么组合不出来(必填)
|
||||
|
||||
用现有哪个技能组合试过?为什么做不到?
|
||||
|
||||
> 能组合出来的,改现有技能,不新增。
|
||||
|
||||
## 3. 是否重复提案
|
||||
|
||||
- [ ] 已读 `.out-of-scope/` 三份拒绝档案,不是重复提案
|
||||
|
||||
## 4. 分桶与调用模式
|
||||
|
||||
- 初始分桶:`beta`(新技能一律先 beta)
|
||||
- 调用模式:`model-invoked` / `user-invoked`
|
||||
- 判定:这件事是不是「人下命令才开始」?是 → `user-invoked`(同时写 `disable-model-invocation: true`)
|
||||
|
||||
## 5. 最低交付
|
||||
|
||||
- [ ] `evals/evals.json` 3 条:正常路径 / 边界 / 失败恢复
|
||||
- [ ] `docs/skills/<name>.md` 四段式:What it does / When to reach for it / Common questions / It's working if
|
||||
- [ ] `docs/skill-routing.md` 已回改
|
||||
- [ ] `catalog.json` 已 regenerate
|
||||
- [ ] `CHANGELOG.md` + `plugin.json` 版本号
|
||||
@@ -0,0 +1,24 @@
|
||||
## 这个 PR 做了什么
|
||||
|
||||
<!-- 一句话,说明改的是哪个技能 / 哪套流程 -->
|
||||
|
||||
## SCOPE 门槛
|
||||
|
||||
- [ ] 真实失败:说清了在哪个真实任务里出的问题
|
||||
- [ ] 组合不出来:说明用现有技能组合为什么做不到
|
||||
- [ ] 已查 `.out-of-scope/`,不是重复提案
|
||||
|
||||
## 改动检查
|
||||
|
||||
- [ ] 分桶正确(新技能一律 `beta`;`promoted` 需要真实使用记录 + evals 通过)
|
||||
- [ ] 调用模式正确(`user-invoked` 必须写 `disable-model-invocation: true`)
|
||||
- [ ] `evals/evals.json` 3 条(正常 / 边界 / 失败恢复)
|
||||
- [ ] `docs/skills/<name>.md` 四段式
|
||||
- [ ] `docs/skill-routing.md` 已回改
|
||||
- [ ] `catalog.json` 已 regenerate(`python3 scripts/generate-catalog.py`)
|
||||
- [ ] `CHANGELOG.md` + `plugin.json` 版本号已更新
|
||||
- [ ] 四道门禁通过:`validate-skills.py --strict`、`scan-skill.sh`、`generate-catalog.py --check`、`tests/install-skills.test.sh`
|
||||
|
||||
## 人工拍板项(有则写明)
|
||||
|
||||
<!-- deprecated 删除、路由变更、promoted 晋升,需要人确认 -->
|
||||
@@ -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
|
||||
@@ -25,3 +25,9 @@ __pycache__/
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
|
||||
# 定时任务产出(本机运行产物,不回写仓库;evals 结果报告除外)
|
||||
reports/skill-health/
|
||||
reports/monthly/
|
||||
reports/_scheduler/
|
||||
reports/evals/queue-*.md
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# 已拒绝:为某一种 harness 单开分支
|
||||
|
||||
- 状态:拒绝(长期有效)
|
||||
- 日期:2026-10-31
|
||||
|
||||
## 想法
|
||||
|
||||
为 Codex / DSH / Claude 某一个客户端的特例,单独复制一份技能或加一个 harness 分支。
|
||||
|
||||
## 为什么拒绝
|
||||
|
||||
1. 复制出来的两份技能会立刻开始漂移,评测也要做两遍;
|
||||
2. 客户端差异应交给安装器(目录、调用模式、字段映射)处理,不该写进技能内容;
|
||||
3. 特例需求通常是个人的工作流调整,属于 `AGENTS.md` / `CLAUDE.md` 或 fork 的范围。
|
||||
|
||||
## 允许的替代路径
|
||||
|
||||
- 客户端差异 → 安装器加 target / 字段映射;
|
||||
- 个人流程 → 个人目录或 fork。
|
||||
@@ -0,0 +1,20 @@
|
||||
# 已拒绝:新增「随手想到」的技能
|
||||
|
||||
- 状态:拒绝(长期有效)
|
||||
- 日期:2026-10-31
|
||||
|
||||
## 想法
|
||||
|
||||
「我觉得应该有个技能做 X」——从想象出发新增技能,而不是从真实失败出发。
|
||||
|
||||
## 为什么拒绝
|
||||
|
||||
1. 技能目录是每次会话启动就要付的上下文预算,加一个就多一份;
|
||||
2. 没有真实失败做依据的技能,写出来是泛泛而谈,实际触发率极低;
|
||||
3. 技能之间会互相抢触发(description 相似),新增一个会稀释已有技能的准确率。
|
||||
|
||||
## 允许的替代路径
|
||||
|
||||
- 先用现有技能做一遍真实任务,记录卡点;
|
||||
- 确实组合不出来,再按 `SCOPE.md` 门槛提,附真实失败证据;
|
||||
- 只是个人偏好 → 放 `skills-personal/`,不进团队库。
|
||||
@@ -0,0 +1,19 @@
|
||||
# 已拒绝:把个人工作流调优塞进团队技能
|
||||
|
||||
- 状态:拒绝(长期有效)
|
||||
- 日期:2026-10-31
|
||||
|
||||
## 想法
|
||||
|
||||
把某个人的偏好(提交信息格式、分支命名、审查语气、工具习惯)直接写进团队技能。
|
||||
|
||||
## 为什么拒绝
|
||||
|
||||
1. 团队技能要能被所有人用,个人偏好会变成隐式规训;
|
||||
2. 一旦写进技能,改起来要走 PR,个人改不动;
|
||||
3. 这类偏好本来就该放个人 `AGENTS.md` / `CLAUDE.md`。
|
||||
|
||||
## 允许的替代路径
|
||||
|
||||
- 个人偏好 → 个人配置文件或 `skills-personal/`;
|
||||
- 如果是团队共识(不是个人偏好),走 `SCOPE.md` 门槛并附真实失败。
|
||||
@@ -0,0 +1,70 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased — 2026-10-31
|
||||
|
||||
### 新增
|
||||
|
||||
- 技能治理:`SCOPE.md` 收录门槛 + `.out-of-scope/` 拒绝档案。
|
||||
- 生命周期分桶(`promoted` / `beta` / `deprecated`)与调用模式(`model-invoked` / `user-invoked`)。
|
||||
- `scripts/validate-skills.py`:规范/大小/引用/调用模式/文档校验。
|
||||
- `scripts/generate-catalog.py`:生成可审阅的 `catalog.json`。
|
||||
- `scripts/scan-skill.sh` + `scripts/scan-allowlist.txt`:入库前安全扫描。
|
||||
- 新技能(beta):`skill-creator`、`ai-proj-helper-entry`、`verification-before-completion`。
|
||||
- `install-skills.sh`:`--agent dsh`、`--link`、`--audit`、`--include-beta`、`--no-validate`。
|
||||
|
||||
### 变更
|
||||
|
||||
- 18 个命令型技能转 `user-invoked`(biz/integration/dev-android|ios|pda/publish/发布链),目录预算减少 18 项。
|
||||
|
||||
- `dev-review`:增加「接收与响应评审意见」。
|
||||
- `req-design`:增加「任务依赖边(blocking edges)」。
|
||||
- `agent-swarm`:增加「并行编排流程」。
|
||||
- `dev-test`:增加「TDD 循环」。
|
||||
- `generate-marketplace.py`:只发布 `promoted`,beta/deprecated 不随 marketplace 分发。
|
||||
|
||||
- 按 SCOPE 降级两个无增量技能:`verification-before-completion`、`ai-proj-helper-entry`(规则已在用户级 AGENTS.md),入口路由内容转为 `docs/skill-routing.md`。
|
||||
|
||||
### 清理(2026-10-31)
|
||||
|
||||
- 11 个超过 20KB 的 `SKILL.md` 拆分:大章节移入 `references/details.md`,正文全部回到 20KB 预算内。
|
||||
- 7 处非标准 frontmatter 字段(`arguments`、`user_invocable`)移入 `metadata`。
|
||||
- 修复 `req-dev` 的 4 条相对链接;校验器改为跳过行内代码,消除 `` 误报。
|
||||
- `validate-skills.py`:23 个警告 → **0 个警告**。
|
||||
|
||||
### 清理(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` 同步。
|
||||
|
||||
### 覆盖率阶段 2(2026-11-01)
|
||||
|
||||
- 补齐剩余 24 个技能(dev 类为主)的 `evals/evals.json`(各 3 条)与 `docs/skills/<name>.md`(四段式)。
|
||||
- `--coverage`:40 个 promoted+model-invoked 技能的 evals 与文档缺口 **24 → 0**。
|
||||
|
||||
### 继续推进(2026-11-01)
|
||||
|
||||
- 新增 beta 技能 `diagnosing-bugs`(难查 bug 诊断纪律:先建红循环再推理),补齐 evals 与四段式文档;路由表同步。
|
||||
- 新增 `scripts/monthly-review.sh`:一条命令跑月度审阅(覆盖率 / 扫描 / 第三方 / catalog / description 异常)。
|
||||
|
||||
### 治理落地:定时任务与提案入口(2026-11-01)
|
||||
|
||||
- 新增 `scripts/scheduled-task.sh`:`daily` / `weekly` / `monthly` 三个确定性任务,只跑门禁、扫漂移、生成队列;需要模型判断的部分(真跑 evals、审阅 routing)生成 queue 文件交 agent。
|
||||
- 新增 `scripts/install-schedulers.sh`:把节拍装成 macOS 用户级 launchd(`com.pipexerp.ai-proj-skill-{daily,weekly,monthly}`),不再依赖会话提醒;支持 `--status` / `--run` / `--uninstall`。
|
||||
- 新增 `.gitea/issue_template/new-skill.md`、`.gitea/issue_template/config.yaml`、`.gitea/pull_request_template.md`:提案与 PR 走同一套 SCOPE 门槛问题。
|
||||
- `README.md` 一键部署从 2 步改为 4 步(补 git hooks 与定时任务);`MAINTENANCE.md` 增加「定时任务」一节,写明脚本只做确定性动作的边界。
|
||||
- `.gitignore` 排除定时任务产出(`reports/skill-health/`、`reports/monthly/`、`reports/_scheduler/`、`reports/evals/queue-*.md`)。
|
||||
- 新增 `scripts/evals-status.py` 与 `reports/evals/TEMPLATE.md`:evals 覆盖率与回归报告模板。
|
||||
- `skill-creator` 实战记录 #1:用标准流程新建 `diagnosing-bugs`(beta,待观察后晋升)。
|
||||
@@ -1,14 +1,14 @@
|
||||
# ai-proj-helper
|
||||
|
||||
Claude Code 技能市场 + MCP 配置管理工具。
|
||||
Codex 优先、兼容 Claude Code 的 Agent Skills 市场与 MCP 配置管理工具。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
./init.sh
|
||||
./install-skills.sh
|
||||
```
|
||||
|
||||
交互式配置 MCP 连接(默认 SSE 模式)+ 自动注册技能市场到 `~/.claude/plugins/known_marketplaces.json`。支持命令行参数:
|
||||
默认安装到 Codex 标准目录 `~/.agents/skills`。Claude Code 的 MCP 与 marketplace 初始化使用:
|
||||
|
||||
```bash
|
||||
./init.sh --mode sse --token aiproj_pk_xxx
|
||||
@@ -18,11 +18,11 @@ Claude Code 技能市场 + MCP 配置管理工具。
|
||||
|
||||
```
|
||||
ai-proj-helper/
|
||||
├── skills-core/ # 基础设施 (1): ai-proj
|
||||
├── skills-dev/ # 开发 (4): dev-arch, dev-coding, dev-test, pull-request
|
||||
├── skills-req/ # 需求 (4): req, req-prd, req-dev, req-test-gate
|
||||
├── skills-integration/ # 集成 (8): feishu, feishu-bitable, feishu-docx, wecom, siyuan, siyuan-to-feishu, data-excel, doubao-voice
|
||||
├── skills-biz/ # 商务 (4): biz-contract, biz-ops, biz-plan, finance
|
||||
├── skills-core/ # 基础设施技能
|
||||
├── skills-dev/ # 开发与发布技能
|
||||
├── skills-req/ # 需求管理技能
|
||||
├── skills-integration/ # 第三方集成技能
|
||||
├── skills-biz/ # 商务技能
|
||||
├── skills-personal/ # 个人(.gitignore 排除)
|
||||
├── claude-config.yaml # 技能启用/禁用 + MCP 配置
|
||||
├── init.sh # MCP 初始化
|
||||
@@ -51,4 +51,11 @@ skills:
|
||||
|
||||
- `skills-personal/` 不被 Git 跟踪,用于存放个人配置和工具
|
||||
- 其余 `skills-*` 目录均由 Git 版本控制
|
||||
- 所有目录都会被 `generate-marketplace.py` 自动扫描并加入 marketplace.json
|
||||
- 只有受 Git 跟踪的分类目录会被 `generate-marketplace.py` 扫描并加入公开 marketplace;`skills-personal/` 始终排除
|
||||
|
||||
## 技能治理规则
|
||||
|
||||
- 公开 marketplace 只发布 `status=promoted`;`beta`/`deprecated` 不进 marketplace(`generate-marketplace.py` 已按 status 过滤)。
|
||||
- 每个 `plugin.json` 建议带 `status`,每个 `SKILL.md` 在 `metadata.invocation` 标 `model-invoked` / `user-invoked`。
|
||||
- 任何技能增删改后,回改 `catalog.json` 与入口技能 `ai-proj-helper-entry`。
|
||||
- 提交前依次跑:`python3 scripts/validate-skills.py`、`./scripts/scan-skill.sh`、`python3 scripts/generate-catalog.py`。
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# 技能维护节奏
|
||||
|
||||
> 配套 `SCOPE.md`(收不收)、`skill-creator`(怎么建)、`docs/skill-routing.md`(用哪个)。
|
||||
> 本文件管「多久做一次什么」。
|
||||
|
||||
## 每次改动(任何分支)
|
||||
|
||||
> **批量提交前先建分支**:`git checkout -b <branch>`,再提交、推分支、开 PR。不要直接 commit 到 main(2026-11-01 阶段 2 漏建分支直接进 main,少了 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`(模板 `reports/evals/TEMPLATE.md`),进周报。
|
||||
- 用 `python3 scripts/evals-status.py` 看哪些技能已有 evals、哪些已有回归报告。
|
||||
- 连续两次无增量的技能 → 按 `SCOPE.md` 评估降级。
|
||||
- 不做全量(61 技能 × 3 用例成本过高);全量只在治理大改时做。
|
||||
|
||||
## 每月 1 日 10:00 — 漂移与目录审阅
|
||||
|
||||
- 两台机器 `--audit` 漂移必须为 0(beta/deprecated 除外)。
|
||||
- 审 `catalog.json`:`description` 长度 <80 或 >600 的标黄;含「各种 / 等等 / 相关」这类泛化词的标出。
|
||||
- 第三方技能(arkcli 等)有变动时,评估是否影响团队技能;跑一次 `./scripts/scan-installed.sh`。
|
||||
- 跑 `./scripts/monthly-review.sh` 一次出齐:覆盖率、本仓库扫描、第三方扫描、catalog 一致性、description 异常项。
|
||||
|
||||
## 每季度首月 1 日 10:30 — 治理复核
|
||||
|
||||
- 人工过一遍 `docs/skill-routing.md`:删掉不再用的引用、补上新技能。
|
||||
- 清理 `deprecated`:满 3 个月且无回滚需求 → 删除插件目录。
|
||||
- 复核分桶与调用模式:还有没有命令型技能占着 `model-invoked`。
|
||||
- 复核覆盖率:`--coverage` 的两项缺口是否按路线图收敛。
|
||||
- 更新本文件与 `CHANGELOG.md`。
|
||||
|
||||
## 定时任务(launchd,2026-11-01 起)
|
||||
|
||||
节拍不再挂在「会话提醒」上(会话一断就失效),改成宿主级 launchd:
|
||||
|
||||
```bash
|
||||
./scripts/install-schedulers.sh # 安装(幂等;换机器重跑一次即可复现)
|
||||
./scripts/install-schedulers.sh --status # 看状态与最近产出
|
||||
./scripts/install-schedulers.sh --run daily # 立刻跑一次(调试)
|
||||
./scripts/install-schedulers.sh --uninstall
|
||||
```
|
||||
|
||||
| 任务 | 时间 | 脚本做什么 | 产出 |
|
||||
| --- | --- | --- | --- |
|
||||
| `daily` | 每天 07:20 | 漂移 audit + 入库安全扫描 + 覆盖率 | `reports/skill-health/daily-YYYY-MM-DD.md`(`latest.md` 供 7:30 日报直接读) |
|
||||
| `weekly` | 每周一 10:00 | 算上周改动技能 + 3 个高风险项,生成 evals 抽样队列 | `reports/evals/queue-YYYY-MM-DD.md` |
|
||||
| `monthly` | 每月 1 日 10:00 | 一条命令跑完月度审阅;1/4/7/10 月附季度治理清单 | `reports/monthly/review-YYYY-MM.md`、`quarterly-YYYY-Qn.md` |
|
||||
|
||||
**边界**:脚本只做确定性动作。真跑 evals、审阅 routing 这类需要模型判断的部分,脚本只生成队列文件,由项目经理 Bot 在 7:30 日报里接手执行——分工不写在脚本里就会漏。
|
||||
|
||||
日志:`reports/_scheduler/<job>.log`。
|
||||
|
||||
## 责任分工
|
||||
|
||||
- 项目经理 Bot:执行节奏、出周/月报、盯漂移与门禁。
|
||||
- 改技能的 agent:负责门禁四件套与路由回改。
|
||||
- 人工拍板项:废弃技能、路由变更、`promoted` 晋升。
|
||||
@@ -1,6 +1,6 @@
|
||||
# ai-proj-helper — 让 Claude Code 变成你的项目经理
|
||||
# ai-proj-helper — 让 Codex / Claude Code 变成你的项目经理
|
||||
|
||||
> 一套开箱即用的 Claude Code 技能包 + MCP 服务,帮你用自然语言管理需求、写代码、做评审、同步飞书,把 AI 助手变成真正的项目经理。
|
||||
> 一套遵循 Agent Skills 标准的技能包 + MCP 服务,支持 Codex,并兼容 Claude Code。
|
||||
|
||||
## 它能帮你做什么
|
||||
|
||||
@@ -66,27 +66,34 @@ PRD 文档存储在思源笔记中,可以导出发送到飞书群,通过飞
|
||||
|
||||
### 前置条件
|
||||
|
||||
- **Claude Code** 已安装([安装指南](https://docs.anthropic.com/en/docs/claude-code/overview))
|
||||
- **Codex**([Skills 文档](https://developers.openai.com/codex/skills))或 **Claude Code** 已安装
|
||||
- **ai-proj 账号 + MCP API Key**:联系管理员获取(Key 格式: `aiproj_pk_xxx`)
|
||||
|
||||
### 一键部署(2 步搞定)
|
||||
### 一键部署(4 步搞定)
|
||||
|
||||
```bash
|
||||
# 1. 克隆仓库
|
||||
git clone https://gitea.pipexerp.com/pipexerp/ai-proj-helper.git
|
||||
cd ai-proj-helper
|
||||
|
||||
# 2. 运行初始化(按提示输入 API Key 即可)
|
||||
./init.sh
|
||||
# 2. 默认安装到 Codex 的用户级标准目录 ~/.agents/skills
|
||||
./install-skills.sh
|
||||
|
||||
# 3. 装本地门禁(Gitea Actions 无 runner,commit/push 靠 git hook 兜底)
|
||||
./scripts/install-git-hooks.sh
|
||||
|
||||
# 4. 装技能治理定时任务(macOS launchd:每日漂移检查 / 每周 evals 队列 / 每月审阅)
|
||||
./scripts/install-schedulers.sh
|
||||
```
|
||||
|
||||
脚本会自动完成:
|
||||
- 配置 MCP 服务器连接(`~/.claude/.mcp.json`)
|
||||
- 注册技能市场到 Claude Code(`~/.claude/plugins/known_marketplaces.json`)
|
||||
第 3、4 步只对「本仓库的维护者」需要——只装技能给别人用的,跑完第 2 步就可以停。
|
||||
|
||||
也支持命令行参数跳过交互:
|
||||
安装器会复制完整技能目录,包括 `SKILL.md`、references、scripts 和 assets。Codex 会自动发现 `~/.agents/skills` 中的技能;若没有出现,重启 Codex。
|
||||
|
||||
Claude Code 用户显式选择 Claude 目标;需要同时配置 MCP 和 marketplace 时运行 `init.sh`:
|
||||
|
||||
```bash
|
||||
./install-skills.sh --agent claude
|
||||
./init.sh --mode sse --token aiproj_pk_xxx
|
||||
```
|
||||
|
||||
@@ -163,7 +170,7 @@ skills:
|
||||
|
||||
- **mode**: MCP 连接模式。`sse` 直连远程服务器(推荐),`stdio` 在本地启动 Node.js 进程
|
||||
- **disabled**: 不需要的技能可以加到这里,重新运行 `./init.sh` 生效
|
||||
- **personal_dir**: 个人技能目录,默认不被 Git 跟踪
|
||||
- **personal_dir**: 本机个人技能目录,默认不被 Git 跟踪,也不会写入公开 marketplace
|
||||
|
||||
## 常见问题
|
||||
|
||||
@@ -171,13 +178,17 @@ skills:
|
||||
|
||||
A: 需要先联系管理员获取 MCP API Key(格式 `aiproj_pk_xxx`),然后在提示处输入。
|
||||
|
||||
**Q: 安装后 Codex 没有识别到技能?**
|
||||
|
||||
A: 确认技能位于 `~/.agents/skills/<name>/SKILL.md`,然后重启 Codex。Codex CLI 也可用 `/skills` 查看。
|
||||
|
||||
**Q: 安装后 Claude Code 没有识别到技能?**
|
||||
|
||||
A: 重启 Claude Code 后生效。如果仍不生效,检查 `~/.claude/plugins/known_marketplaces.json` 中是否包含 `ai-proj-helper` 条目。
|
||||
|
||||
**Q: 如何更新到最新版本?**
|
||||
|
||||
A: 进入项目目录执行 `git pull`,然后重新运行 `./init.sh`。
|
||||
A: 进入项目目录执行 `git pull`,然后运行 `./install-skills.sh`;Claude Code 用户增加 `--agent claude`。
|
||||
|
||||
**Q: 如何禁用不需要的技能?**
|
||||
|
||||
@@ -192,3 +203,22 @@ A: SSE 模式直连远程服务器,零依赖开箱即用(推荐)。stdio
|
||||
- **管理员**:Donglin Lai (qiudl)
|
||||
- **邮箱**:qiudl@zhiyuncai.com
|
||||
- **仓库**:https://gitea.pipexerp.com/pipexerp/ai-proj-helper
|
||||
|
||||
## 技能治理(2026-10-31 起)
|
||||
|
||||
- **真相源**:本仓库。`~/.agents/skills`、`~/.claude/skills`、`$DSH_HOME/skills` 都只是安装产物。
|
||||
- **收录门槛**见 [SCOPE.md](./SCOPE.md):只收「真实失败 + 组合不出来」,被拒的想法记在 `.out-of-scope/`。
|
||||
- **生命周期**:`promoted`(默认安装)/ `beta`(需 `--include-beta`)/ `deprecated`(不安装);新技能一律先进 beta。
|
||||
- **调用模式**:`model-invoked`(占模型目录预算)vs `user-invoked`(只由人触发,不占目录预算)。
|
||||
- **安装**:
|
||||
- Codex:`./install-skills.sh --agent codex`
|
||||
- DSH:`DSH_HOME=<path> ./install-skills.sh --agent dsh --link`
|
||||
- Claude(兼容):`./install-skills.sh --agent claude`
|
||||
- 看漂移:`./install-skills.sh --audit`
|
||||
- **校验 / 安全 / 清单**:`python3 scripts/validate-skills.py`、`./scripts/scan-skill.sh`、`python3 scripts/generate-catalog.py`
|
||||
- **本地门禁(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、四段式文档、路由回改),不要手写。
|
||||
- **路由表**:`docs/skill-routing.md`(什么任务用哪个技能,人读;由原 `ai-proj-helper-entry` 技能降级而来)。
|
||||
- **维护节奏**:`MAINTENANCE.md`(每次改动 / 每日 / 每周 / 每月 / 每季度各做什么)。
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Scope — ai-proj-helper 技能的收录门槛
|
||||
|
||||
本仓库是团队每天在用的技能集,**不是技能收藏夹**。技能目录会在每次会话启动时占用模型的上下文预算,所以「多加一个」永远是有代价的。
|
||||
|
||||
## 门槛
|
||||
|
||||
新技能或改动**默认拒绝**,只有同时满足两条才进入评审:
|
||||
|
||||
1. **真实失败**:能说清在哪个真实任务/会话里出了问题——跑了什么、期望什么、实际什么。假设性的「这样会更好」不算。
|
||||
2. **组合不出来**:用现有技能组合做不到。能组合的,改现有技能,不新增。
|
||||
|
||||
规模不重要:一个词的修改和一次重写按同一标准审。
|
||||
|
||||
## 分桶
|
||||
|
||||
| 桶 | 含义 | 安装 | 进 README / plugin.json |
|
||||
| --- | --- | --- | --- |
|
||||
| `promoted` | 已验证、日常在用 | 默认 | 必须 |
|
||||
| `beta` | 公开试跑、收反馈 | `--include-beta` | 不进默认清单 |
|
||||
| `deprecated` | 不再使用 | 不安装 | 只留文档,标弃用 |
|
||||
|
||||
新技能一律先进 `beta`;promoted 需要真实使用记录 + evals 通过。
|
||||
|
||||
## 调用模式
|
||||
|
||||
- `model-invoked`(默认):进模型目录,agent 自己判断何时用。
|
||||
- `user-invoked`:只由人下命令触发,不占目录预算;必须写 `disable-model-invocation: true` + `metadata.invocation: user-invoked`。
|
||||
|
||||
判定:这件事是不是「人下命令才开始」?是 → user-invoked。
|
||||
|
||||
## 已经拒绝的想法
|
||||
|
||||
每个被拒概念一个文件,写在 [`.out-of-scope/`](./.out-of-scope/) 下,先读再提。当前已有:
|
||||
|
||||
- [`new-skills.md`](./.out-of-scope/new-skills.md)
|
||||
- [`harness-specific-branches.md`](./.out-of-scope/harness-specific-branches.md)
|
||||
- [`personal-workflow-tweaks.md`](./.out-of-scope/personal-workflow-tweaks.md)
|
||||
|
||||
## 提交流程
|
||||
|
||||
1. 先过 [`skill-creator`](./skills-core/skill-creator-plugin/skills/SKILL.md) 的收录门槛与质量门禁。
|
||||
2. `python3 scripts/validate-skills.py` 通过。
|
||||
3. `./scripts/scan-skill.sh` 无高危项。
|
||||
4. `python3 scripts/generate-catalog.py` 更新 `catalog.json`。
|
||||
5. 版本号 + CHANGELOG 更新,走 PR。
|
||||
|
||||
## 覆盖率路线图(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` 看当前缺口,再按阶段补。
|
||||
@@ -1,28 +1,23 @@
|
||||
# Setup Guide
|
||||
# Claude Code Marketplace Setup
|
||||
|
||||
## 1. Create Repository on Gitea
|
||||
本页只描述 Claude Code marketplace。Codex 用户直接运行 `./install-skills.sh`,技能默认安装到 `~/.agents/skills`。
|
||||
|
||||
Go to https://gitea.pipexerp.com and create a new repository:
|
||||
- Name: `claude-marketplace`
|
||||
- Visibility: Private or Public (your choice)
|
||||
- **Do NOT** initialize with README (we already have one)
|
||||
|
||||
## 2. Push to Gitea
|
||||
## 1. Clone the Gitea Repository
|
||||
|
||||
```bash
|
||||
cd /Users/junhuang/coolbuy/claude-marketplace
|
||||
git push -u origin main
|
||||
git clone https://gitea.pipexerp.com/pipexerp/ai-proj-helper.git
|
||||
cd ai-proj-helper
|
||||
```
|
||||
|
||||
## 3. Test Installation
|
||||
## 2. Test Installation
|
||||
|
||||
### Add the marketplace
|
||||
```bash
|
||||
# SSH (recommended)
|
||||
/plugin marketplace add git@gitea.pipexerp.com:huangjun/claude-marketplace.git
|
||||
# SSH
|
||||
/plugin marketplace add ssh://git@gitea.pipexerp.com:10022/pipexerp/ai-proj-helper.git
|
||||
|
||||
# OR HTTPS (requires credential configuration)
|
||||
/plugin marketplace add https://gitea.pipexerp.com/huangjun/claude-marketplace.git
|
||||
/plugin marketplace add https://gitea.pipexerp.com/pipexerp/ai-proj-helper.git
|
||||
```
|
||||
|
||||
### List available plugins
|
||||
@@ -42,12 +37,12 @@ git push -u origin main
|
||||
# Check for your installed plugins
|
||||
```
|
||||
|
||||
## 4. Update Plugins Later
|
||||
## 3. Update Plugins Later
|
||||
|
||||
When you make changes and push updates:
|
||||
|
||||
```bash
|
||||
cd /Users/junhuang/coolbuy/claude-marketplace
|
||||
cd /path/to/ai-proj-helper
|
||||
|
||||
# Make changes to plugins
|
||||
# ...
|
||||
@@ -67,7 +62,7 @@ Users update with:
|
||||
/plugin update ai-proj-plugin@coolbuy-claude-plugins
|
||||
```
|
||||
|
||||
## 5. Private Repository Setup
|
||||
## 4. Repository Authentication
|
||||
|
||||
If your Gitea repo is private, users need authentication:
|
||||
|
||||
@@ -87,30 +82,28 @@ To create a Gitea token:
|
||||
3. Give it "Read repository" permissions
|
||||
4. Copy the token and add to your environment
|
||||
|
||||
## 6. Structure Overview
|
||||
## 5. Structure Overview
|
||||
|
||||
```
|
||||
claude-marketplace/
|
||||
ai-proj-helper/
|
||||
├── .claude-plugin/
|
||||
│ └── marketplace.json # Catalog of all plugins
|
||||
├── plugins/
|
||||
│ ├── ai-proj-plugin/
|
||||
│ │ ├── .claude-plugin/
|
||||
│ │ │ └── plugin.json # Plugin metadata
|
||||
│ │ └── skills/
|
||||
│ │ └── SKILL.md # Skill definition
|
||||
│ └── [33 more plugins...]
|
||||
├── skills-core/ # Core plugins
|
||||
├── skills-dev/ # Development and release plugins
|
||||
├── skills-req/ # Requirement plugins
|
||||
├── skills-integration/ # Integration plugins
|
||||
├── skills-biz/ # Business plugins
|
||||
├── README.md # User documentation
|
||||
├── SETUP.md # This file
|
||||
└── convert-skills.sh # Conversion script (reference)
|
||||
├── generate-marketplace.py # Marketplace generator
|
||||
└── install-skills.sh # Versioned local installer
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Push to Gitea: `git push -u origin main`
|
||||
2. ✅ Test locally: `/plugin marketplace add <url>`
|
||||
3. ✅ Install plugins: `/plugin install <name>@coolbuy-claude-plugins`
|
||||
4. ✅ Share with team: Send them the repository URL
|
||||
1. ✅ Test locally: `/plugin marketplace add <url>`
|
||||
2. ✅ Install plugins: `/plugin install <name>@coolbuy-claude-plugins`
|
||||
3. ✅ Share with team: Send them the repository URL
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -123,7 +116,7 @@ claude-marketplace/
|
||||
- Check plugin name is correct
|
||||
- Ensure marketplace.json is valid: `cat .claude-plugin/marketplace.json | jq`
|
||||
|
||||
**"Skills not working"**
|
||||
**"Skills not working in Claude Code"**
|
||||
- Skills are Agent Skills (auto-invoked by Claude when relevant)
|
||||
- They don't create slash commands
|
||||
- Check plugin installation: `/plugin list`
|
||||
|
||||
+71
-199
@@ -1,218 +1,90 @@
|
||||
# Skill Sync Guide
|
||||
|
||||
## Overview
|
||||
仓库中的插件是团队技能的发布源。默认安装目标是 Codex 的用户级标准目录 `~/.agents/skills/`。个人技能保留在
|
||||
`skills-personal/` 或其他本机目录,不会进入公开 marketplace。
|
||||
|
||||
This guide explains how to keep your local skills (`~/.claude/skills/`) synchronized with the marketplace plugins.
|
||||
|
||||
## Quick Sync
|
||||
## 从仓库更新本机
|
||||
|
||||
```bash
|
||||
cd /path/to/claude-marketplace
|
||||
./sync-skills.sh
|
||||
git pull
|
||||
./install-skills.sh --dry-run
|
||||
./install-skills.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
1. ✅ Compare local skills with marketplace plugins
|
||||
2. ➕ Add new skills as plugins
|
||||
3. 📝 Update changed skills
|
||||
4. ✓ Skip unchanged plugins
|
||||
|
||||
## Sync Workflow
|
||||
|
||||
### 1. Edit Skills Locally
|
||||
|
||||
Work on your skills in `~/.claude/skills/`:
|
||||
```bash
|
||||
code ~/.claude/skills/my-skill/SKILL.md
|
||||
```
|
||||
|
||||
### 2. Run Sync Script
|
||||
Claude Code 需要显式选择目标:
|
||||
|
||||
```bash
|
||||
cd ~/path/to/claude-marketplace
|
||||
./sync-skills.sh
|
||||
./install-skills.sh --agent claude
|
||||
```
|
||||
|
||||
### 3. Review Changes
|
||||
安装器会复制完整技能目录,包括 `SKILL.md`、`references/`、`scripts/` 和 `assets/`。它用内容摘要区分仓库升级和本地修改:
|
||||
|
||||
- 目标未修改时,版本升级会自动安装。
|
||||
- 旧版只安装了 `SKILL.md` 时,会安全补齐仓库中的其他同源文件。
|
||||
- 目标存在本地修改时会跳过;确认覆盖后才使用 `--force`。
|
||||
- `--cleanup` 会删除状态文件记录中已从仓库移除的技能,使用前先运行 `--dry-run --cleanup`。
|
||||
|
||||
按分类安装或查看清单:
|
||||
|
||||
```bash
|
||||
git status
|
||||
git diff
|
||||
./install-skills.sh --list
|
||||
./install-skills.sh --category dev
|
||||
./install-skills.sh --exclude ai-proj-cicd-release
|
||||
```
|
||||
|
||||
### 4. Commit & Push
|
||||
## 将本机技能发布到仓库
|
||||
|
||||
不要批量复制整个 `~/.agents/skills/` 或其他 Agent 的安装目录。系统技能、第三方托管技能、包含机器路径或凭据的技能不应发布。
|
||||
|
||||
1. 选择确实属于本仓库、可供团队复用的技能。
|
||||
2. 在对应 `skills-*/<name>-plugin/` 下放置 `.claude-plugin/plugin.json` 和完整 `skills/` 目录。
|
||||
3. 清除用户名、绝对路径、内网地址、密钥标识和历史凭据;把环境差异改为从仓库配置解析。
|
||||
4. 更新插件版本并运行:
|
||||
|
||||
```bash
|
||||
python3 generate-marketplace.py
|
||||
claude plugin validate .
|
||||
git diff --check
|
||||
```
|
||||
|
||||
5. 审核变更后通过分支和 PR 发布。
|
||||
|
||||
## 本地个人技能
|
||||
|
||||
`skills-personal/` 受 `.gitignore` 保护,仅供当前机器使用。生成器明确排除此目录,避免
|
||||
`marketplace.json` 引用公开克隆中不存在的文件。若个人技能要转为团队技能,应先按上面的发布流程完成脱敏和审核。
|
||||
|
||||
## 常见问题
|
||||
|
||||
**本地修改被跳过怎么办?**
|
||||
|
||||
先比较仓库源和 `~/.agents/skills/<name>/`。保留本地修改时将其整理成插件变更;确认丢弃时再对该次安装使用 `--force`。Claude 目标改查 `~/.claude/skills/`。
|
||||
|
||||
**marketplace 没更新?**
|
||||
|
||||
运行 `python3 generate-marketplace.py`,然后检查 `.claude-plugin/marketplace.json` 是否只包含受 Git 跟踪且真实存在的 source。
|
||||
|
||||
**如何移除技能?**
|
||||
|
||||
删除插件目录、重新生成 marketplace、提交变更。使用者随后执行 `./install-skills.sh --dry-run --cleanup`,确认后再去掉 `--dry-run`。
|
||||
|
||||
## 安装矩阵(2026-10 更新)
|
||||
|
||||
| agent | 安装目录 | 默认模式 | 状态文件 |
|
||||
| --- | --- | --- | --- |
|
||||
| `codex`(默认) | `~/.agents/skills` | copy | `~/.agents/.ai-proj-helper-installed-skills.json` |
|
||||
| `dsh` | `$DSH_HOME/skills` | copy(开发机建议 `--link`) | `$DSH_HOME/.ai-proj-helper-installed-skills.json` |
|
||||
| `claude`(兼容) | `~/.claude/skills` | copy | `~/.claude/.installed-skills.json` |
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "Update skill: description of changes"
|
||||
git push
|
||||
# 本机(DSH 开发机,改仓库即时生效)
|
||||
DSH_HOME="<your DSH_HOME>" ./install-skills.sh --agent dsh --link
|
||||
|
||||
# 远程/其他机器
|
||||
./install-skills.sh --agent codex
|
||||
|
||||
# 看谁落后了
|
||||
./install-skills.sh --agent codex --audit
|
||||
```
|
||||
|
||||
### 5. Team Updates
|
||||
|
||||
Team members update with:
|
||||
```bash
|
||||
/plugin marketplace update coolbuy-claude-plugins
|
||||
/plugin update <plugin-name>@coolbuy-claude-plugins
|
||||
```
|
||||
|
||||
## Automated Sync (Optional)
|
||||
|
||||
### Git Hook (Pre-commit)
|
||||
|
||||
Auto-sync when committing changes to skills:
|
||||
|
||||
```bash
|
||||
# In your dotfiles/skills repo
|
||||
cat > .git/hooks/pre-commit << 'EOF'
|
||||
#!/bin/bash
|
||||
# Auto-sync skills to marketplace
|
||||
~/path/to/claude-marketplace/sync-skills.sh
|
||||
EOF
|
||||
|
||||
chmod +x .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
### Cron Job (Scheduled)
|
||||
|
||||
Sync daily at 9 AM:
|
||||
|
||||
```bash
|
||||
crontab -e
|
||||
|
||||
# Add this line:
|
||||
0 9 * * * cd ~/path/to/claude-marketplace && ./sync-skills.sh && git add . && git commit -m "Daily sync" && git push
|
||||
```
|
||||
|
||||
## Skill Splitting Guidelines
|
||||
|
||||
From `~/.claude/CLAUDE.md`:
|
||||
|
||||
- **Token Limit**: Single skill ≤ 10,000 tokens
|
||||
- **Check Size**: `wc -w ~/.claude/skills/<skill>/SKILL.md`
|
||||
- **When to Split**: If > 7,500 words (≈10,000 tokens)
|
||||
|
||||
### Split Strategy
|
||||
|
||||
When a skill grows too large:
|
||||
|
||||
1. **Entry Skill** - Overview + command routing (<100 lines)
|
||||
- Example: `req/SKILL.md`
|
||||
|
||||
2. **Command Reference** - Detailed commands (<200 lines)
|
||||
- Example: `req-commands/SKILL.md`
|
||||
|
||||
3. **Workflow Guide** - Complete processes (<200 lines)
|
||||
- Example: `req-workflow/SKILL.md`
|
||||
|
||||
4. **Methodology** - Complex concepts (<150 lines)
|
||||
- Example: `req-review/SKILL.md`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Sync Script Fails
|
||||
|
||||
```bash
|
||||
# Check permissions
|
||||
ls -la sync-skills.sh
|
||||
|
||||
# Make executable
|
||||
chmod +x sync-skills.sh
|
||||
|
||||
# Check paths
|
||||
echo $HOME/.claude/skills
|
||||
```
|
||||
|
||||
### marketplace.json Not Updated
|
||||
|
||||
```bash
|
||||
# Manually regenerate
|
||||
python3 generate-marketplace.py
|
||||
|
||||
# Or edit directly
|
||||
code .claude-plugin/marketplace.json
|
||||
```
|
||||
|
||||
### Git Conflicts
|
||||
|
||||
```bash
|
||||
# Discard local changes
|
||||
git checkout .claude-plugin/marketplace.json
|
||||
|
||||
# Or merge manually
|
||||
git mergetool
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Descriptive Frontmatter
|
||||
|
||||
Always include in `SKILL.md`:
|
||||
```yaml
|
||||
---
|
||||
name: skill-name
|
||||
description: Clear, concise description of what this skill does
|
||||
---
|
||||
```
|
||||
|
||||
### 2. Version Bumping
|
||||
|
||||
When making significant changes:
|
||||
```bash
|
||||
# Update version in plugin.json
|
||||
{
|
||||
"version": "1.1.0" # was 1.0.0
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Testing Before Sync
|
||||
|
||||
```bash
|
||||
# Test skill locally first
|
||||
/skill-name
|
||||
|
||||
# Then sync to marketplace
|
||||
./sync-skills.sh
|
||||
```
|
||||
|
||||
### 4. Commit Messages
|
||||
|
||||
Use clear, descriptive messages:
|
||||
```bash
|
||||
git commit -m "Add feishu-bitable plugin for table operations"
|
||||
git commit -m "Update req-workflow with new approval process"
|
||||
git commit -m "Fix: Correct PRD template in req-prd"
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Check Sync Status
|
||||
|
||||
```bash
|
||||
# Compare local vs marketplace
|
||||
diff -qr ~/.claude/skills /tmp/claude-marketplace/plugins
|
||||
```
|
||||
|
||||
### List Differences
|
||||
|
||||
```bash
|
||||
# Find skills not in marketplace
|
||||
comm -23 <(ls ~/.claude/skills | sort) <(ls plugins | sed 's/-plugin$//' | sort)
|
||||
|
||||
# Find plugins not in local
|
||||
comm -13 <(ls ~/.claude/skills | sort) <(ls plugins | sed 's/-plugin$//' | sort)
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Can I sync in reverse (marketplace → local)?**
|
||||
A: Not recommended. Treat local skills as the source of truth.
|
||||
|
||||
**Q: What about binary files (images, scripts)?**
|
||||
A: Copy them manually to the plugin directory, then commit.
|
||||
|
||||
**Q: How do I remove a plugin?**
|
||||
A: Delete the plugin directory, regenerate marketplace.json, commit, and push.
|
||||
|
||||
**Q: Can I sync specific skills only?**
|
||||
A: Modify `sync-skills.sh` to accept a skill name parameter.
|
||||
`beta` 技能默认不装:需要时加 `--include-beta`。`promoted/beta/deprecated` 见 `SCOPE.md`。
|
||||
|
||||
+828
@@ -0,0 +1,828 @@
|
||||
{
|
||||
"repo": "ai-proj-helper",
|
||||
"coverage": {
|
||||
"required": 40,
|
||||
"with_evals": 40,
|
||||
"with_docs": 40,
|
||||
"missing_evals": [],
|
||||
"missing_docs": []
|
||||
},
|
||||
"generated_at": "2026-10-31T21:33:50Z",
|
||||
"total": 62,
|
||||
"counts": {
|
||||
"promoted/user-invoked": 18,
|
||||
"deprecated/user-invoked": 1,
|
||||
"promoted/model-invoked": 40,
|
||||
"beta/user-invoked": 1,
|
||||
"beta/model-invoked": 1,
|
||||
"deprecated/model-invoked": 1
|
||||
},
|
||||
"skills": [
|
||||
{
|
||||
"name": "biz-contract",
|
||||
"category": "biz",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。",
|
||||
"description_len": 78,
|
||||
"path": "skills-biz/biz-contract-plugin/skills/SKILL.md",
|
||||
"digest": "3c17cae1380d82a4",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "biz-ops",
|
||||
"category": "biz",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。",
|
||||
"description_len": 71,
|
||||
"path": "skills-biz/biz-ops-plugin/skills/SKILL.md",
|
||||
"digest": "aa5433074ba277d6",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "biz-plan",
|
||||
"category": "biz",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。",
|
||||
"description_len": 66,
|
||||
"path": "skills-biz/biz-plan-plugin/skills/SKILL.md",
|
||||
"digest": "b6f43a914b2f7b45",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "finance",
|
||||
"category": "biz",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。",
|
||||
"description_len": 94,
|
||||
"path": "skills-biz/finance-plugin/skills/SKILL.md",
|
||||
"digest": "0a9209bc4bf4db3a",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "ai-proj-helper-entry",
|
||||
"category": "core",
|
||||
"version": "0.1.0",
|
||||
"status": "deprecated",
|
||||
"invocation": "user-invoked",
|
||||
"description": "ai-proj-helper 技能库路由:告诉你有哪些技能、什么任务用哪个、以及「REQ released + PDV 通过才算交付」的强制口径。人在开始需求/开发/评审/发布任务前主动调用。",
|
||||
"description_len": 96,
|
||||
"path": "skills-core/ai-proj-helper-entry-plugin/skills/SKILL.md",
|
||||
"digest": "41d39b76d4c71dbf",
|
||||
"has_evals": true,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "ai-proj",
|
||||
"category": "core",
|
||||
"version": "2.0.1",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。",
|
||||
"description_len": 77,
|
||||
"path": "skills-core/ai-proj-plugin/skills/SKILL.md",
|
||||
"digest": "77ce4d2eb10e1d29",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "pm-ask",
|
||||
"category": "core",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "基于 ai-proj MCP + git log 真实数据的项目问答。禁止编造。当用户说\"/ask\"、\"问一下\"、\"统计一下\"、\"最近多少\"、\"谁做了多少\"、\"本月进度\"等项目相关问题时自动激活。",
|
||||
"description_len": 98,
|
||||
"path": "skills-core/pm-ask-plugin/skills/SKILL.md",
|
||||
"digest": "e5b69a0b56985472",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "pm-risk",
|
||||
"category": "core",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "三维度项目风险扫描(需求层/代码层/流程层)。当用户说\"/risk\"、\"风险扫描\"、\"有什么风险\"、\"哪些需求停滞了\"、\"分支健康\"时自动激活。",
|
||||
"description_len": 72,
|
||||
"path": "skills-core/pm-risk-plugin/skills/SKILL.md",
|
||||
"digest": "928cfec76018329f",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "publish",
|
||||
"category": "core",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。",
|
||||
"description_len": 48,
|
||||
"path": "skills-core/publish-plugin/skills/SKILL.md",
|
||||
"digest": "48bfbb4149baddcc",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "skill-creator",
|
||||
"category": "core",
|
||||
"version": "0.1.0",
|
||||
"status": "beta",
|
||||
"invocation": "user-invoked",
|
||||
"description": "团队技能的标准流程:收录门槛、分桶生命周期、调用模式、SKILL.md 规范、evals 全量、四段式文档、路由回改。当要新建技能、大改现有技能、或评审一个技能是否合格时使用。",
|
||||
"description_len": 88,
|
||||
"path": "skills-core/skill-creator-plugin/skills/SKILL.md",
|
||||
"digest": "18f18c43c3fa56a6",
|
||||
"has_evals": true,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "agent-browser",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "浏览器自动化技能。用于网页交互、E2E冒烟测试、需求验收验证、前端开发验证、截图对比。基于 Vercel agent-browser CLI。",
|
||||
"description_len": 71,
|
||||
"path": "skills-dev/agent-browser-plugin/skills/SKILL.md",
|
||||
"digest": "f382bffe3d1c84b2",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "agent-swarm",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
|
||||
"description_len": 153,
|
||||
"path": "skills-dev/agent-swarm-plugin/skills/SKILL.md",
|
||||
"digest": "4164f82025a421bb",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "ai-chat",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
|
||||
"description_len": 78,
|
||||
"path": "skills-dev/ai-chat-plugin/skills/SKILL.md",
|
||||
"digest": "81193de50b43d95c",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "ai-proj-cicd-release",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "Execute and audit the AI-Proj service CI/CD flow across Gitea gates, immutable image builds, staging verification, approved production release, rollback, and evidence capture. Use for AI-Proj CI status, release PRs, staging or production deployments, failed-release diagnosis, deployed commit/schema verification, and delivery-chain repair. Do not use for unrelated repositories or macOS application packaging.",
|
||||
"description_len": 410,
|
||||
"path": "skills-dev/ai-proj-cicd-release-plugin/skills/SKILL.md",
|
||||
"digest": "49ef1b6865c2f1b3",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "ai-proj-macos-release",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "Build, sign, notarize, publish, and verify the AI-Proj macOS Apple Silicon package through the repository's release chain. Use when asked to release, republish, update, or repair the downloadable macOS application, including Gatekeeper failures and download-manifest updates. Do not use for service deployments or unrelated applications.",
|
||||
"description_len": 337,
|
||||
"path": "skills-dev/ai-proj-macos-release-plugin/skills/SKILL.md",
|
||||
"digest": "6f2496b941706137",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "db-migration",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时由 req-design 推荐激活。",
|
||||
"description_len": 77,
|
||||
"path": "skills-dev/db-migration-plugin/skills/SKILL.md",
|
||||
"digest": "f8d6ebbddbbf7a71",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "defect-analysis",
|
||||
"category": "dev",
|
||||
"version": "1.1.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
||||
"description_len": 68,
|
||||
"path": "skills-dev/defect-analysis-plugin/skills/SKILL.md",
|
||||
"digest": "23dfafda92d0c622",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "deploy-rollback",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "回滚方案插件。部署后发现问题时的回滚策略和执行步骤。挂载在 deploy 阶段,部署出问题时激活。",
|
||||
"description_len": 49,
|
||||
"path": "skills-dev/deploy-rollback-plugin/skills/SKILL.md",
|
||||
"digest": "75ebf66a3f74195a",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-android",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。",
|
||||
"description_len": 72,
|
||||
"path": "skills-dev/dev-android-plugin/skills/SKILL.md",
|
||||
"digest": "fb058d1059b56cdd",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "dev-arch",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "软件架构设计技能。用于系统设计、技术选型、架构评审、设计文档编写。当用户提到架构设计、系统设计、技术方案、API 设计相关任务时自动激活。",
|
||||
"description_len": 69,
|
||||
"path": "skills-dev/dev-arch-plugin/skills/SKILL.md",
|
||||
"digest": "c9a419c83a307a11",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-cicd",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "CI/CD 流水线设计、优化与排查。适配 Gitea Actions + Go/Swift/Next.js/Docker 栈。当用户提到 CI、CD、流水线、pipeline、workflow、构建失败、runner 相关任务时自动激活。",
|
||||
"description_len": 119,
|
||||
"path": "skills-dev/dev-cicd-plugin/skills/SKILL.md",
|
||||
"digest": "a763f0f140516411",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-coding",
|
||||
"category": "dev",
|
||||
"version": "2.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "软件编码开发技能。用于代码编写、功能实现、重构优化。集成 ai-proj CLI 进行任务管理和进度跟踪。核心支持 Go 后端 + Vue/React 前端开发。",
|
||||
"description_len": 81,
|
||||
"path": "skills-dev/dev-coding-plugin/skills/SKILL.md",
|
||||
"digest": "cc43720aa12bb8f6",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-commit",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "智能 git commit — 分支保护 + 自动建功能分支 + Conventional Commits 生成。当用户说\"提交代码\"、\"commit\"、\"/commit\"、\"保存修改\"时自动激活。",
|
||||
"description_len": 99,
|
||||
"path": "skills-dev/dev-commit-plugin/skills/SKILL.md",
|
||||
"digest": "1560658c4a30df45",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-deploy",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。",
|
||||
"description_len": 90,
|
||||
"path": "skills-dev/dev-deploy-plugin/skills/SKILL.md",
|
||||
"digest": "fd15a9e9135bcc49",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-integration",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "前后端联调技能。API 契约验证、接口对接、联调报告生成。对应 req 流程 integration 阶段。纯后端需求自动跳过。",
|
||||
"description_len": 64,
|
||||
"path": "skills-dev/dev-integration-plugin/skills/SKILL.md",
|
||||
"digest": "eff6be8eb57b7fef",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-ios",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。",
|
||||
"description_len": 65,
|
||||
"path": "skills-dev/dev-ios-plugin/skills/SKILL.md",
|
||||
"digest": "44499e31f1abd5e2",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "dev-mcp",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "MCP Bridge 开发插件。TypeScript MCP 服务开发,Token 管理,HTTP 客户端模式。当涉及 mcp-task-bridge 开发时按需加载。",
|
||||
"description_len": 84,
|
||||
"path": "skills-dev/dev-mcp-plugin/skills/SKILL.md",
|
||||
"digest": "83d5912212fcfa4a",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-pda",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。",
|
||||
"description_len": 57,
|
||||
"path": "skills-dev/dev-pda-plugin/skills/SKILL.md",
|
||||
"digest": "2524f2ecd4f2a834",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "dev-review",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "代码评审技能。六视角对抗性扫描法(含 Karpathy Scope 审计),用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。",
|
||||
"description_len": 91,
|
||||
"path": "skills-dev/dev-review-plugin/skills/SKILL.md",
|
||||
"digest": "a8a1ec79b85e3227",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-scaffold",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架。挂载在 dev 阶段,新建模块时激活。",
|
||||
"description_len": 43,
|
||||
"path": "skills-dev/dev-scaffold-plugin/skills/SKILL.md",
|
||||
"digest": "4044787b1e468302",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "dev-test",
|
||||
"category": "dev",
|
||||
"version": "2.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "软件测试技能。用于单元测试、集成测试、E2E测试、测试用例设计。支持 Go、Vue、React、iOS、Android 等多平台测试。",
|
||||
"description_len": 67,
|
||||
"path": "skills-dev/dev-test-plugin/skills/dev-test/SKILL.md",
|
||||
"digest": "a03536a478eabe6c",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "diagnosing-bugs",
|
||||
"category": "dev",
|
||||
"version": "0.1.0",
|
||||
"status": "beta",
|
||||
"invocation": "model-invoked",
|
||||
"description": "难查 bug 的诊断纪律:先造出一条「现在就为这个 bug 变红」的紧反馈循环,再二分定位、最小验证、补回归测试。当 bug 难复现、间歇失败、或第一次看没头绪时使用。",
|
||||
"description_len": 84,
|
||||
"path": "skills-dev/diagnosing-bugs-plugin/skills/SKILL.md",
|
||||
"digest": "d93fb95c073858cc",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "executing-plans",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints",
|
||||
"description_len": 104,
|
||||
"path": "skills-dev/executing-plans-plugin/skills/SKILL.md",
|
||||
"digest": "8991a50091af83a4",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "finishing-a-development-branch",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "Use when implementation is complete and all tests pass - verifies and creates PR",
|
||||
"description_len": 80,
|
||||
"path": "skills-dev/finishing-branch-plugin/skills/SKILL.md",
|
||||
"digest": "1102047ccc060e60",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "frontend-design",
|
||||
"category": "dev",
|
||||
"version": "1.0.1",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.",
|
||||
"description_len": 234,
|
||||
"path": "skills-dev/frontend-design-plugin/skills/SKILL.md",
|
||||
"digest": "84e9c104be7967cc",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "karpathy-guidelines",
|
||||
"category": "dev",
|
||||
"version": "1.0.1",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "Karpathy 四原则编码行为守则。减少 LLM 常见编码错误:过度实现、静默假设、顺手重构、无验证标准。在任意编码场景激活。",
|
||||
"description_len": 64,
|
||||
"path": "skills-dev/karpathy-guidelines-plugin/skills/SKILL.md",
|
||||
"digest": "a68f347031110d6a",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "pull-request",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "Use when starting new development tasks, creating pull requests, reviewing code, or managing PR feedback cycles on Gitea. Triggers on /pr commands or when user mentions PR, pull request, code review, or branch creation.",
|
||||
"description_len": 219,
|
||||
"path": "skills-dev/pull-request-plugin/skills/SKILL.md",
|
||||
"digest": "40dac0d290782bb5",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "review-checklist",
|
||||
"category": "dev",
|
||||
"version": "1.1.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "项目级代码评审检查清单。按项目积累特定检查项,挂载在 dev-review 下自动加载。每次 CR 时触发。",
|
||||
"description_len": 54,
|
||||
"path": "skills-dev/review-checklist-plugin/skills/SKILL.md",
|
||||
"digest": "5bae13b6a8d92650",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "slark-cicd",
|
||||
"category": "dev",
|
||||
"version": "1.1.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/CD 可执行 runbook。覆盖本地预推快检、PR 与内网 ci/internal-gate 门禁、合并 origin/main、生产前强制 staging 验收、审批式生产发布,以及在 m5max 构建签名、公证并上传 macOS/Windows Desktop 包到 OSS。当用户要在 slark(qiudl/qiu-slark)里发布/上线、部署到生产或 staging、发布 Desktop 安装包、盯 CI/合并 PR、复现或验证运行时行为、或排查发布链/推送路由问题时使用。",
|
||||
"description_len": 285,
|
||||
"path": "skills-dev/slark-cicd-plugin/skills/SKILL.md",
|
||||
"digest": "1ff9b89a5fbc6e2d",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "slark-dsh-cicd",
|
||||
"category": "dev",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "DSH 插件包与 Slark 宿主补丁的发布 runbook。覆盖 npm run pack 双 tgz 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、改指向后必须重新打包、draft→publish 节奏、GitHub HTTPS/SSH 与 fetch 卡死处置、发布后 SHA 核对、app.asar 完整性约束、ai-proj 发布状态对齐。当用户要发布或升级 DSH 插件(dsh-worktable 等)、发布/更新 Slark 宿主补丁、push 或下载 GitHub 失败、插件「检查更新」看不到新版本、需要 fork 发布、或排查 asar 完整性/签名问题时使用。",
|
||||
"description_len": 306,
|
||||
"path": "skills-dev/slark-dsh-cicd-plugin/skills/SKILL.md",
|
||||
"digest": "9819e2d515ab44e0",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "data-excel",
|
||||
"category": "integration",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "Excel 数据处理与 BI 集成。通过自然语言操作 Excel 文件的读取、编辑、转换,并支持导入到 BI 系统(Metabase)进行可视化分析。当用户提到 Excel、表格处理、数据导入、BI 分析相关任务时自动激活。",
|
||||
"description_len": 112,
|
||||
"path": "skills-integration/data-excel-plugin/skills/SKILL.md",
|
||||
"digest": "8dc4be8f88cfd866",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "doubao-voice",
|
||||
"category": "integration",
|
||||
"version": "1.0.1",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "豆包语音API调用。支持语音合成(TTS)和唱歌。当用户提到语音合成、文字转语音、唱歌、豆包语音相关任务时自动激活。",
|
||||
"description_len": 58,
|
||||
"path": "skills-integration/doubao-voice-plugin/skills/SKILL.md",
|
||||
"digest": "7dc73d3b97e61fd2",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "feishu-bitable",
|
||||
"category": "integration",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "飞书多维表格操作。用于记录增删改查、批量操作、筛选排序、数据同步。当需要操作飞书多维表格时使用。",
|
||||
"description_len": 48,
|
||||
"path": "skills-integration/feishu-bitable-plugin/skills/SKILL.md",
|
||||
"digest": "3bf37533007365a3",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "feishu-docx",
|
||||
"category": "integration",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "飞书云文档操作。用于创建、编辑云文档,插入内容块,会议纪要生成。当需要操作飞书云文档时使用。",
|
||||
"description_len": 46,
|
||||
"path": "skills-integration/feishu-docx-plugin/skills/SKILL.md",
|
||||
"digest": "49d097bfa2c283ab",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "feishu",
|
||||
"category": "integration",
|
||||
"version": "1.1.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "飞书文档与多维表格操作入口。当用户提到飞书、云文档、多维表格、Bitable 相关任务时自动激活。",
|
||||
"description_len": 49,
|
||||
"path": "skills-integration/feishu-plugin/skills/SKILL.md",
|
||||
"digest": "973c6ac22104ee38",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "siyuan",
|
||||
"category": "integration",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "思源笔记 API 集成。通过自然语言创建、编辑、搜索笔记,管理笔记本和文档树。当用户提到思源笔记、SiYuan、笔记管理、知识库相关任务时自动激活。",
|
||||
"description_len": 74,
|
||||
"path": "skills-integration/siyuan-plugin/skills/SKILL.md",
|
||||
"digest": "2736c2b708791763",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "siyuan-to-feishu",
|
||||
"category": "integration",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "将思源笔记导出为 PDF 并发送到飞书群。当用户提到\"发送笔记\"、\"导出PDF发飞书\"、\"/siyuan send\"、\"分享到飞书\"相关任务时自动激活。",
|
||||
"description_len": 76,
|
||||
"path": "skills-integration/siyuan-to-feishu-plugin/skills/SKILL.md",
|
||||
"digest": "998dace910c1eb7a",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "wecom",
|
||||
"category": "integration",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "user-invoked",
|
||||
"description": "企业微信集成。通过自然语言发送消息、管理群机器人、操作审批流程、管理通讯录。当用户提到企业微信、微信工作、群机器人、企业号、wecom相关任务时自动激活。",
|
||||
"description_len": 77,
|
||||
"path": "skills-integration/wecom-plugin/skills/SKILL.md",
|
||||
"digest": "7084d88ccb87ba9b",
|
||||
"has_evals": false,
|
||||
"has_docs": false
|
||||
},
|
||||
{
|
||||
"name": "req-audit",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
|
||||
"description_len": 55,
|
||||
"path": "skills-req/req-audit-plugin/skills/SKILL.md",
|
||||
"digest": "ff2e6197bbb059c6",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-compare",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "对比式需求分析插件。系统平移、竞品借鉴、版本升级时使用参考对象对比法编写 PRD。挂载在 analysis 阶段,需要对比分析时由 req-prd 推荐激活。",
|
||||
"description_len": 79,
|
||||
"path": "skills-req/req-compare-plugin/skills/SKILL.md",
|
||||
"digest": "3428e3f37ef9e9e8",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-design",
|
||||
"category": "req",
|
||||
"version": "2.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。",
|
||||
"description_len": 96,
|
||||
"path": "skills-req/req-design-plugin/skills/SKILL.md",
|
||||
"digest": "74848800cab6e269",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-dev",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。",
|
||||
"description_len": 74,
|
||||
"path": "skills-req/req-dev-plugin/skills/SKILL.md",
|
||||
"digest": "494a5be5e7a11c0b",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-lookback",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
|
||||
"description_len": 47,
|
||||
"path": "skills-req/req-lookback-plugin/skills/SKILL.md",
|
||||
"digest": "cc6bff614a8f7bf4",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req",
|
||||
"category": "req",
|
||||
"version": "2.1.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "需求工作流管理。用于 /req 命令使用、需求全生命周期管理(创建→PRD→评审→开发→测试→部署→归档)。当用户提到需求管理、/req 命令、REQ-XXX 相关任务时自动激活。",
|
||||
"description_len": 90,
|
||||
"path": "skills-req/req-plugin/skills/SKILL.md",
|
||||
"digest": "193cf871b73faff1",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-prd",
|
||||
"category": "req",
|
||||
"version": "2.3.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document。",
|
||||
"description_len": 76,
|
||||
"path": "skills-req/req-prd-plugin/skills/SKILL.md",
|
||||
"digest": "7c4ad2baab6b2118",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-prototype",
|
||||
"category": "req",
|
||||
"version": "2.2.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "原型生成与关联。生成可交互 HTML、上传到 ai-proj OSS 并关联 Requirement/PRD iframe,也支持 Stitch AI 视觉探索。",
|
||||
"description_len": 81,
|
||||
"path": "skills-req/req-prototype-plugin/skills/SKILL.md",
|
||||
"digest": "7e362f4f895fb50a",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-research",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "需求调研插件。代码审计、数据库分析、现有功能调研、技术可行性评估。挂载在 analysis 阶段,需要深度调研时由 req-prd 推荐激活。",
|
||||
"description_len": 71,
|
||||
"path": "skills-req/req-research-plugin/skills/SKILL.md",
|
||||
"digest": "a94924f53bd4c80e",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-retro",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
|
||||
"description_len": 57,
|
||||
"path": "skills-req/req-retro-plugin/skills/SKILL.md",
|
||||
"digest": "636b7099d85e9549",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-review",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "PRD 评审方法论。用于需求评审、PRD 文档审查、评审意见编写。当执行 /req review 或需要评审 PRD 文档时使用。",
|
||||
"description_len": 65,
|
||||
"path": "skills-req/req-review-plugin/skills/SKILL.md",
|
||||
"digest": "cd1a5181d72f35f4",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-test-gate",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。",
|
||||
"description_len": 159,
|
||||
"path": "skills-req/req-test-gate-plugin/skills/SKILL.md",
|
||||
"digest": "ceebcc2516ad40d3",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "req-workflow",
|
||||
"category": "req",
|
||||
"version": "1.0.0",
|
||||
"status": "promoted",
|
||||
"invocation": "model-invoked",
|
||||
"description": "需求完整工作流。用于从创建到归档的完整流程、Hook 自动同步、测试环境流程。当需要了解需求完整生命周期或同步策略时使用。",
|
||||
"description_len": 61,
|
||||
"path": "skills-req/req-workflow-plugin/skills/SKILL.md",
|
||||
"digest": "87499da3817d162d",
|
||||
"has_evals": true,
|
||||
"has_docs": true
|
||||
},
|
||||
{
|
||||
"name": "verification-before-completion",
|
||||
"category": "req",
|
||||
"version": "0.1.0",
|
||||
"status": "deprecated",
|
||||
"invocation": "model-invoked",
|
||||
"description": "汇报「完成/修好/通过」或关闭任务、提交 PR、宣布 REQ 完成之前,必须先运行验证命令并附证据;没有证据不得声称完成。当准备说完成、关闭任务、或汇报交付前使用。",
|
||||
"description_len": 82,
|
||||
"path": "skills-req/verification-before-completion-plugin/skills/SKILL.md",
|
||||
"digest": "7c23649f4a0a8798",
|
||||
"has_evals": true,
|
||||
"has_docs": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -241,7 +241,7 @@ on:
|
||||
|
||||
- devflow-claude: `plugins/req/commands/release.md`
|
||||
- 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
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# 技能路由表(人读)
|
||||
|
||||
> 由原 `ai-proj-helper-entry` 技能降级而来:内容属于「索引」而不是「规则」,做成文档即可,不占技能目录、不用维护技能生命周期。
|
||||
|
||||
## 交付终点(强制)
|
||||
|
||||
开发任务交付终点是:需求按 REQ 流程推进到具备发布条件并完成上线(`delivery_stage=released` + PDV 验收通过)。
|
||||
写代码、过单测、提 PR、合并都只是过程节点;任务 `completed` ≠ 需求完成。
|
||||
|
||||
## 主流程
|
||||
|
||||
讨论(`req`) → PRD(`req-prd`) → 评审(`req-review`) → 设计(`req-design`) → 开发(`dev-coding`/`karpathy-guidelines`) → 测试(`dev-test`+`req-test-gate`) → 代码评审(`dev-review`) → 发布(`slark-cicd`/`slark-dsh-cicd`/`publish`) → PDV 验收 → released
|
||||
|
||||
## 按任务选技能
|
||||
|
||||
| 任务 | 技能 |
|
||||
| --- | --- |
|
||||
| 讨论 / 调研 / 对比 | `req`、`req-research`、`req-compare` |
|
||||
| PRD / 原型 | `req-prd`、`req-prototype` |
|
||||
| PRD 评审 | `req-review` |
|
||||
| 设计 / 拆任务 | `req-design`、`dev-arch`、`db-migration` |
|
||||
| 编码 | `dev-coding`、`karpathy-guidelines` |
|
||||
| 测试 | `dev-test`、`req-test-gate` |
|
||||
| 难查的 bug / 间歇失败 | `diagnosing-bugs`(先建红循环再推理) |
|
||||
| 代码评审 | `dev-review`、`review-checklist` |
|
||||
| 发布(Slark 仓库/Desktop) | `slark-cicd` |
|
||||
| 发布(DSH 插件/宿主补丁) | `slark-dsh-cicd` |
|
||||
| 发布(ai-proj 服务 / macOS 包) | `ai-proj-cicd-release`、`ai-proj-macos-release` |
|
||||
| 回滚 | `deploy-rollback` |
|
||||
| 部署后审计 / 回归 | `req-audit`、`req-lookback` |
|
||||
| 复盘 | `req-retro` |
|
||||
| 项目管理 | `project-management`、`pm-risk`、`pm-ask` |
|
||||
| 建/改技能 | `skill-creator` |
|
||||
|
||||
## 红线
|
||||
|
||||
- 不得自审批;不得绕过门禁(测试 Gate / 发布 Gate / 归档 Gate)
|
||||
- 不得把「某步骤完成」当结束;不得无证据声称完成
|
||||
- 不得把凭据/内网地址/绝对路径写进技能或文档
|
||||
@@ -0,0 +1,21 @@
|
||||
# agent-browser
|
||||
|
||||
## What it does
|
||||
|
||||
浏览器自动化(snapshot-ref)。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# agent-swarm
|
||||
|
||||
## What it does
|
||||
|
||||
多 agent 编排(Swarm 模式)。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# ai-chat
|
||||
|
||||
## What it does
|
||||
|
||||
AI Chat 工具调用测试与配置。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,24 @@
|
||||
# ai-proj — 任务与需求管理
|
||||
|
||||
## What it does
|
||||
|
||||
通过 ai-proj(`https://ai.pipexerp.com`)的 MCP / CLI 管理需求、任务、文档与进度:查询、创建、更新状态、关联需求与任务、挂证据文档。是所有项目状态数据的唯一权威来源。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
- 要查「某个 REQ / 任务的真实状态、优先级、截止、关联任务」
|
||||
- 要更新任务状态、挂文档、改优先级、改派
|
||||
- 汇报进度或做对齐检查前,需要拉真实数据
|
||||
|
||||
## Common questions
|
||||
|
||||
- **数据以哪为准?** ai-proj,不靠聊天记录或记忆。主机必须是 `https://ai.pipexerp.com`(写成 `api.pipexerp.com` 会失败)。
|
||||
- **可以自己改需求优先级吗?** 不行,需求优先级是业务决策;任务优先级可以对齐需求。
|
||||
- **任务 completed 就是完成了吗?** 不是,REQ 未 `released` 只能算步骤完成。
|
||||
- **接口报错怎么排查?** 先 GET 查证实体是否存在、凭证主机是否正确,不要猜 ID。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 问「REQ-XXXX 现在什么状态」时,它给出带字段名与时间戳的真实数据,而不是复述对话
|
||||
- 更新任务状态前先查一次实体,写入后回读确认
|
||||
- 遇到 404/401 时给的是排查步骤与真实查询结果,不是编造的 ID
|
||||
@@ -0,0 +1,21 @@
|
||||
# db-migration
|
||||
|
||||
## What it does
|
||||
|
||||
数据库变更与回滚。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# defect-analysis
|
||||
|
||||
## What it does
|
||||
|
||||
设计缺陷多维度分析。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# deploy-rollback
|
||||
|
||||
## What it does
|
||||
|
||||
部署回滚决策与步骤。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-arch
|
||||
|
||||
## What it does
|
||||
|
||||
架构设计与技术选型。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-cicd
|
||||
|
||||
## What it does
|
||||
|
||||
CI/CD 流水线设计、优化与排障。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-coding
|
||||
|
||||
## What it does
|
||||
|
||||
编码实现与任务跟踪。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-commit
|
||||
|
||||
## What it does
|
||||
|
||||
智能提交(分支保护 + Conventional)。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-deploy
|
||||
|
||||
## What it does
|
||||
|
||||
应用部署(TestFlight / Docker)。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-integration
|
||||
|
||||
## What it does
|
||||
|
||||
前后端联调。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-mcp
|
||||
|
||||
## What it does
|
||||
|
||||
MCP Bridge 开发。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-review
|
||||
|
||||
## What it does
|
||||
|
||||
六视角对抗性代码评审。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-scaffold
|
||||
|
||||
## What it does
|
||||
|
||||
模块脚手架。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# dev-test
|
||||
|
||||
## What it does
|
||||
|
||||
测试设计与执行。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,26 @@
|
||||
# diagnosing-bugs
|
||||
|
||||
## What it does
|
||||
|
||||
给难查的 bug 一套诊断纪律:先造出「现在就为这个 bug 变红」的紧反馈循环,再二分定位、一次验一个假设、最小改动修复,最后补一条曾经红过的回归测试。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
- 间歇失败、偶发问题,第一次看没头绪
|
||||
- 已知两个好版本之间引入的回归(适合 git bisect)
|
||||
- 修过几次没修好的 bug
|
||||
|
||||
一眼可见的拼写/配置错误不需要它,直接修。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **为什么不能先猜着改?** 没有红循环就没有验证手段,改了也不知道是不是这次改动生效。
|
||||
- **复现不出来怎么办?** 提高复现率(缩小输入、调整时序、加观测),或用 git bisect 从版本维度定位,而不是盲上。
|
||||
- **修完就完了吗?** 不,必须补一条回归测试,并证明修复前它是红的。
|
||||
- **和 dev-test 的区别?** dev-test 管测试怎么写;本技能管「怎么找到 bug」。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 动手改代码前,先给出了一条现在为红的命令
|
||||
- 每次改动只变一个变量,并能说出上次结果的差异
|
||||
- 修复结论包含根因 + 回归测试 + 「曾经红过」的证据
|
||||
@@ -0,0 +1,21 @@
|
||||
# executing-plans
|
||||
|
||||
## What it does
|
||||
|
||||
执行书面计划并设检查点。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# finishing-a-development-branch
|
||||
|
||||
## What it does
|
||||
|
||||
开发分支收尾与建 PR。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# frontend-design
|
||||
|
||||
## What it does
|
||||
|
||||
高质量前端界面设计。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# karpathy-guidelines
|
||||
|
||||
## What it does
|
||||
|
||||
Karpathy 四原则编码守则。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,22 @@
|
||||
# pm-ask
|
||||
|
||||
## What it does
|
||||
|
||||
基于 ai-proj MCP 与 git log 真实数据的项目问答:统计任务/需求数量、进度、谁做了什么,回答前必须查真实数据。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户说 /ask、问一下、统计一下、最近多少、谁做了多少、本月进度时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **数据从哪来?** ai-proj MCP + git log,不靠记忆和估算。
|
||||
- **查不到数据怎么办?** 如实报告缺失与失败原因,给出查询口径,不编造数字。
|
||||
- **能做预测吗?** 只能基于真实阶段数据做推导,并标注假设。
|
||||
- **回答格式?** 先给结论数字,再给来源与口径。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 问团队本月产出时,它先拉数据再回答,并附时间范围与来源
|
||||
- 接口异常时明确说“数据未取到”,不出现任何编造数字
|
||||
- 对“估一下”的请求,给口径而不是给假数字
|
||||
@@ -0,0 +1,22 @@
|
||||
# pm-risk
|
||||
|
||||
## What it does
|
||||
|
||||
三维度项目风险扫描:需求层(停滞/逾期/优先级)、代码层(分支健康/评审积压)、流程层(门禁/交付态),按严重/警告/提示分级输出。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户说 /risk、风险扫描、有什么风险、哪些需求停滞了、分支健康时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **找不到数据算无风险吗?** 不算,缺失要标注未覆盖。
|
||||
- **分级标准?** 严重=已阻塞交付;警告=会拖延;提示=观察项。
|
||||
- **会不会误报?** 每条风险附证据与来源,便于人工复核。
|
||||
- **输出多长?** 先概览再分项,严重项必须置顶。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 扫描报告每条风险都有数据来源,不是笼统描述
|
||||
- 数据源不可用时明确写“该维度未覆盖”
|
||||
- 不会因为查不到就输出“无风险”
|
||||
@@ -0,0 +1,21 @@
|
||||
# pull-request
|
||||
|
||||
## What it does
|
||||
|
||||
PR 创建与评审反馈循环(Gitea)。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-audit
|
||||
|
||||
## What it does
|
||||
|
||||
部署后审计:运行时日志检查、静态缺陷分析、设计偏移检测,产出审计报告并关联任务。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
需求部署后,或用户说“审计一下”“部署后检查”时;也可由 /req done 触发。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **审计和测试的区别?** 测试验证功能对错,审计查运行时异常、代码缺陷与实现是否偏离设计。
|
||||
- **没日志能审吗?** 能审静态部分,但运行时项必须标未覆盖。
|
||||
- **发现偏移怎么办?** 列偏移项+影响面+建议,交给负责人决策。
|
||||
- **报告存哪?** 关联到需求/任务文档,便于回溯。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 审计报告包含三类检查结果与证据
|
||||
- 缺运行时证据时明确标未覆盖
|
||||
- 发现偏移会列出具体条目而不是笼统说“有偏差”
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-compare
|
||||
|
||||
## What it does
|
||||
|
||||
对比式需求分析:以参考系统为基准做功能对比,再推导目标系统设计,产出对比式 PRD。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
系统平移、竞品借鉴、版本升级需要对比论证时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **没有参考资料能写吗?** 可以写,但参考系统细节必须标注为待验证假设。
|
||||
- **对比维度怎么定?** 按功能、交互、数据、权限、成本五类,按需求裁剪。
|
||||
- **结论要什么形式?** 对比表 + 差异结论 + 目标系统取舍。
|
||||
- **会不会照搬竞品?** 只借鉴结论,落地按自身约束重新设计。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 对比表每行能追溯到资料或标注为假设
|
||||
- 结论明确给出取舍而不是罗列
|
||||
- 没有把“凭印象”写成事实
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-design
|
||||
|
||||
## What it does
|
||||
|
||||
PRD 到开发设计的转换:API 契约、数据模型变更、变更文件清单、任务拆分与依赖、技术风险评估。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
执行 /req doc、或在评审后进入设计阶段时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **设计前需要什么?** PRD 与验收标准;缺验收标准要先补。
|
||||
- **任务拆到什么粒度?** 单任务 2–4 小时可完成,带依赖边与验收标准。
|
||||
- **找不到模块怎么办?** 报告缺失并给待确认项,不猜路径。
|
||||
- **要写测试策略吗?** 要,随设计一起给。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 输出含契约表、变更文件、子任务三件套
|
||||
- 缺验收标准时会阻断而不是硬写
|
||||
- 任务清单里有 blockers,不只有层级
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-dev
|
||||
|
||||
## What it does
|
||||
|
||||
把 PRD 转成可执行开发计划:代码库分析、现有实现盘点、任务拆分、技术方案与风险。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
执行 /req doc、需要从需求走到可执行计划时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **和 req-design 区别?** req-dev 重“怎么落地”:代码库分析与任务拆分;req-design 重设计文档与契约。
|
||||
- **不看代码能拆吗?** 不能,先分析再拆。
|
||||
- **计划里要有什么?** 任务、依赖、验收、风险、需要确认项。
|
||||
- **代码库与 PRD 冲突?** 以真实代码为准,报告差异后调整。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 计划里的任务能直接执行,附依赖与验收
|
||||
- 不读代码就要求拆任务会被拒绝
|
||||
- 发现代码与 PRD 不符会报告差异而不是硬拆
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-lookback
|
||||
|
||||
## What it does
|
||||
|
||||
部署后回归测试:按变更影响面验证既有功能是否被破坏,产出回归报告。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
需求部署后;或 /req done 前需要确认无回归时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **回归范围怎么定?** 按变更影响的模块与调用方扩散确定。
|
||||
- **只测新功能行吗?** 不行,必须覆盖受影响面。
|
||||
- **发现问题怎么办?** 报告复现与影响,给回滚/修复建议。
|
||||
- **要留证据吗?** 要,命令与结果。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 回归报告含影响范围、用例、结果
|
||||
- 要求只测新功能会被拒绝
|
||||
- 发现回归会给出回滚或修复建议
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-prd
|
||||
|
||||
## What it does
|
||||
|
||||
产品需求文档(PRD)编写与双写:按模板产出需求/预期/验收标准、界面原型要求,并同时写入本地仓库与 ai-proj Task Document。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
需求讨论确认后要写 PRD、或用 /req prd 时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **PRD 最少要有什么?** 需求描述、预期结果、≥2 条可检查验收标准。
|
||||
- **为什么要双写?** 本地可版本化,ai-proj 供评审与追溯。
|
||||
- **验收标准怎么写?** 可检查、可对应测试,不写“体验良好”这类。
|
||||
- **写入失败怎么办?** 保留本地并报告失败,不假装成功。
|
||||
|
||||
## It's working if
|
||||
|
||||
- PRD 都含可检查验收标准
|
||||
- 写入后回读确认,不只打印成功
|
||||
- 失败时明确报失败并保留本地稿
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-prototype
|
||||
|
||||
## What it does
|
||||
|
||||
原型生成与关联:把 PRD 转成可交互 HTML 原型,上传并关联到需求的 prototype_urls,支持 Stitch 视觉探索。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
有 UI 的需求在 PRD/缺陷收敛后需要原型时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **原型要确认吗?** 要,确认后才能作为开发依据。
|
||||
- **上传失败会丢吗?** 本地保留,报告失败。
|
||||
- **能直接改线上页面吗?** 不能,原型是独立 HTML。
|
||||
- **和 PRD 什么关系?** 原型回填到 PRD 的界面章节。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 原型生成后有可访问链接并回读关联成功
|
||||
- 要求跳过确认直接开发会被劝回
|
||||
- 上传失败时本地文件仍在
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-research
|
||||
|
||||
## What it does
|
||||
|
||||
需求调研:代码审计、数据库分析、现有功能盘点、技术可行性评估,产出带证据的调研报告。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
analysis 阶段需要深度调研,或 /req research 时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **结论要有证据吗?** 要有,file:line 或查询结果。
|
||||
- **找不到怎么办?** 报告缺口与假设,不编造。
|
||||
- **和 req-dev 区别?** research 判断“能不能、怎么做”,dev 出“具体计划”。
|
||||
- **报告给谁?** PRD/设计阶段的输入。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 调研报告每条结论有证据出处
|
||||
- 不看代码就下结论会被拒
|
||||
- 找不到模块会列待确认项
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-retro
|
||||
|
||||
## What it does
|
||||
|
||||
复盘总结:采集数据计算质量评分、还原时间线、识别跨需求模式,沉淀经验教训。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
/req done 后、或用户说“复盘一下”时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **评分怎么来?** 按时间线、缺陷、返工、门禁通过率等指标计算。
|
||||
- **数据不全怎么办?** 标注缺失,给部分结论。
|
||||
- **经验存哪?** 写入 memory / 技能改进项。
|
||||
- **会不会只夸不批?** 要列问题与改进项。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 评分附计算依据
|
||||
- 数据缺失时明确标注而不是编造
|
||||
- 复盘产出可执行的改进项
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-review
|
||||
|
||||
## What it does
|
||||
|
||||
PRD 评审方法论:结构完整性、需求清晰度(SMART)、技术可行性、数据模型、API、原型检查,输出通过/打回结论与理由。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
/req review、或需要评审 PRD 时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **评审看什么?** 结构、清晰度、可行性、数据/API/原型。
|
||||
- **能免检通过吗?** 不能,必须有依据。
|
||||
- **打回要写什么?** 缺失项+修改要求。
|
||||
- **提交人能自审吗?** 不能。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 评审结论都有逐项检查记录
|
||||
- 无依据的“通过”会被拒绝
|
||||
- 打回时列出具体缺失项
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-test-gate
|
||||
|
||||
## What it does
|
||||
|
||||
需求级质量门禁(Gate 0–5)与部署级验证制度:逐门禁检查、留证据、不通过不放行。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
/req test、准备部署前、或用户问门禁是否通过时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **门禁能跳吗?** 不能,除非记录阻塞并获豁免。
|
||||
- **失败能忽略吗?** 不能,必须记录并阻断。
|
||||
- **证据留哪?** 测试报告与任务文档。
|
||||
- **部署前还要什么?** 部署级验证与回滚预案。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 门禁结论逐条有证据
|
||||
- 跳门禁/忽略失败会被拒绝
|
||||
- 测试报告能直接作为发布依据
|
||||
@@ -0,0 +1,22 @@
|
||||
# req-workflow
|
||||
|
||||
## What it does
|
||||
|
||||
需求完整工作流参考:从创建到归档的阶段划分、每阶段门禁、交付物、Hook 同步与测试环境流程。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
需要理解完整生命周期、或判断某需求下一步该做什么时。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **阶段顺序?** 创建→讨论→PRD→评审→设计→开发→测试→staging→released→归档。
|
||||
- **能跳 staging 吗?** 不能,强制验收。
|
||||
- **状态不一致听谁的?** 以真实数据为准并核对。
|
||||
- **交付终点?** released + PDV 验收通过。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 能按阶段说清下一步动作与门禁
|
||||
- 跳过 staging 会被拒绝
|
||||
- 发现状态矛盾会先核对再结论
|
||||
@@ -0,0 +1,23 @@
|
||||
# req — 需求全生命周期
|
||||
|
||||
## What it does
|
||||
|
||||
把一条需求从创建推进到归档:创建 → 讨论确认 → PRD → 评审 → 开发 → 测试 → 部署 staging/prod → PDV 验收 → released → 归档。同时约束评审、门禁与交付口径,防止跳步和自审批。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
- 用户说「/req」「新建需求」「需求走到哪了」「提交评审」「部署上线」时
|
||||
- 任何需求类工作开始时的第一步(先确定当前阶段,再决定下一个命令)
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能不能跳过评审直接开发?** 不能。PRD 是评审前置,评审通过才能进 dev。
|
||||
- **提交人能不能自己批?** 不能;`allow_self_approve` 只作临时手段且要收回。
|
||||
- **任务都 completed 了算交付吗?** 不算,需求要走到 `released` + PDV 验收通过。
|
||||
- **部署是每个需求单独做吗?** 各需求独立到 testing 后进待部署池,由 `/req deploy` 批量推进。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 问「这个需求现在到哪一步、下一步做什么」时,它先给出 `delivery_stage` 与下一个命令,而不是直接开始写代码
|
||||
- 出现「直接改 approved」「跳过 PRD」这类请求时,它会阻断并给出正确路径
|
||||
- 汇报完成时用的是「REQ 阶段 + 下一节点」,不是「某个开发步骤完成」
|
||||
@@ -0,0 +1,21 @@
|
||||
# review-checklist
|
||||
|
||||
## What it does
|
||||
|
||||
项目级评审检查清单。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# slark-cicd
|
||||
|
||||
## What it does
|
||||
|
||||
Slark 仓库发布 runbook。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
@@ -0,0 +1,21 @@
|
||||
# slark-dsh-cicd
|
||||
|
||||
## What it does
|
||||
|
||||
DSH 插件与宿主补丁发布 runbook。
|
||||
|
||||
## When to reach for it
|
||||
|
||||
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
|
||||
|
||||
## Common questions
|
||||
|
||||
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
|
||||
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
|
||||
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
|
||||
|
||||
## It's working if
|
||||
|
||||
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
|
||||
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
|
||||
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
|
||||
+15
-14
@@ -14,28 +14,27 @@ script_dir = Path(__file__).parent.resolve()
|
||||
config_file = script_dir / "claude-config.yaml"
|
||||
marketplace_file = script_dir / ".claude-plugin" / "marketplace.json"
|
||||
|
||||
# Skill directories (label, directory name)
|
||||
# Public marketplace skill directories. skills-personal is deliberately
|
||||
# excluded: it is gitignored and must never produce sources that disappear from
|
||||
# a public clone of this repository.
|
||||
SKILL_DIRS = [
|
||||
("core", "skills-core"),
|
||||
("dev", "skills-dev"),
|
||||
("req", "skills-req"),
|
||||
("integration", "skills-integration"),
|
||||
("biz", "skills-biz"),
|
||||
("personal", "skills-personal"),
|
||||
]
|
||||
|
||||
|
||||
def load_config():
|
||||
"""Load claude-config.yaml and return disabled list + personal_dir."""
|
||||
"""Load claude-config.yaml and return the disabled plugin list."""
|
||||
disabled = []
|
||||
personal = "skills-personal"
|
||||
|
||||
if config_file.exists() and HAS_YAML:
|
||||
with open(config_file) as f:
|
||||
cfg = yaml.safe_load(f) or {}
|
||||
skills_cfg = cfg.get("skills", {})
|
||||
disabled = skills_cfg.get("disabled", []) or []
|
||||
personal = skills_cfg.get("personal_dir", personal)
|
||||
elif config_file.exists():
|
||||
# Fallback: parse disabled list without PyYAML
|
||||
in_disabled = False
|
||||
@@ -54,17 +53,14 @@ def load_config():
|
||||
disabled.append(val)
|
||||
elif stripped and not stripped.startswith("#"):
|
||||
break
|
||||
if stripped.startswith("personal_dir:"):
|
||||
personal = stripped.split(":", 1)[1].strip().strip('"').strip("'")
|
||||
|
||||
return disabled, personal
|
||||
return disabled
|
||||
|
||||
|
||||
# Category mapping
|
||||
def get_category_and_keywords(plugin_name):
|
||||
if any(x in plugin_name for x in ['dev-', 'coding', 'frontend']):
|
||||
return "development", ["development", "coding", "workflow"]
|
||||
elif any(x in plugin_name for x in ['ops-', 'deploy', 'server']):
|
||||
elif any(x in plugin_name for x in ['ops-', 'deploy', 'server', 'slark-cicd', 'slark-dsh-cicd']):
|
||||
return "devops", ["devops", "deployment", "operations"]
|
||||
elif any(x in plugin_name for x in ['ai-proj', 'req']):
|
||||
return "productivity", ["project-management", "tasks", "requirements"]
|
||||
@@ -100,6 +96,14 @@ def scan_plugins(directory, source_prefix, disabled):
|
||||
with open(manifest_path) as f:
|
||||
manifest = json.load(f)
|
||||
|
||||
# Lifecycle buckets: only promoted plugins ship in the public
|
||||
# marketplace. beta/deprecated must be installed with --include-beta.
|
||||
import os
|
||||
status = manifest.get("status", "promoted")
|
||||
if status in ("beta", "deprecated") and os.environ.get("ALLOW_BETA") != "1":
|
||||
print(f" ⊘ {plugin_name} (status={status}, not shipped)")
|
||||
continue
|
||||
|
||||
category, keywords = get_category_and_keywords(plugin_name)
|
||||
|
||||
plugins.append({
|
||||
@@ -116,15 +120,12 @@ def scan_plugins(directory, source_prefix, disabled):
|
||||
|
||||
|
||||
# Load config
|
||||
disabled_skills, personal_dir_name = load_config()
|
||||
disabled_skills = load_config()
|
||||
|
||||
# Collect plugins from all skill directories
|
||||
plugins = []
|
||||
counts = {}
|
||||
for label, dir_name in SKILL_DIRS:
|
||||
# personal_dir may be overridden by config
|
||||
if label == "personal":
|
||||
dir_name = personal_dir_name
|
||||
skill_path = script_dir / dir_name
|
||||
if not skill_path.is_dir():
|
||||
continue
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@
|
||||
"hooks": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
@@ -35,7 +35,7 @@
|
||||
"hooks": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
@@ -48,7 +48,7 @@
|
||||
### 方式 2:一键安装脚本
|
||||
|
||||
```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 配置:
|
||||
# {
|
||||
# "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
|
||||
# }
|
||||
#
|
||||
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
#!/bin/bash
|
||||
# validate-prd.sh
|
||||
# PostToolUse Hook: PRD 文档写入/编辑后自动校验章节完整性
|
||||
#
|
||||
# 检查标准 PRD 模板的必需章节是否存在。
|
||||
# 借鉴 devflow-claude validate-requirement.sh。
|
||||
# REQ-20260416-0017 P1-14
|
||||
#
|
||||
# 安装方式(可选,加到 ~/.claude/settings.json):
|
||||
# hooks.PostToolUse:
|
||||
# - matcher: "Write|Edit"
|
||||
# hooks:
|
||||
# - type: command
|
||||
# command: "<path>/hooks/validate-prd.sh"
|
||||
# timeout: 5
|
||||
#
|
||||
# 也可由 skill 脚本在 PRD 写完后手动调用:
|
||||
# bash validate-prd.sh /path/to/PRD.md
|
||||
|
||||
set -e
|
||||
|
||||
# ============ 1. 获取文件路径 ============
|
||||
FILE_PATH="$1"
|
||||
|
||||
# 如果没有参数,从 stdin JSON 提取(PostToolUse hook 模式)
|
||||
if [ -z "$FILE_PATH" ] && command -v jq >/dev/null 2>&1; then
|
||||
INPUT=$(cat 2>/dev/null || echo "")
|
||||
if [ -n "$INPUT" ]; then
|
||||
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.content // empty' 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
# 没有文件路径,静默退出
|
||||
if [ -z "$FILE_PATH" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 不是 .md 文件,跳过
|
||||
case "$FILE_PATH" in
|
||||
*.md) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
# 文件不存在,跳过
|
||||
if [ ! -f "$FILE_PATH" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 不含 PRD / 需求 关键词的文件名,跳过
|
||||
BASENAME=$(basename "$FILE_PATH")
|
||||
case "$BASENAME" in
|
||||
*PRD*|*prd*|*需求*|*requirement*|*REQ*) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
# ============ 2. 检查章节 ============
|
||||
|
||||
# 标准 PRD 必需章节(来自 req-prd SKILL.md 的模板)
|
||||
REQUIRED_SECTIONS=(
|
||||
"## 1. 概述"
|
||||
"### 1.1 背景"
|
||||
"### 1.2 目标"
|
||||
"### 1.4 客户原始诉求"
|
||||
"## 2. 用户分析"
|
||||
"## 3. 功能需求"
|
||||
"## 4. 交互设计"
|
||||
"## 5. 技术要求"
|
||||
"## 6. 上线计划"
|
||||
"## 7. 风险评估"
|
||||
)
|
||||
|
||||
MISSING=()
|
||||
CONTENT=$(cat "$FILE_PATH")
|
||||
|
||||
for section in "${REQUIRED_SECTIONS[@]}"; do
|
||||
# 模糊匹配:忽略空格差异和标点
|
||||
PATTERN=$(echo "$section" | sed 's/[[:space:]]*//g')
|
||||
CONTENT_CLEAN=$(echo "$CONTENT" | sed 's/[[:space:]]*//g')
|
||||
if ! echo "$CONTENT_CLEAN" | grep -qi "$(echo "$PATTERN" | sed 's/#//g')"; then
|
||||
MISSING+=("$section")
|
||||
fi
|
||||
done
|
||||
|
||||
# ============ 3. 输出 ============
|
||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
echo "⚠️ PRD 章节检查:${BASENAME}"
|
||||
echo ""
|
||||
echo "缺少 ${#MISSING[@]} 个必需章节:"
|
||||
for m in "${MISSING[@]}"; do
|
||||
echo " ❌ $m"
|
||||
done
|
||||
echo ""
|
||||
echo "💡 请参考 req-prd skill 的 PRD 模板补充缺失章节。"
|
||||
echo " 章节结构不可变:不得新增、删除、合并或重命名模板中的章节。"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# ai-proj-helper 初始化脚本
|
||||
# 配置 MCP 连接 + 安装技能到 ~/.claude/skills/
|
||||
# 配置 Claude MCP 连接 + 安装 Claude 技能
|
||||
|
||||
set -e
|
||||
|
||||
@@ -183,32 +183,13 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Install skills to ~/.claude/skills/ ──────────────────────────────
|
||||
# ── Install complete skill packages ──────────────────────────────────
|
||||
# Use the versioned installer as the single installation path so references,
|
||||
# scripts and assets stay beside SKILL.md and local edits are not overwritten.
|
||||
echo "📦 安装技能到 ~/.claude/skills/ ..."
|
||||
SKILLS_DIR="$HOME/.claude/skills"
|
||||
mkdir -p "$SKILLS_DIR"
|
||||
|
||||
SKILL_COUNT=0
|
||||
for plugin_dir in "$SCRIPT_DIR"/skills-*/; do
|
||||
for skill_path in "$plugin_dir"*-plugin/; do
|
||||
[ -d "$skill_path" ] || continue
|
||||
skill_md="$skill_path/skills/SKILL.md"
|
||||
[ -f "$skill_md" ] || continue
|
||||
|
||||
# Extract skill name: ai-proj-plugin -> ai-proj
|
||||
dir_name=$(basename "$skill_path")
|
||||
skill_name="${dir_name%-plugin}"
|
||||
|
||||
target_dir="$SKILLS_DIR/$skill_name"
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
# Copy SKILL.md (overwrite if exists)
|
||||
cp "$skill_md" "$target_dir/SKILL.md"
|
||||
SKILL_COUNT=$((SKILL_COUNT + 1))
|
||||
done
|
||||
done
|
||||
echo " 已安装 $SKILL_COUNT 个技能"
|
||||
echo "✅ 技能安装完成 → $SKILLS_DIR"
|
||||
"$SCRIPT_DIR/install-skills.sh" --agent claude
|
||||
SKILL_COUNT=$(python3 -c 'import json, os; p=os.path.expanduser("~/.claude/.installed-skills.json"); print(len(json.load(open(p))) if os.path.exists(p) else 0)' 2>/dev/null || echo 0)
|
||||
echo "✅ 技能安装完成"
|
||||
|
||||
# ── Verify MCP connection ────────────────────────────────────────────
|
||||
echo ""
|
||||
@@ -270,7 +251,7 @@ if $HAS_CLAUDE; then
|
||||
else
|
||||
echo " ✅ MCP 服务器 → $MCP_CONFIG"
|
||||
fi
|
||||
echo " ✅ 技能 ($SKILL_COUNT 个) → $SKILLS_DIR"
|
||||
echo " ✅ 技能 ($SKILL_COUNT 个) → ~/.claude/skills"
|
||||
echo ""
|
||||
echo "重启 Claude Code 即可使用。"
|
||||
echo "如需更改配置,编辑 claude-config.yaml 后重新运行 ./init.sh"
|
||||
|
||||
Executable
+572
@@ -0,0 +1,572 @@
|
||||
#!/usr/bin/env bash
|
||||
# install-skills.sh — Cross-agent skill sync from ai-proj-helper
|
||||
#
|
||||
# Usage:
|
||||
# ./install-skills.sh [options]
|
||||
#
|
||||
# Options:
|
||||
# --agent <agent> Install target: codex (default), dsh, or claude
|
||||
# --link Symlink skills from the repo instead of copying (dev machines)
|
||||
# --audit Show installed vs repo versions for the selected target
|
||||
# --include-beta Also install skills whose plugin.json status=beta
|
||||
# --no-validate Skip scripts/validate-skills.py before installing
|
||||
# --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
|
||||
# --category <cat> Only install plugins in dir_category=<cat>
|
||||
# Valid values: biz, core, dev, integration, personal, req
|
||||
# --exclude <name> Skip one install_name (repeatable)
|
||||
# --force Overwrite even if local files were modified
|
||||
# --cleanup Remove locally installed skills that are no longer in repo
|
||||
# --list List all available plugins without installing
|
||||
# --help Show this help
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SKILLS_DIR=""
|
||||
COMMANDS_DIR=""
|
||||
STATE_FILE=""
|
||||
|
||||
AGENT_TARGET="codex"
|
||||
DRY_RUN=false
|
||||
CATEGORY_FILTER=""
|
||||
EXCLUDED_NAMES=()
|
||||
FORCE=false
|
||||
CLEANUP=false
|
||||
LIST_ONLY=false
|
||||
INSTALL_ACTION=false
|
||||
LINK_MODE=false
|
||||
AUDIT_ONLY=false
|
||||
INCLUDE_BETA=false
|
||||
NO_VALIDATE=false
|
||||
SCOPE="user"
|
||||
PROJECT_DIR=""
|
||||
|
||||
# ── Colour helpers ─────────────────────────────────────────────────────────────
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
RESET='\033[0m'
|
||||
|
||||
info() { echo -e "${BLUE}[info]${RESET} $*"; }
|
||||
ok() { echo -e "${GREEN}[ok]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[warn]${RESET} $*"; }
|
||||
error() { echo -e "${RED}[error]${RESET} $*" >&2; }
|
||||
dry() { echo -e "${YELLOW}[dry]${RESET} $*"; }
|
||||
|
||||
# ── Argument parsing ───────────────────────────────────────────────────────────
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--agent)
|
||||
[[ $# -ge 2 ]] || { error "--agent requires codex, dsh or claude"; exit 1; }
|
||||
AGENT_TARGET="$2"; shift ;;
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--link) LINK_MODE=true ;;
|
||||
--audit) AUDIT_ONLY=true ;;
|
||||
--include-beta) INCLUDE_BETA=true ;;
|
||||
--no-validate) NO_VALIDATE=true ;;
|
||||
--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 ;;
|
||||
--cleanup) CLEANUP=true ;;
|
||||
--list) LIST_ONLY=true ;;
|
||||
--category)
|
||||
[[ $# -ge 2 ]] || { error "--category requires a value"; exit 1; }
|
||||
CATEGORY_FILTER="$2"; shift ;;
|
||||
--exclude)
|
||||
[[ $# -ge 2 ]] || { error "--exclude requires an install_name"; exit 1; }
|
||||
EXCLUDED_NAMES+=("$2"); shift ;;
|
||||
--help|-h)
|
||||
grep '^#' "$0" | grep -v '!/usr' | sed 's/^# \?//'
|
||||
exit 0 ;;
|
||||
*)
|
||||
error "Unknown argument: $1"
|
||||
exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$AGENT_TARGET" in
|
||||
codex)
|
||||
# ~/.agents/skills is the current user-level Codex discovery location and
|
||||
# is intentionally agent-neutral. Commands are installed as normal skills.
|
||||
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.agents/skills}"
|
||||
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.agents/.ai-proj-helper-installed-skills.json}"
|
||||
;;
|
||||
dsh)
|
||||
if [[ "$SCOPE" != "project" && -z "${DSH_HOME:-}" ]]; then
|
||||
error "--agent dsh requires DSH_HOME (or set AI_PROJ_HELPER_SKILLS_DIR explicitly)"
|
||||
exit 1
|
||||
fi
|
||||
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${DSH_HOME}/skills}"
|
||||
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${DSH_HOME}/.ai-proj-helper-installed-skills.json}"
|
||||
;;
|
||||
claude)
|
||||
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.claude/skills}"
|
||||
COMMANDS_DIR="${AI_PROJ_HELPER_COMMANDS_DIR:-${HOME}/.claude/commands}"
|
||||
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.claude/.installed-skills.json}"
|
||||
;;
|
||||
*)
|
||||
error "Unsupported agent: $AGENT_TARGET (expected codex, dsh or claude)"
|
||||
exit 1
|
||||
;;
|
||||
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_get() {
|
||||
# state_get <install_name> -> prints version or empty string
|
||||
local name="$1"
|
||||
if [[ -f "$STATE_FILE" ]]; then
|
||||
python3 -c "
|
||||
import json,sys
|
||||
try:
|
||||
d=json.load(open('$STATE_FILE'))
|
||||
print(d.get('$name',{}).get('version',''))
|
||||
except: pass
|
||||
" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
state_digest() {
|
||||
# state_digest <install_name> -> prints installed content digest or empty string
|
||||
local name="$1"
|
||||
if [[ -f "$STATE_FILE" ]]; then
|
||||
python3 -c "
|
||||
import json
|
||||
try:
|
||||
d=json.load(open('$STATE_FILE'))
|
||||
print(d.get('$name',{}).get('content_digest',''))
|
||||
except: pass
|
||||
" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
state_set() {
|
||||
# state_set <install_name> <version> <install_type> <content_digest>
|
||||
local name="$1" ver="$2" itype="$3" digest="$4"
|
||||
python3 -c "
|
||||
import json,os
|
||||
f='$STATE_FILE'
|
||||
d=json.load(open(f)) if os.path.exists(f) else {}
|
||||
d['$name']={'version':'$ver','install_type':'$itype','content_digest':'$digest','agent':'$AGENT_TARGET'}
|
||||
json.dump(d,open(f,'w'),indent=2)
|
||||
" 2>/dev/null
|
||||
}
|
||||
|
||||
state_remove() {
|
||||
local name="$1"
|
||||
python3 -c "
|
||||
import json,os
|
||||
f='$STATE_FILE'
|
||||
if not os.path.exists(f): exit()
|
||||
d=json.load(open(f))
|
||||
d.pop('$name',None)
|
||||
json.dump(d,open(f,'w'),indent=2)
|
||||
" 2>/dev/null
|
||||
}
|
||||
|
||||
state_all_names() {
|
||||
if [[ -f "$STATE_FILE" ]]; then
|
||||
python3 -c "
|
||||
import json
|
||||
d=json.load(open('$STATE_FILE'))
|
||||
for k in d: print(k)
|
||||
" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Plugin discovery ───────────────────────────────────────────────────────────
|
||||
find_plugins() {
|
||||
find "$REPO_DIR" -path "*/skills-*/*-plugin/.claude-plugin/plugin.json" | sort
|
||||
}
|
||||
|
||||
read_field() {
|
||||
# read_field <json_file> <field>
|
||||
python3 -c "import json,sys; d=json.load(open('$1')); print(d.get('$2',''))" 2>/dev/null || true
|
||||
}
|
||||
|
||||
content_digest() {
|
||||
# Stable digest for one command file or a complete skill directory.
|
||||
python3 - "$1" <<'PY'
|
||||
import hashlib
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
target = pathlib.Path(sys.argv[1])
|
||||
if not target.exists():
|
||||
print("")
|
||||
raise SystemExit
|
||||
|
||||
digest = hashlib.sha256()
|
||||
files = [target] if target.is_file() else sorted(
|
||||
path for path in target.rglob("*") if path.is_file() or path.is_symlink()
|
||||
)
|
||||
for path in files:
|
||||
# A single-file command is renamed when installed for Claude. Hash its
|
||||
# content under a stable logical name so source and target compare equally.
|
||||
relative = "." if target.is_file() else path.relative_to(target).as_posix()
|
||||
digest.update(relative.encode("utf-8"))
|
||||
digest.update(b"\0")
|
||||
if path.is_symlink():
|
||||
digest.update(b"link\0")
|
||||
digest.update(os.readlink(path).encode("utf-8"))
|
||||
else:
|
||||
digest.update(path.read_bytes())
|
||||
digest.update(b"\0")
|
||||
print(digest.hexdigest())
|
||||
PY
|
||||
}
|
||||
|
||||
is_compatible_subset() {
|
||||
# True when every file in an existing legacy target also exists unchanged in
|
||||
# the repository source. This safely upgrades old SKILL.md-only installs.
|
||||
python3 - "$1" "$2" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
source = pathlib.Path(sys.argv[1])
|
||||
target = pathlib.Path(sys.argv[2])
|
||||
if not source.is_dir() or not target.is_dir():
|
||||
raise SystemExit(1)
|
||||
|
||||
for target_path in target.rglob("*"):
|
||||
if target_path.is_dir():
|
||||
continue
|
||||
source_path = source / target_path.relative_to(target)
|
||||
if not source_path.is_file() or target_path.is_symlink() != source_path.is_symlink():
|
||||
raise SystemExit(1)
|
||||
if target_path.is_symlink():
|
||||
if target_path.readlink() != source_path.readlink():
|
||||
raise SystemExit(1)
|
||||
elif target_path.read_bytes() != source_path.read_bytes():
|
||||
raise SystemExit(1)
|
||||
raise SystemExit(0)
|
||||
PY
|
||||
}
|
||||
|
||||
# Resolve the actual source directory to rsync from.
|
||||
# If skills/ has SKILL.md at the top level, use it directly.
|
||||
# If skills/ has a single subdirectory (e.g. skills/dev-test/SKILL.md), use that subdirectory.
|
||||
resolve_skills_src() {
|
||||
local skills_dir="$1"
|
||||
if [[ -f "$skills_dir/SKILL.md" ]]; then
|
||||
echo "$skills_dir"
|
||||
return
|
||||
fi
|
||||
# Find the first subdirectory that contains SKILL.md
|
||||
local sub
|
||||
sub="$(find "$skills_dir" -maxdepth 2 -name 'SKILL.md' | head -1)"
|
||||
if [[ -n "$sub" ]]; then
|
||||
echo "$(dirname "$sub")"
|
||||
return
|
||||
fi
|
||||
echo "$skills_dir"
|
||||
}
|
||||
|
||||
# ── Install a single plugin ────────────────────────────────────────────────────
|
||||
install_plugin() {
|
||||
INSTALL_ACTION=false
|
||||
local json_path="$1"
|
||||
local plugin_dir
|
||||
plugin_dir="$(dirname "$(dirname "$json_path")")" # strip /.claude-plugin/plugin.json
|
||||
local skills_dir="$plugin_dir/skills"
|
||||
|
||||
local install_name install_type dir_category version
|
||||
install_name="$(read_field "$json_path" install_name)"
|
||||
install_type="$(read_field "$json_path" install_type)"
|
||||
dir_category="$(read_field "$json_path" dir_category)"
|
||||
version="$(read_field "$json_path" version)"
|
||||
|
||||
local excluded
|
||||
for excluded in ${EXCLUDED_NAMES[@]+"${EXCLUDED_NAMES[@]}"}; do
|
||||
[[ "$install_name" == "$excluded" ]] && return
|
||||
done
|
||||
|
||||
# Skip if no install metadata (legacy plugin without our new fields)
|
||||
if [[ -z "$install_name" || -z "$install_type" ]]; then
|
||||
warn "$(basename "$plugin_dir"): missing install_name/install_type, skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
# Category filter
|
||||
if [[ -n "$CATEGORY_FILTER" && "$dir_category" != "$CATEGORY_FILTER" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Verify skills directory exists
|
||||
if [[ ! -d "$skills_dir" ]]; then
|
||||
warn "$install_name: no skills/ directory in plugin, skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
local effective_install_type="$install_type"
|
||||
if [[ "$AGENT_TARGET" == "codex" ]]; then
|
||||
effective_install_type="skill"
|
||||
fi
|
||||
|
||||
local status
|
||||
status="$(read_field "$json_path" status)"
|
||||
status="${status:-promoted}"
|
||||
|
||||
if [[ "$LIST_ONLY" == true ]]; then
|
||||
echo " [$dir_category] $status $effective_install_type:$install_name v$version"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$status" == "deprecated" ]]; then
|
||||
return
|
||||
fi
|
||||
if [[ "$status" == "beta" && "$INCLUDE_BETA" != true ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Resolve actual source (handles plugins where content sits one level deeper,
|
||||
# e.g. skills/dev-test/SKILL.md instead of skills/SKILL.md).
|
||||
local src_dir
|
||||
src_dir="$(resolve_skills_src "$skills_dir")"
|
||||
|
||||
local source_path target_path
|
||||
if [[ "$effective_install_type" == "command" ]]; then
|
||||
source_path="$src_dir/SKILL.md"
|
||||
target_path="$COMMANDS_DIR/${install_name}.md"
|
||||
else
|
||||
source_path="$src_dir"
|
||||
target_path="$SKILLS_DIR/$install_name"
|
||||
fi
|
||||
|
||||
if [[ ! -e "$source_path" ]]; then
|
||||
warn "$install_name: install source not found, skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
# Dev machines may symlink instead of copying: repository edits apply at once.
|
||||
if [[ "$LINK_MODE" == true && "$effective_install_type" != "command" ]]; then
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
dry "$install_name → symlink $target_path → $src_dir"
|
||||
else
|
||||
mkdir -p "$SKILLS_DIR"
|
||||
rm -rf "$target_path"
|
||||
ln -sfn "$src_dir" "$target_path"
|
||||
state_set "$install_name" "$version" "symlink" "$(content_digest "$src_dir")"
|
||||
ok "$install_name → symlink (v$version)"
|
||||
fi
|
||||
INSTALL_ACTION=true
|
||||
return
|
||||
fi
|
||||
|
||||
# A recorded content digest distinguishes repository updates from user edits.
|
||||
# Legacy state is adopted automatically only when the target is missing or
|
||||
# already identical to the repository source.
|
||||
local current_version recorded_digest source_digest target_digest
|
||||
current_version="$(state_get "$install_name")"
|
||||
recorded_digest="$(state_digest "$install_name")"
|
||||
source_digest="$(content_digest "$source_path")"
|
||||
target_digest="$(content_digest "$target_path")"
|
||||
|
||||
if [[ "$FORCE" == false && -n "$target_digest" && "$target_digest" == "$source_digest" ]]; then
|
||||
if [[ "$DRY_RUN" == false && ( "$current_version" != "$version" || "$recorded_digest" != "$source_digest" ) ]]; then
|
||||
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
local legacy_subset=false
|
||||
if [[ "$effective_install_type" == "skill" && -z "$recorded_digest" && -n "$target_digest" ]]; then
|
||||
if is_compatible_subset "$source_path" "$target_path"; then
|
||||
legacy_subset=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$FORCE" == false && -n "$target_digest" && "$legacy_subset" == false ]]; then
|
||||
if [[ -z "$recorded_digest" || "$target_digest" != "$recorded_digest" ]]; then
|
||||
warn "$install_name: local files were modified or have legacy unverified state — skipping (use --force once to adopt repository content)"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# Perform install
|
||||
if [[ "$effective_install_type" == "command" ]]; then
|
||||
# Single-file command → ~/.claude/commands/<name>.md
|
||||
local src_md="$src_dir/SKILL.md"
|
||||
if [[ ! -f "$src_md" ]]; then
|
||||
warn "$install_name: SKILL.md not found, skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
dry "$install_name → $COMMANDS_DIR/${install_name}.md"
|
||||
INSTALL_ACTION=true
|
||||
else
|
||||
mkdir -p "$COMMANDS_DIR"
|
||||
cp "$src_md" "$COMMANDS_DIR/${install_name}.md"
|
||||
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
|
||||
ok "$install_name → command (v$version)"
|
||||
INSTALL_ACTION=true
|
||||
fi
|
||||
|
||||
else
|
||||
# Standard skill directory → the selected agent's discovery root.
|
||||
local dst_dir="$SKILLS_DIR/$install_name"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
dry "$install_name → $dst_dir/"
|
||||
INSTALL_ACTION=true
|
||||
else
|
||||
mkdir -p "$dst_dir"
|
||||
# rsync resolved source (handles nested skills/ structures)
|
||||
rsync -a --checksum --delete "$src_dir/" "$dst_dir/"
|
||||
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
|
||||
ok "$install_name → skill (v$version)"
|
||||
INSTALL_ACTION=true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Cleanup removed plugins ────────────────────────────────────────────────────
|
||||
cleanup_removed() {
|
||||
if [[ "$CLEANUP" == false ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
info "Checking for removed plugins to clean up..."
|
||||
|
||||
# Collect all install_names still in repo
|
||||
local repo_names=()
|
||||
while IFS= read -r json_path; do
|
||||
local name
|
||||
name="$(read_field "$json_path" install_name)"
|
||||
[[ -n "$name" ]] && repo_names+=("$name")
|
||||
done < <(find_plugins)
|
||||
|
||||
# Check state file for installed plugins no longer in repo
|
||||
while IFS= read -r installed_name; do
|
||||
local found=false
|
||||
for repo_name in "${repo_names[@]}"; do
|
||||
[[ "$repo_name" == "$installed_name" ]] && found=true && break
|
||||
done
|
||||
|
||||
if [[ "$found" == false ]]; then
|
||||
local itype
|
||||
itype="$(python3 -c "import json; d=json.load(open('$STATE_FILE')); print(d.get('$installed_name',{}).get('install_type',''))" 2>/dev/null || true)"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
dry "Would remove: $installed_name ($itype)"
|
||||
else
|
||||
if [[ "$itype" == "command" ]]; then
|
||||
rm -f "$COMMANDS_DIR/${installed_name}.md"
|
||||
else
|
||||
rm -rf "${SKILLS_DIR:?}/$installed_name"
|
||||
fi
|
||||
state_remove "$installed_name"
|
||||
ok "Removed: $installed_name"
|
||||
fi
|
||||
fi
|
||||
done < <(state_all_names)
|
||||
}
|
||||
|
||||
# ── Audit ──────────────────────────────────────────────────────────────────────
|
||||
audit_target() {
|
||||
python3 - "$REPO_DIR" "$STATE_FILE" "$AGENT_TARGET" "$SKILLS_DIR" <<'PYEOF'
|
||||
import json, sys, pathlib
|
||||
repo, state_file, agent, skills_dir = sys.argv[1:5]
|
||||
root = pathlib.Path(repo)
|
||||
state = {}
|
||||
if pathlib.Path(state_file).is_file():
|
||||
try: state = json.loads(pathlib.Path(state_file).read_text())
|
||||
except Exception: state = {}
|
||||
rows = []
|
||||
for pj in sorted(root.glob('skills-*/*-plugin/.claude-plugin/plugin.json')):
|
||||
try: meta = json.loads(pj.read_text())
|
||||
except Exception: continue
|
||||
name = meta.get('install_name')
|
||||
if not name: continue
|
||||
status = meta.get('status', 'promoted')
|
||||
if status == 'deprecated':
|
||||
continue
|
||||
want = meta.get('version', '')
|
||||
got = state.get(name, {}).get('version', '')
|
||||
itype = state.get(name, {}).get('install_type', '')
|
||||
drift = '' if got == want else ('missing' if not got else 'stale')
|
||||
rows.append((name, status, want, got or '-', itype or '-', drift))
|
||||
drift_rows = [r for r in rows if r[5]]
|
||||
print(f'audit agent={agent} skills_dir={skills_dir}')
|
||||
print(f'{"name":34} {"status":10} {"repo":8} {"installed":9} {"mode":8} drift')
|
||||
for r in rows:
|
||||
print(f'{r[0]:34} {r[1]:10} {r[2]:8} {r[3]:9} {r[4]:8} {r[5]}')
|
||||
print(f'\n{len(rows)} 个技能,{len(drift_rows)} 个漂移(未安装/版本落后)')
|
||||
raise SystemExit(1 if drift_rows else 0)
|
||||
PYEOF
|
||||
}
|
||||
|
||||
# ── Main ───────────────────────────────────────────────────────────────────────
|
||||
main() {
|
||||
if [[ "$LIST_ONLY" == true ]]; then
|
||||
info "Available plugins in $REPO_DIR:"
|
||||
local count=0
|
||||
while IFS= read -r json_path; do
|
||||
install_plugin "$json_path"
|
||||
((count++)) || true
|
||||
done < <(find_plugins)
|
||||
echo ""
|
||||
info "Total: $count plugins"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$AUDIT_ONLY" == true ]]; then
|
||||
audit_target
|
||||
return
|
||||
fi
|
||||
|
||||
info "Installing skills for $AGENT_TARGET from: $REPO_DIR"
|
||||
if [[ "$NO_VALIDATE" != true && -f "$REPO_DIR/scripts/validate-skills.py" ]]; then
|
||||
if ! python3 "$REPO_DIR/scripts/validate-skills.py" >/tmp/ai-proj-helper-validate.log 2>&1; then
|
||||
error "validate-skills.py 未通过,修复后再装(或加 --no-validate 跳过)"
|
||||
tail -20 /tmp/ai-proj-helper-validate.log >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written"
|
||||
[[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER"
|
||||
|
||||
local installed=0
|
||||
|
||||
while IFS= read -r json_path; do
|
||||
install_plugin "$json_path"
|
||||
if [[ "$INSTALL_ACTION" == true ]]; then
|
||||
((installed++)) || true
|
||||
fi
|
||||
done < <(find_plugins)
|
||||
|
||||
cleanup_removed
|
||||
|
||||
echo ""
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
info "Dry run complete. $installed plugins would be installed/updated."
|
||||
else
|
||||
info "Done. $installed plugins installed/updated."
|
||||
info "State saved to: $STATE_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,15 @@
|
||||
# <YYYY-Www> 技能 evals 抽样回归
|
||||
|
||||
- 日期:YYYY-MM-DD
|
||||
- 范围:上周改动过的技能 + 高风险技能(dev-coding / dev-review / req-design)
|
||||
- 方法:每技能取 1 条关键用例,做「有技能 / 无技能」两组独立输出对比,按 rubric 打分(0–2)
|
||||
|
||||
| 技能 | 用例 | 有技能 | 无技能 | 差值 | 结论 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| | | | | | |
|
||||
|
||||
## 结论与新发现
|
||||
|
||||
- 有效技能:
|
||||
- 无增量技能(按 SCOPE 评估是否降级):
|
||||
- 需要补的用例:
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
"""列出各技能的 evals 与是否已有回归报告(reports/evals/<name>.md)。"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
rows = []
|
||||
for pj in sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json")):
|
||||
meta = json.loads(pj.read_text())
|
||||
name = meta.get("install_name")
|
||||
if not name:
|
||||
continue
|
||||
ev = next((pj.parent.parent / "skills").rglob("evals/evals.json"), None)
|
||||
cases = 0
|
||||
if ev:
|
||||
try:
|
||||
cases = len(json.loads(ev.read_text()).get("evals", []))
|
||||
except Exception:
|
||||
cases = 0
|
||||
report = ROOT / "reports" / "evals" / f"{name}.md"
|
||||
rows.append((name, meta.get("status", "promoted"), cases, report.is_file()))
|
||||
|
||||
with_evals = sum(1 for r in rows if r[2] > 0)
|
||||
with_report = sum(1 for r in rows if r[3])
|
||||
print(f"{'技能':32} {'status':10} {'用例':4} 回归报告")
|
||||
for name, status, cases, has_report in rows:
|
||||
if cases == 0:
|
||||
continue
|
||||
print(f"{name:32} {status:10} {cases:<4} {'有' if has_report else '-'}")
|
||||
print(f"\n有 evals 的技能: {with_evals}/{len(rows)};已有回归报告: {with_report}/{with_evals}")
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate catalog.json — the reviewable list of every skill in this repo.
|
||||
|
||||
Usage: python3 scripts/generate-catalog.py [--check]
|
||||
(default) write catalog.json
|
||||
--check fail if catalog.json is out of date (for CI)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CATALOG = ROOT / "catalog.json"
|
||||
|
||||
|
||||
def frontmatter(text: str) -> dict:
|
||||
if not text.startswith("---"):
|
||||
return {}
|
||||
end = text.find("\n---", 3)
|
||||
if end == -1:
|
||||
return {}
|
||||
data: dict = {}
|
||||
metadata: dict = {}
|
||||
key = None
|
||||
for line in text[3:end].splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
m = re.match(r"^([A-Za-z0-9_-]+):\s*(.*)$", line)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
data[key] = m.group(2).strip().strip('"').strip("'")
|
||||
continue
|
||||
m = re.match(r"^\s+([A-Za-z0-9_-]+):\s*(.*)$", line)
|
||||
if m and key == "metadata":
|
||||
metadata[m.group(1)] = m.group(2).strip().strip('"').strip("'")
|
||||
if metadata:
|
||||
data["metadata"] = metadata
|
||||
return data
|
||||
|
||||
|
||||
def build() -> dict:
|
||||
skills = []
|
||||
for plugin_json in sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json")):
|
||||
plugin_dir = plugin_json.parent.parent
|
||||
meta = json.loads(plugin_json.read_text(encoding="utf-8"))
|
||||
skill_files = sorted((plugin_dir / "skills").rglob("SKILL.md"))
|
||||
if not skill_files:
|
||||
continue
|
||||
skill_md = skill_files[0]
|
||||
fm = frontmatter(skill_md.read_text(encoding="utf-8", errors="replace"))
|
||||
desc = fm.get("description", "")
|
||||
body = skill_md.read_text(encoding="utf-8", errors="replace")
|
||||
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(
|
||||
{
|
||||
"name": install_name,
|
||||
"category": meta.get("dir_category"),
|
||||
"version": meta.get("version"),
|
||||
"status": meta.get("status", "promoted"),
|
||||
"invocation": (fm.get("metadata") or {}).get("invocation", "model-invoked"),
|
||||
"description": desc,
|
||||
"description_len": len(desc),
|
||||
"path": str(skill_md.relative_to(ROOT)),
|
||||
"digest": hashlib.sha256(body.encode("utf-8")).hexdigest()[:16],
|
||||
"has_evals": has_evals,
|
||||
"has_docs": has_docs,
|
||||
}
|
||||
)
|
||||
counts: dict = {}
|
||||
for skill in skills:
|
||||
key = f"{skill['status']}/{skill['invocation']}"
|
||||
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 {
|
||||
"repo": "ai-proj-helper",
|
||||
"coverage": coverage,
|
||||
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"total": len(skills),
|
||||
"counts": counts,
|
||||
"skills": skills,
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
catalog = build()
|
||||
text = json.dumps(catalog, ensure_ascii=False, indent=2) + "\n"
|
||||
if "--check" in sys.argv:
|
||||
if not CATALOG.is_file():
|
||||
print("catalog.json 不存在,请运行 python3 scripts/generate-catalog.py")
|
||||
return 1
|
||||
current = json.loads(CATALOG.read_text(encoding="utf-8"))
|
||||
current.pop("generated_at", None)
|
||||
fresh = dict(catalog)
|
||||
fresh.pop("generated_at", None)
|
||||
if current != fresh:
|
||||
print("catalog.json 已过期,请重新生成")
|
||||
return 1
|
||||
print("catalog.json 是最新的")
|
||||
return 0
|
||||
CATALOG.write_text(text, encoding="utf-8")
|
||||
print(f"写入 {CATALOG.relative_to(ROOT)}:{catalog['total']} 个技能 {catalog['counts']}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
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 ..."
|
||||
Executable
+143
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env bash
|
||||
# 安装 / 卸载 / 查看技能治理的定时任务(macOS 用户级 launchd)
|
||||
#
|
||||
# 为什么用 launchd 而不是会话提醒:会话提醒随会话消失,节拍就断了;
|
||||
# launchd 是宿主级,跟仓库绑定,换机器重跑一次即可复现。
|
||||
#
|
||||
# 用法:
|
||||
# ./scripts/install-schedulers.sh 安装(幂等,会先卸载再装)
|
||||
# ./scripts/install-schedulers.sh --status 看三个任务的加载状态
|
||||
# ./scripts/install-schedulers.sh --run daily 立刻跑一次(调试用)
|
||||
# ./scripts/install-schedulers.sh --uninstall 卸载
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TASK="$ROOT/scripts/scheduled-task.sh"
|
||||
AGENTS="$HOME/Library/LaunchAgents"
|
||||
LOGDIR="$ROOT/reports/_scheduler"
|
||||
PREFIX="com.pipexerp.ai-proj-skill"
|
||||
UID_N="$(id -u)"
|
||||
JOBS="daily weekly monthly"
|
||||
|
||||
SCHED_PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
SCHED_PATH="$(dirname "$(command -v node)"):$SCHED_PATH"
|
||||
fi
|
||||
|
||||
log() { printf '%s\n' "$*"; }
|
||||
|
||||
calendar_xml() {
|
||||
case "$1" in
|
||||
daily) printf ' <key>Hour</key><integer>7</integer>\n <key>Minute</key><integer>20</integer>' ;;
|
||||
weekly) printf ' <key>Weekday</key><integer>1</integer>\n <key>Hour</key><integer>10</integer>\n <key>Minute</key><integer>0</integer>' ;;
|
||||
monthly) printf ' <key>Day</key><integer>1</integer>\n <key>Hour</key><integer>10</integer>\n <key>Minute</key><integer>0</integer>' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
write_plist() {
|
||||
local job="$1" label="$PREFIX-$job" file="$AGENTS/$PREFIX-$job.plist" cal
|
||||
cal="$(calendar_xml "$job")"
|
||||
cat > "$file" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key><string>$label</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>$TASK</string>
|
||||
<string>$job</string>
|
||||
</array>
|
||||
<key>WorkingDirectory</key><string>$ROOT</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>HOME</key><string>$HOME</string>
|
||||
<key>PATH</key><string>$SCHED_PATH</string>
|
||||
</dict>
|
||||
<key>StandardOutPath</key><string>$LOGDIR/$job.log</string>
|
||||
<key>StandardErrorPath</key><string>$LOGDIR/$job.err.log</string>
|
||||
<key>RunAtLoad</key><false/>
|
||||
<key>StartCalendarInterval</key>
|
||||
<dict>
|
||||
$cal
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
}
|
||||
|
||||
unload_one() {
|
||||
local label="$PREFIX-$1"
|
||||
launchctl bootout "gui/$UID_N/$label" >/dev/null 2>&1 || launchctl unload "$AGENTS/$label.plist" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
install() {
|
||||
mkdir -p "$AGENTS" "$LOGDIR"
|
||||
chmod +x "$TASK" 2>/dev/null
|
||||
local job label
|
||||
for job in $JOBS; do
|
||||
label="$PREFIX-$job"
|
||||
write_plist "$job"
|
||||
unload_one "$job"
|
||||
if launchctl bootstrap "gui/$UID_N" "$AGENTS/$label.plist" 2>/dev/null; then
|
||||
log "已加载 $label"
|
||||
elif launchctl load -w "$AGENTS/$label.plist" 2>/dev/null; then
|
||||
log "已加载(load) $label"
|
||||
else
|
||||
log "加载失败 $label(检查 plist: $AGENTS/$label.plist)"
|
||||
fi
|
||||
done
|
||||
log ""
|
||||
log "触发时间:daily 每天 07:20 / weekly 每周一 10:00 / monthly 每月 1 日 10:00(1/4/7/10 月附季度清单)"
|
||||
log "日志:${LOGDIR#$ROOT/}/<job>.log"
|
||||
log "产出:reports/skill-health/、reports/evals/queue-*.md、reports/monthly/"
|
||||
}
|
||||
|
||||
status() {
|
||||
local job label
|
||||
for job in $JOBS; do
|
||||
label="$PREFIX-$job"
|
||||
if launchctl print "gui/$UID_N/$label" >/dev/null 2>&1; then
|
||||
printf '%-8s 已加载 %s\n' "$job" \
|
||||
"$(launchctl print "gui/$UID_N/$label" 2>/dev/null | grep -E 'state =|last exit code' | tr -d '\t' | tr '\n' ' ')"
|
||||
else
|
||||
printf '%-8s 未加载\n' "$job"
|
||||
fi
|
||||
done
|
||||
log ""
|
||||
log "日志目录:${LOGDIR#$ROOT/}"
|
||||
log "最近产出:"
|
||||
ls -1t "$ROOT/reports/skill-health"/daily-*.md "$ROOT/reports/evals"/queue-*.md "$ROOT/reports/monthly"/review-*.md 2>/dev/null | head -3 | sed "s#^$ROOT/# - #"
|
||||
}
|
||||
|
||||
run_now() {
|
||||
local job="${1:-}"
|
||||
case "$job" in daily|weekly|monthly) ;; *) log "用法: $0 --run daily|weekly|monthly"; exit 2 ;; esac
|
||||
mkdir -p "$LOGDIR"
|
||||
local label="$PREFIX-$job"
|
||||
if launchctl print "gui/$UID_N/$label" >/dev/null 2>&1; then
|
||||
launchctl kickstart -k "gui/$UID_N/$label" && log "已触发 $job(看 ${LOGDIR#$ROOT/}/$job.log)"
|
||||
else
|
||||
log "任务未加载,直接前台执行"
|
||||
/bin/bash "$TASK" "$job"
|
||||
fi
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
local job label
|
||||
for job in $JOBS; do
|
||||
label="$PREFIX-$job"
|
||||
unload_one "$job"
|
||||
rm -f "$AGENTS/$label.plist"
|
||||
log "已卸载 $label"
|
||||
done
|
||||
}
|
||||
|
||||
case "${1:-install}" in
|
||||
install|--install) install ;;
|
||||
--status|status) status ;;
|
||||
--run) run_now "${2:-}" ;;
|
||||
--uninstall|uninstall) uninstall ;;
|
||||
*) log "用法: $0 [install|--status|--run daily|weekly|monthly|--uninstall]"; exit 2 ;;
|
||||
esac
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# 月度技能审阅:一条命令跑完 MAINTENANCE 里的月度项。
|
||||
# 用法: ./scripts/monthly-review.sh
|
||||
set -uo pipefail
|
||||
cd "$(git rev-parse --show-toplevel)" || exit 1
|
||||
echo "== 技能月度审阅 $(date '+%F %H:%M') =="
|
||||
echo; echo "-- 1. 覆盖率(promoted + model-invoked)--"
|
||||
python3 scripts/validate-skills.py --coverage
|
||||
echo; echo "-- 2. 本仓库安全扫描 --"
|
||||
./scripts/scan-skill.sh | tail -2
|
||||
echo; echo "-- 3. 第三方已装技能扫描 --"
|
||||
./scripts/scan-installed.sh | tail -2
|
||||
echo; echo "-- 4. catalog 是否最新 --"
|
||||
python3 scripts/generate-catalog.py --check
|
||||
echo; echo "-- 5. catalog 摘要与 description 异常项 --"
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
d = json.load(open('catalog.json'))
|
||||
print("总数:", d['total'], "| 分布:", d['counts'])
|
||||
bad = [(x['name'], x['description_len']) for x in d['skills']
|
||||
if x['description_len'] < 60 or x['description_len'] > 600]
|
||||
print("description 长度异常:", bad if bad else "无")
|
||||
cov = d.get('coverage', {})
|
||||
print(f"覆盖率: evals {cov.get('with_evals')}/{cov.get('required')},文档 {cov.get('with_docs')}/{cov.get('required')}")
|
||||
PY
|
||||
@@ -0,0 +1,13 @@
|
||||
# 安全扫描允许清单(人工确认过的误报)
|
||||
# 格式:<路径子串>::<匹配文本中的特征串>
|
||||
# 例:某技能文档把危险命令当作“禁止清单”引用,而非真的要执行。
|
||||
skills-dev/slark-cicd-plugin::NEVER_AUTO
|
||||
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
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
# scan-skill.sh — security scan before a skill enters the team library.
|
||||
#
|
||||
# Usage: ./scripts/scan-skill.sh [plugin-dir ...]
|
||||
# With no arguments scans every skills-*/*-plugin directory.
|
||||
# Exit 0 = clean (warnings allowed), 1 = high severity finding.
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
HIGH=0
|
||||
WARN=0
|
||||
|
||||
# High severity: destructive, remote-code-execution, secret material.
|
||||
HIGH_PATTERNS=(
|
||||
'rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*[[:space:]]+/($|[[:space:]])'
|
||||
'curl[^|]*\|[[:space:]]*(ba|z)?sh'
|
||||
'wget[^|]*\|[[:space:]]*(ba|z)?sh'
|
||||
'chmod[[:space:]]+777'
|
||||
'BEGIN (RSA|OPENSSH|EC|DSA) PRIVATE KEY'
|
||||
'sk-[A-Za-z0-9]{16,}'
|
||||
'AKIA[0-9A-Z]{16}'
|
||||
'ghp_[A-Za-z0-9]{20,}'
|
||||
)
|
||||
# Warnings: machine-specific paths, plain http endpoints.
|
||||
WARN_PATTERNS=(
|
||||
'/Users/[A-Za-z0-9._-]+/'
|
||||
'http://[A-Za-z0-9.:-]+'
|
||||
'password[[:space:]]*=[[:space:]]*[^$]'
|
||||
)
|
||||
|
||||
targets=("$@")
|
||||
if [[ ${#targets[@]} -eq 0 ]]; then
|
||||
while IFS= read -r dir; do
|
||||
targets+=("$(dirname "$(dirname "$dir")")")
|
||||
done < <(find "$ROOT" -path "*/skills-*/*-plugin/.claude-plugin/plugin.json" | sort)
|
||||
fi
|
||||
|
||||
ALLOWLIST="$ROOT/scripts/scan-allowlist.txt"
|
||||
is_allowlisted() {
|
||||
# is_allowlisted <abs_path> <matched_line>
|
||||
local path="$1" line="$2" entry sub needle
|
||||
[[ -f "$ALLOWLIST" ]] || return 1
|
||||
while IFS= read -r entry; do
|
||||
[[ -z "$entry" || "$entry" == \#* ]] && continue
|
||||
sub="${entry%%::*}"
|
||||
needle="${entry#*::}"
|
||||
if [[ "$path" == *"$sub"* && "$line" == *"$needle"* ]]; then
|
||||
return 0
|
||||
fi
|
||||
done < "$ALLOWLIST"
|
||||
return 1
|
||||
}
|
||||
|
||||
for dir in "${targets[@]}"; do
|
||||
rel="${dir#"$ROOT"/}"
|
||||
for pattern in "${HIGH_PATTERNS[@]}"; do
|
||||
while IFS= read -r hit; do
|
||||
abs="${hit%%:*}"
|
||||
detail="${hit#*:}"
|
||||
if is_allowlisted "$abs" "$hit"; then
|
||||
echo "[allow] $rel: $detail"
|
||||
continue
|
||||
fi
|
||||
echo "[HIGH] $rel: $detail"
|
||||
HIGH=$((HIGH + 1))
|
||||
done < <(grep -rnE --include='*.md' --include='*.sh' --include='*.py' --include='*.js' --include='*.ts' --include='*.json' -- "$pattern" "$dir" 2>/dev/null | cut -c1-200)
|
||||
done
|
||||
for pattern in "${WARN_PATTERNS[@]}"; do
|
||||
while IFS= read -r hit; do
|
||||
# 本机回环地址不算风险(本地开发/测试示例)
|
||||
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#*:}"
|
||||
WARN=$((WARN + 1))
|
||||
done < <(grep -rnE --include='*.md' --include='*.sh' --include='*.py' --include='*.js' --include='*.ts' -- "$pattern" "$dir" 2>/dev/null | cut -c1-200)
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "扫描完成:${#targets[@]} 个插件,$HIGH 个高危,$WARN 个警告"
|
||||
if [[ "$HIGH" -gt 0 ]]; then
|
||||
echo "存在高危项,禁止入库,除非逐条确认并记录原因。"
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
Executable
+162
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env bash
|
||||
# 技能治理定时任务入口(由 launchd 调用,见 scripts/install-schedulers.sh)
|
||||
#
|
||||
# 设计原则:脚本只做「确定性」的事——跑门禁、扫漂移、生成清单。
|
||||
# 需要模型判断的部分(真跑 evals、人工审阅 routing)不在脚本里做,只生成 queue 文件,
|
||||
# 由项目经理 Bot 在 7:30 项目日报里接手执行。这样节拍不依赖任何会话的记忆。
|
||||
#
|
||||
# 用法:
|
||||
# ./scripts/scheduled-task.sh daily # 每天 07:20,先于 7:30 项目日报
|
||||
# ./scripts/scheduled-task.sh weekly # 每周一 10:00
|
||||
# ./scripts/scheduled-task.sh monthly # 每月 1 日 10:00(1/4/7/10 月附带季度清单)
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT" || exit 1
|
||||
|
||||
JOB="${1:-daily}"
|
||||
DAY="$(date '+%F')"
|
||||
HEALTH_DIR="$ROOT/reports/skill-health"
|
||||
EVALS_DIR="$ROOT/reports/evals"
|
||||
MONTHLY_DIR="$ROOT/reports/monthly"
|
||||
mkdir -p "$HEALTH_DIR" "$EVALS_DIR" "$MONTHLY_DIR"
|
||||
|
||||
log() { printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$*"; }
|
||||
|
||||
# ---------- 公共采集:漂移 / 入库扫描 / 覆盖率 / 第三方 ----------
|
||||
DRIFT_LINE=""; SCAN_LINE=""; COV_LINE=""; THIRD_LINE=""
|
||||
DRIFT_N="0"; HIGH_N="0"
|
||||
collect_health() {
|
||||
local audit
|
||||
audit="$(./install-skills.sh --audit 2>&1)"
|
||||
DRIFT_LINE="$(printf '%s\n' "$audit" | grep -E '[0-9]+ 个漂移' | tail -1)"
|
||||
SCAN_LINE="$(./scripts/scan-skill.sh 2>&1 | grep '扫描完成' | tail -1)"
|
||||
COV_LINE="$(/usr/bin/python3 scripts/validate-skills.py --coverage 2>&1 | grep '覆盖率' | tail -1)"
|
||||
THIRD_LINE="$(./scripts/scan-installed.sh 2>&1 | grep '扫描完成' | tail -1)"
|
||||
DRIFT_N="$(printf '%s' "$DRIFT_LINE" | grep -oE '[0-9]+ 个漂移' | grep -oE '^[0-9]+' || true)"
|
||||
HIGH_N="$(printf '%s' "$SCAN_LINE" | grep -oE '[0-9]+ 个高危' | grep -oE '^[0-9]+' || true)"
|
||||
DRIFT_N="${DRIFT_N:-0}"
|
||||
HIGH_N="${HIGH_N:-0}"
|
||||
AUDIT_OUT="$audit"
|
||||
}
|
||||
|
||||
# ---------- 每日:漂移 + 入库扫描,写一份可被日报直接读的健康快照 ----------
|
||||
run_daily() {
|
||||
local md="$HEALTH_DIR/daily-$DAY.md" todo=0
|
||||
collect_health
|
||||
{
|
||||
echo "# 技能健康 $DAY"
|
||||
echo
|
||||
echo "- 漂移:${DRIFT_LINE:-未采集}"
|
||||
echo "- 入库扫描:${SCAN_LINE:-未采集}"
|
||||
echo "- 覆盖率:${COV_LINE:-未采集}"
|
||||
echo "- 第三方技能:${THIRD_LINE:-未采集}"
|
||||
echo
|
||||
echo "## 需要处理"
|
||||
if [ "$DRIFT_N" != "0" ]; then
|
||||
echo "- 漂移 ${DRIFT_N} 个(未安装 / 版本落后),按 MAINTENANCE「每日」处理"
|
||||
todo=1
|
||||
fi
|
||||
if [ "$HIGH_N" != "0" ]; then
|
||||
echo "- 入库扫描高危 ${HIGH_N} 个,立即处理,不等日报"
|
||||
todo=1
|
||||
fi
|
||||
[ "$todo" = "0" ] && echo "无"
|
||||
echo
|
||||
echo "## 原始输出(audit 尾部)"
|
||||
echo '```'
|
||||
printf '%s\n' "$AUDIT_OUT" | tail -15
|
||||
echo '```'
|
||||
} > "$md"
|
||||
cp "$md" "$HEALTH_DIR/latest.md"
|
||||
log "daily -> ${md#$ROOT/}"
|
||||
log "漂移 ${DRIFT_N} / 高危 ${HIGH_N} / ${SCAN_LINE:-扫描未采集}"
|
||||
}
|
||||
|
||||
# ---------- 每周:生成 evals 抽样队列(执行仍需模型,见 MAINTENANCE「每周一」)----------
|
||||
run_weekly() {
|
||||
local md="$EVALS_DIR/queue-$DAY.md"
|
||||
local changed changed_total highrisk reports
|
||||
changed_total="$(git log --since='7 days ago' --name-only --pretty=format: 2>/dev/null \
|
||||
| grep -oE '^skills-[a-z]+/[^/]+-plugin/' | sort -u | wc -l | tr -d ' ')"
|
||||
changed="$(git log --since='7 days ago' --name-only --pretty=format: 2>/dev/null \
|
||||
| grep -oE '^skills-[a-z]+/[^/]+-plugin/' \
|
||||
| sed -E 's#^skills-[a-z]+/##; s#-plugin/$##' \
|
||||
| sort | uniq -c | sort -rn | head -5 | awk '{print $2}' | tr '\n' ' ')"
|
||||
highrisk="dev-coding dev-review req-design"
|
||||
reports="$(ls "$EVALS_DIR"/*.md 2>/dev/null | grep -vE 'TEMPLATE|queue-' | wc -l | tr -d ' ')"
|
||||
{
|
||||
echo "# 本周 evals 抽样队列 $DAY"
|
||||
echo
|
||||
echo "现有回归报告:${reports:-0} 份(目标:每周至少 +1 份)"
|
||||
echo
|
||||
echo "## 范围"
|
||||
echo
|
||||
echo "- 上周改动技能共 ${changed_total:-0} 个,按改动次数取前 5:${changed:-(无)}"
|
||||
echo "- 高风险固定项:$highrisk"
|
||||
echo
|
||||
echo "## 做法(每个技能)"
|
||||
echo
|
||||
echo "1. 取 1 条关键用例,跑「有技能 / 无技能」两遍;"
|
||||
echo "2. 按 rubric 打分,写增量;"
|
||||
echo "3. 结果写入 \`reports/evals/$DAY-<技能名>.md\`(模板 \`reports/evals/TEMPLATE.md\`)。"
|
||||
echo
|
||||
echo "## 判读"
|
||||
echo
|
||||
echo "- 连续两次无增量 → 按 \`SCOPE.md\` 评估降级。"
|
||||
echo "- 全量只在治理大改时做,不做每周全量。"
|
||||
echo
|
||||
echo "> 本文件由 launchd 每周一 10:00 自动生成;脚本不调模型,执行由 agent 接手。"
|
||||
} > "$md"
|
||||
log "weekly -> ${md#$ROOT/}(报告现有 ${reports:-0} 份)"
|
||||
}
|
||||
|
||||
# ---------- 每月:一条命令出齐审阅项;季度月附治理清单 ----------
|
||||
run_monthly() {
|
||||
local mo md mon q
|
||||
mo="$(date '+%Y-%m')"
|
||||
mon="$(date '+%m')"
|
||||
md="$MONTHLY_DIR/review-$mo.md"
|
||||
{
|
||||
echo "# 技能月度审阅 $mo"
|
||||
echo
|
||||
./scripts/monthly-review.sh 2>&1
|
||||
} > "$md"
|
||||
collect_health
|
||||
{
|
||||
echo
|
||||
echo "## 补充:漂移与第三方"
|
||||
echo
|
||||
echo "- ${DRIFT_LINE:-漂移未采集}"
|
||||
echo "- ${THIRD_LINE:-第三方未采集}"
|
||||
} >> "$md"
|
||||
log "monthly -> ${md#$ROOT/}"
|
||||
case "$mon" in
|
||||
01|04|07|10)
|
||||
q="$(date '+%Y')-Q$(( (10#$mon - 1) / 3 + 1 ))"
|
||||
local mq="$MONTHLY_DIR/quarterly-$q.md"
|
||||
{
|
||||
echo "# 季度治理复核 $q"
|
||||
echo
|
||||
echo "按 MAINTENANCE「每季度首月 1 日 10:30」逐项打勾:"
|
||||
echo
|
||||
echo "- [ ] 过一遍 \`docs/skill-routing.md\`:删掉不再用的引用、补上新技能"
|
||||
echo "- [ ] 清理 \`deprecated\`:满 3 个月且无回滚需求 → 删除插件目录"
|
||||
echo "- [ ] 复核分桶与调用模式:还有没有命令型技能占着 \`model-invoked\`"
|
||||
echo "- [ ] 复核覆盖率:\`--coverage\` 两项缺口是否按路线图收敛"
|
||||
echo "- [ ] 更新 \`MAINTENANCE.md\` 与 \`CHANGELOG.md\`"
|
||||
} > "$mq"
|
||||
log "quarterly -> ${mq#$ROOT/}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
case "$JOB" in
|
||||
daily) run_daily ;;
|
||||
weekly) run_weekly ;;
|
||||
monthly) run_monthly ;;
|
||||
*)
|
||||
echo "用法: $0 daily|weekly|monthly" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
Executable
+217
@@ -0,0 +1,217 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate plugins and SKILL.md files against the Agent Skills spec + team rules.
|
||||
|
||||
Usage: python3 scripts/validate-skills.py [--strict]
|
||||
Exit 0 = pass, 1 = errors found.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
ALLOWED_FRONTMATTER = {
|
||||
"name", "description", "license", "compatibility", "metadata",
|
||||
"allowed-tools", "disable-model-invocation", "version",
|
||||
}
|
||||
NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,63}$")
|
||||
BODY_MAX_BYTES = 20 * 1024
|
||||
DESC_MIN, DESC_MAX = 30, 1024
|
||||
STATUSES = {"promoted", "beta", "deprecated"}
|
||||
INVOCATIONS = {"model-invoked", "user-invoked"}
|
||||
REQUIRED_PLUGIN_FIELDS = ("install_name", "install_type", "dir_category", "version")
|
||||
|
||||
errors: list[str] = []
|
||||
warnings: list[str] = []
|
||||
|
||||
|
||||
def parse_frontmatter(text: str):
|
||||
"""Return (frontmatter dict, body text). Supports one level of nesting."""
|
||||
if not text.startswith("---"):
|
||||
return {}, text
|
||||
end = text.find("\n---", 3)
|
||||
if end == -1:
|
||||
return {}, text
|
||||
raw, body = text[3:end], text[end + 4:]
|
||||
data: dict = {}
|
||||
metadata: dict = {}
|
||||
key = None
|
||||
for line in raw.splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
m = re.match(r"^([A-Za-z0-9_-]+):\s*(.*)$", line)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
data[key] = m.group(2).strip().strip('"').strip("'")
|
||||
continue
|
||||
m = re.match(r"^\s+([A-Za-z0-9_-]+):\s*(.*)$", line)
|
||||
if m and key == "metadata":
|
||||
metadata[m.group(1)] = m.group(2).strip().strip('"').strip("'")
|
||||
elif key:
|
||||
data[key] = (data.get(key, "") + " " + line.strip()).strip()
|
||||
if metadata:
|
||||
data["metadata"] = metadata
|
||||
return data, body
|
||||
|
||||
|
||||
def check_plugin(plugin_json: Path):
|
||||
plugin_dir = plugin_json.parent.parent
|
||||
rel = plugin_dir.relative_to(ROOT)
|
||||
try:
|
||||
meta = json.loads(plugin_json.read_text(encoding="utf-8"))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
errors.append(f"{rel}: plugin.json 解析失败: {exc}")
|
||||
return
|
||||
|
||||
for field in REQUIRED_PLUGIN_FIELDS:
|
||||
if not meta.get(field):
|
||||
errors.append(f"{rel}: plugin.json 缺少 {field}")
|
||||
status = meta.get("status", "")
|
||||
if status and status not in STATUSES:
|
||||
errors.append(f"{rel}: status 非法 {status!r}(应为 {sorted(STATUSES)})")
|
||||
install_name = meta.get("install_name", "")
|
||||
if install_name and not NAME_RE.match(install_name):
|
||||
errors.append(f"{rel}: install_name 不合规 {install_name!r}")
|
||||
|
||||
skills_dir = plugin_dir / "skills"
|
||||
skill_files = sorted(skills_dir.rglob("SKILL.md")) if skills_dir.is_dir() else []
|
||||
if not skill_files:
|
||||
errors.append(f"{rel}: 没有找到 skills/**/SKILL.md")
|
||||
return
|
||||
for skill_md in skill_files:
|
||||
check_skill(rel, skill_md, meta)
|
||||
|
||||
|
||||
def check_skill(rel: Path, skill_md: Path, meta: dict):
|
||||
text = skill_md.read_text(encoding="utf-8", errors="replace")
|
||||
fm, body = parse_frontmatter(text)
|
||||
where = f"{rel}/{skill_md.name}"
|
||||
|
||||
name = fm.get("name", "")
|
||||
if not name:
|
||||
errors.append(f"{where}: frontmatter 缺少 name")
|
||||
elif not NAME_RE.match(name):
|
||||
errors.append(f"{where}: name 不合规 {name!r}(小写字母/数字/连字符,≤64)")
|
||||
|
||||
desc = fm.get("description", "")
|
||||
if not desc:
|
||||
errors.append(f"{where}: frontmatter 缺少 description")
|
||||
elif not (DESC_MIN <= len(desc) <= DESC_MAX):
|
||||
warnings.append(f"{where}: description 长度 {len(desc)},建议 {DESC_MIN}-{DESC_MAX}(决定是否被触发)")
|
||||
|
||||
for key in fm:
|
||||
if key == "metadata":
|
||||
continue
|
||||
if key not in ALLOWED_FRONTMATTER:
|
||||
warnings.append(f"{where}: 非标准 frontmatter 字段 {key!r},建议移入 metadata")
|
||||
|
||||
body_bytes = len(body.encode("utf-8"))
|
||||
if body_bytes > BODY_MAX_BYTES:
|
||||
warnings.append(f"{where}: 正文 {body_bytes} 字节,超过 20KB 预算,建议拆 references/")
|
||||
|
||||
metadata = fm.get("metadata") or {}
|
||||
invocation = metadata.get("invocation", "")
|
||||
if invocation and invocation not in INVOCATIONS:
|
||||
errors.append(f"{where}: metadata.invocation 非法 {invocation!r}")
|
||||
disable_model = str(fm.get("disable-model-invocation", "")).lower() == "true"
|
||||
if invocation == "user-invoked" and not disable_model:
|
||||
errors.append(f"{where}: user-invoked 技能必须设置 disable-model-invocation: true")
|
||||
if invocation == "model-invoked" and disable_model:
|
||||
errors.append(f"{where}: model-invoked 技能不应设置 disable-model-invocation: true")
|
||||
|
||||
status = meta.get("status", "")
|
||||
if status == "promoted":
|
||||
docs = ROOT / "docs" / "skills" / f"{meta.get('install_name', name)}.md"
|
||||
if not docs.is_file():
|
||||
warnings.append(f"{where}: promoted 技能缺少四段式文档 docs/skills/{docs.name}")
|
||||
|
||||
# Relative links must resolve (inline code spans are examples, skip them).
|
||||
scan_body = re.sub(r"`[^`]*`", "", body)
|
||||
for target in re.findall(r"\]\((\.[^)\s]+|[A-Za-z0-9_-]+/[^)\s]+)\)", scan_body):
|
||||
if target.startswith(("http://", "https://", "#")):
|
||||
continue
|
||||
if not (skill_md.parent / target).exists():
|
||||
warnings.append(f"{where}: 引用的相对路径不存在: {target}")
|
||||
|
||||
|
||||
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:
|
||||
strict = "--strict" in sys.argv
|
||||
if "--coverage" in sys.argv:
|
||||
return coverage_report()
|
||||
plugins = sorted(ROOT.glob("skills-*/*-plugin/.claude-plugin/plugin.json"))
|
||||
if not plugins:
|
||||
print("未找到任何 plugin.json", file=sys.stderr)
|
||||
return 1
|
||||
for plugin_json in plugins:
|
||||
check_plugin(plugin_json)
|
||||
|
||||
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:
|
||||
print(f"[warn] {warning}")
|
||||
for error in errors:
|
||||
print(f"[error] {error}")
|
||||
print(f"\n检查 {len(plugins)} 个插件:{len(errors)} 个错误,{len(warnings)} 个警告")
|
||||
if errors:
|
||||
return 1
|
||||
if strict and warnings:
|
||||
print("--strict 模式下,警告视为失败")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -4,5 +4,9 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "biz-contract",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: biz-contract
|
||||
description: 商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 商务合同撰写技能 (biz-contract)
|
||||
|
||||
## 概述
|
||||
|
||||
本技能用于辅助商务合同的撰写与管理,支持多家公司主体、多种合同类型。
|
||||
@@ -631,112 +631,9 @@ api.publish_to_feishu(doc_id, "物流服务合同-妗晨与名风")
|
||||
|
||||
---
|
||||
|
||||
## 飞书文档集成
|
||||
|
||||
本技能已集成飞书文档,可将合同直接创建到飞书云文档。
|
||||
## 详细内容
|
||||
|
||||
### 权限说明
|
||||
以下章节已移到 [references/details.md](references/details.md):
|
||||
|
||||
**重要**:飞书文档默认对组织内权限是开放的,允许编辑。创建的合同文档在组织内成员均可查看和编辑。如需限制访问权限,请在飞书中手动调整文档权限设置。
|
||||
|
||||
### 可用工具
|
||||
|
||||
| 工具 | 功能 |
|
||||
|------|------|
|
||||
| `create_document` | 创建空白飞书文档 |
|
||||
| `write_document` | 向已有文档写入内容 |
|
||||
| `create_and_write_document` | 创建文档并写入内容(推荐) |
|
||||
| `get_document_info` | 获取文档信息 |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```
|
||||
# 创建合同文档
|
||||
使用 feishu-doc MCP 的 create_and_write_document 工具:
|
||||
- title: "软件订阅服务合同-智慧云彩与妗晨"
|
||||
- content: [Markdown 格式的合同内容]
|
||||
- folder_token: [可选,指定文件夹]
|
||||
```
|
||||
|
||||
### 获取文件夹 Token
|
||||
|
||||
1. 在飞书中打开目标文件夹
|
||||
2. 复制 URL 中的 folder token(格式如:`fldcnXXXXXX`)
|
||||
3. 调用工具时传入 folder_token 参数
|
||||
|
||||
### 飞书输出格式规范
|
||||
|
||||
#### 开票信息表格
|
||||
|
||||
物流合同中的开票信息应使用**表格**格式展示,列宽配置:
|
||||
|
||||
| 配置项 | 值 | 说明 |
|
||||
|--------|-----|------|
|
||||
| 项目列宽 | 120px | 显示"名称"、"纳税人识别号"等标签 |
|
||||
| 内容列宽 | 480px | 显示具体信息 |
|
||||
|
||||
```python
|
||||
# 创建开票信息表格示例
|
||||
from feishu_docx import FeishuDocx
|
||||
|
||||
docx = FeishuDocx()
|
||||
doc = docx.create_document("物流服务合同")
|
||||
|
||||
# 创建表格(4行2列,指定列宽)
|
||||
table = docx.create_table(doc["document_id"], rows=4, cols=2, col_widths=[120, 480])
|
||||
|
||||
# 填充内容
|
||||
docx.fill_table(doc["document_id"], table, [
|
||||
["名称", "重庆妗晨工贸有限公司"],
|
||||
["纳税人识别号", "91500104MA7EJTPA6D"],
|
||||
["地址、电话", "重庆市大渡口区跳磴镇海康路106号1-1 / 15213397998"],
|
||||
["开户行及账号", "中国农业银行重庆大渡口天安支行 31230401040004386"],
|
||||
])
|
||||
```
|
||||
|
||||
#### 签章格式
|
||||
|
||||
推荐使用**独立区块式**签章格式:
|
||||
|
||||
```
|
||||
【甲 方】
|
||||
|
||||
单位名称:重庆妗晨工贸有限公司
|
||||
|
||||
法定代表人(或授权代表): (签字/盖章)
|
||||
|
||||
单位盖章:
|
||||
|
||||
|
||||
|
||||
签订日期: 年 月 日
|
||||
|
||||
─────────────────────────────────────────────────────
|
||||
|
||||
【乙 方】
|
||||
|
||||
单位名称:北京名风新能源科技有限公司
|
||||
|
||||
法定代表人(或授权代表): (签字/盖章)
|
||||
|
||||
单位盖章:
|
||||
|
||||
|
||||
|
||||
签订日期: 年 月 日
|
||||
```
|
||||
|
||||
**特点**:甲乙双方独立区块,留白充足,便于盖章签字。
|
||||
|
||||
#### 物流合同税率
|
||||
|
||||
| 服务类型 | 税率 | 发票类型 |
|
||||
|----------|------|----------|
|
||||
| 交通运输服务 | 9% | 增值税专用发票 |
|
||||
|
||||
#### 默认存储路径
|
||||
|
||||
| 平台 | 路径 |
|
||||
|------|------|
|
||||
| 飞书云文档 | ai-proj/01运营 (folder_token: `C80gfkRnzlonQ5d4AhOcOACDnNg`) |
|
||||
| 思源笔记 | 商务合同笔记本 /物流合同/{甲方简称}-{乙方简称}-{年份} |
|
||||
- 飞书文档集成
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# skills 详细内容
|
||||
|
||||
## 飞书文档集成
|
||||
|
||||
本技能已集成飞书文档,可将合同直接创建到飞书云文档。
|
||||
|
||||
### 权限说明
|
||||
|
||||
**重要**:飞书文档默认对组织内权限是开放的,允许编辑。创建的合同文档在组织内成员均可查看和编辑。如需限制访问权限,请在飞书中手动调整文档权限设置。
|
||||
|
||||
### 可用工具
|
||||
|
||||
| 工具 | 功能 |
|
||||
|------|------|
|
||||
| `create_document` | 创建空白飞书文档 |
|
||||
| `write_document` | 向已有文档写入内容 |
|
||||
| `create_and_write_document` | 创建文档并写入内容(推荐) |
|
||||
| `get_document_info` | 获取文档信息 |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```
|
||||
# 创建合同文档
|
||||
使用 feishu-doc MCP 的 create_and_write_document 工具:
|
||||
- title: "软件订阅服务合同-智慧云彩与妗晨"
|
||||
- content: [Markdown 格式的合同内容]
|
||||
- folder_token: [可选,指定文件夹]
|
||||
```
|
||||
|
||||
### 获取文件夹 Token
|
||||
|
||||
1. 在飞书中打开目标文件夹
|
||||
2. 复制 URL 中的 folder token(格式如:`fldcnXXXXXX`)
|
||||
3. 调用工具时传入 folder_token 参数
|
||||
|
||||
### 飞书输出格式规范
|
||||
|
||||
#### 开票信息表格
|
||||
|
||||
物流合同中的开票信息应使用**表格**格式展示,列宽配置:
|
||||
|
||||
| 配置项 | 值 | 说明 |
|
||||
|--------|-----|------|
|
||||
| 项目列宽 | 120px | 显示"名称"、"纳税人识别号"等标签 |
|
||||
| 内容列宽 | 480px | 显示具体信息 |
|
||||
|
||||
```python
|
||||
# 创建开票信息表格示例
|
||||
from feishu_docx import FeishuDocx
|
||||
|
||||
docx = FeishuDocx()
|
||||
doc = docx.create_document("物流服务合同")
|
||||
|
||||
# 创建表格(4行2列,指定列宽)
|
||||
table = docx.create_table(doc["document_id"], rows=4, cols=2, col_widths=[120, 480])
|
||||
|
||||
# 填充内容
|
||||
docx.fill_table(doc["document_id"], table, [
|
||||
["名称", "重庆妗晨工贸有限公司"],
|
||||
["纳税人识别号", "91500104MA7EJTPA6D"],
|
||||
["地址、电话", "重庆市大渡口区跳磴镇海康路106号1-1 / 15213397998"],
|
||||
["开户行及账号", "中国农业银行重庆大渡口天安支行 31230401040004386"],
|
||||
])
|
||||
```
|
||||
|
||||
#### 签章格式
|
||||
|
||||
推荐使用**独立区块式**签章格式:
|
||||
|
||||
```
|
||||
【甲 方】
|
||||
|
||||
单位名称:重庆妗晨工贸有限公司
|
||||
|
||||
法定代表人(或授权代表): (签字/盖章)
|
||||
|
||||
单位盖章:
|
||||
|
||||
|
||||
|
||||
签订日期: 年 月 日
|
||||
|
||||
─────────────────────────────────────────────────────
|
||||
|
||||
【乙 方】
|
||||
|
||||
单位名称:北京名风新能源科技有限公司
|
||||
|
||||
法定代表人(或授权代表): (签字/盖章)
|
||||
|
||||
单位盖章:
|
||||
|
||||
|
||||
|
||||
签订日期: 年 月 日
|
||||
```
|
||||
|
||||
**特点**:甲乙双方独立区块,留白充足,便于盖章签字。
|
||||
|
||||
#### 物流合同税率
|
||||
|
||||
| 服务类型 | 税率 | 发票类型 |
|
||||
|----------|------|----------|
|
||||
| 交通运输服务 | 9% | 增值税专用发票 |
|
||||
|
||||
#### 默认存储路径
|
||||
|
||||
| 平台 | 路径 |
|
||||
|------|------|
|
||||
| 飞书云文档 | ai-proj/01运营 (folder_token: `C80gfkRnzlonQ5d4AhOcOACDnNg`) |
|
||||
| 思源笔记 | 商务合同笔记本 /物流合同/{甲方简称}-{乙方简称}-{年份} |
|
||||
@@ -4,5 +4,9 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "biz-ops",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: biz-ops
|
||||
description: 商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 商务运营 (biz-ops)
|
||||
|
||||
@@ -4,5 +4,9 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "biz-plan",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: biz-plan
|
||||
description: 商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 商业计划书撰写技能 (biz-plan)
|
||||
|
||||
## 概述
|
||||
|
||||
本技能用于辅助商业计划书的撰写与优化,特别适用于 B2B SaaS、供应链平台、产业互联网等领域。
|
||||
@@ -400,361 +400,9 @@ description: 商业计划书撰写。支持融资BP、战略规划、商业模
|
||||
|
||||
---
|
||||
|
||||
## PPT 设计能力
|
||||
|
||||
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
|
||||
## 详细内容
|
||||
|
||||
### PPT 输出方式
|
||||
以下章节已移到 [references/details.md](references/details.md):
|
||||
|
||||
当用户要求生成 PPT 时,使用以下方式:
|
||||
|
||||
1. **HTML 演示文稿**(推荐)
|
||||
- 生成单个 HTML 文件
|
||||
- 支持全屏演示(按 F 键)
|
||||
- 支持键盘翻页(← →)
|
||||
- 可直接打印为 PDF
|
||||
- 响应式设计,适配各种屏幕
|
||||
|
||||
2. **Markdown 演示脚本**
|
||||
- 输出每页内容的文字稿
|
||||
- 用户可自行复制到 PPT 软件
|
||||
|
||||
### HTML PPT 模板
|
||||
|
||||
生成 PPT 时使用以下 HTML 模板结构:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>商业计划书 - [公司名称]</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: #1a1a2e;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.slide {
|
||||
display: none;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
padding: 60px 80px;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slide.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 封面样式 */
|
||||
.slide.cover {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.slide.cover h1 {
|
||||
font-size: 72px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.slide.cover .slogan {
|
||||
font-size: 28px;
|
||||
color: #a0aec0;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.slide.cover .info {
|
||||
font-size: 18px;
|
||||
color: #718096;
|
||||
}
|
||||
|
||||
/* 内容页样式 */
|
||||
.slide h2 {
|
||||
font-size: 48px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 40px;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.slide h3 {
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 24px;
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
.slide p, .slide li {
|
||||
font-size: 24px;
|
||||
line-height: 1.8;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.slide ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.slide ul li {
|
||||
padding: 12px 0;
|
||||
padding-left: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slide ul li::before {
|
||||
content: "▸";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
/* 数据卡片 */
|
||||
.metrics {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
flex: 1;
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
border: 1px solid rgba(102, 126, 234, 0.3);
|
||||
border-radius: 16px;
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.metric-card .value {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.metric-card .label {
|
||||
font-size: 18px;
|
||||
color: #a0aec0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 两栏布局 */
|
||||
.two-columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 60px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
/* 流程图 */
|
||||
.flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.flow-item {
|
||||
background: rgba(102, 126, 234, 0.15);
|
||||
border-radius: 12px;
|
||||
padding: 24px 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
color: #667eea;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
/* 页码 */
|
||||
.page-number {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
right: 40px;
|
||||
font-size: 16px;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
.progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
transition: width 0.3s;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* 导航提示 */
|
||||
.nav-hint {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 14px;
|
||||
color: #4a5568;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="progress" id="progress"></div>
|
||||
|
||||
<!-- Slide 1: 封面 -->
|
||||
<div class="slide cover active">
|
||||
<h1>[公司名称]</h1>
|
||||
<p class="slogan">[一句话 Slogan]</p>
|
||||
<p class="info">[日期] · 商业计划书</p>
|
||||
<span class="page-number">1 / N</span>
|
||||
</div>
|
||||
|
||||
<!-- Slide 2: 公司定位 -->
|
||||
<div class="slide">
|
||||
<h2>公司定位</h2>
|
||||
<h3>[一句话定位]</h3>
|
||||
<div class="metrics">
|
||||
<div class="metric-card">
|
||||
<div class="value">[X]年</div>
|
||||
<div class="label">深耕行业</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="value">[X]家</div>
|
||||
<div class="label">头部客户</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="value">[X]轮</div>
|
||||
<div class="label">完成融资</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="page-number">2 / N</span>
|
||||
</div>
|
||||
|
||||
<!-- 更多 slides... -->
|
||||
|
||||
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
|
||||
|
||||
<script>
|
||||
const slides = document.querySelectorAll('.slide');
|
||||
const progress = document.getElementById('progress');
|
||||
let current = 0;
|
||||
|
||||
function showSlide(n) {
|
||||
slides[current].classList.remove('active');
|
||||
current = (n + slides.length) % slides.length;
|
||||
slides[current].classList.add('active');
|
||||
progress.style.width = ((current + 1) / slides.length * 100) + '%';
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
|
||||
if (e.key === 'ArrowLeft') showSlide(current - 1);
|
||||
if (e.key === 'f' || e.key === 'F') {
|
||||
document.documentElement.requestFullscreen?.();
|
||||
}
|
||||
});
|
||||
|
||||
// 触摸滑动支持
|
||||
let touchStartX = 0;
|
||||
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
|
||||
document.addEventListener('touchend', e => {
|
||||
const diff = e.changedTouches[0].clientX - touchStartX;
|
||||
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### BP PPT 标准页面结构
|
||||
|
||||
生成商业计划书 PPT 时,按以下结构组织:
|
||||
|
||||
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|
||||
|------|----------|----------|----------|
|
||||
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
|
||||
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
|
||||
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
|
||||
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
|
||||
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
|
||||
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
|
||||
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
|
||||
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
|
||||
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
|
||||
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
|
||||
| 11 | 里程碑 | 发展历程 | 时间线 |
|
||||
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
|
||||
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
|
||||
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
|
||||
|
||||
### PPT 设计原则
|
||||
|
||||
1. **一页一重点**:每页只传达一个核心信息
|
||||
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
|
||||
3. **数据可视化**:用数字卡片、图表代替文字描述
|
||||
4. **留白充足**:内容不要铺满,保持呼吸感
|
||||
5. **配色统一**:使用 2-3 种主色,保持一致性
|
||||
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
|
||||
|
||||
### 配色方案
|
||||
|
||||
**深色科技风**(推荐):
|
||||
- 背景:#1a1a2e → #16213e(渐变)
|
||||
- 主色:#667eea(蓝紫色)
|
||||
- 辅助:#764ba2(紫色)
|
||||
- 文字:#e2e8f0(浅灰)
|
||||
- 次要文字:#a0aec0(中灰)
|
||||
|
||||
**浅色商务风**:
|
||||
- 背景:#ffffff
|
||||
- 主色:#2196F3(蓝色)
|
||||
- 辅助:#1976D2(深蓝)
|
||||
- 文字:#212121(深灰)
|
||||
- 次要文字:#616161(中灰)
|
||||
|
||||
### 使用示例
|
||||
|
||||
用户请求:
|
||||
> 帮我生成商业计划书 PPT
|
||||
|
||||
输出方式:
|
||||
1. 生成完整的 HTML 文件
|
||||
2. 保存到用户指定位置(如桌面、项目目录)
|
||||
3. 用户用浏览器打开即可演示
|
||||
|
||||
```bash
|
||||
# 打开 PPT
|
||||
open ~/Desktop/bp-presentation.html
|
||||
|
||||
# 或在浏览器中打开后按 F 进入全屏模式
|
||||
```
|
||||
|
||||
### 导出为 PDF
|
||||
|
||||
HTML PPT 可通过浏览器打印功能导出为 PDF:
|
||||
1. 在浏览器中打开 HTML 文件
|
||||
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
|
||||
3. 选择"另存为 PDF"
|
||||
4. 设置页面大小为横向 A4 或 16:9
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **内容优先**:先确定 BP 内容,再生成 PPT
|
||||
2. **数据占位**:敏感数据可用 [X] 占位
|
||||
3. **迭代优化**:可分页生成,逐步完善
|
||||
4. **本地演示**:HTML 文件完全离线可用,无需网络
|
||||
- PPT 设计能力
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
# skills 详细内容
|
||||
|
||||
## PPT 设计能力
|
||||
|
||||
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
|
||||
|
||||
### PPT 输出方式
|
||||
|
||||
当用户要求生成 PPT 时,使用以下方式:
|
||||
|
||||
1. **HTML 演示文稿**(推荐)
|
||||
- 生成单个 HTML 文件
|
||||
- 支持全屏演示(按 F 键)
|
||||
- 支持键盘翻页(← →)
|
||||
- 可直接打印为 PDF
|
||||
- 响应式设计,适配各种屏幕
|
||||
|
||||
2. **Markdown 演示脚本**
|
||||
- 输出每页内容的文字稿
|
||||
- 用户可自行复制到 PPT 软件
|
||||
|
||||
### HTML PPT 模板
|
||||
|
||||
生成 PPT 时使用以下 HTML 模板结构:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>商业计划书 - [公司名称]</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
background: #1a1a2e;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.slide {
|
||||
display: none;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
padding: 60px 80px;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slide.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 封面样式 */
|
||||
.slide.cover {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.slide.cover h1 {
|
||||
font-size: 72px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.slide.cover .slogan {
|
||||
font-size: 28px;
|
||||
color: #a0aec0;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.slide.cover .info {
|
||||
font-size: 18px;
|
||||
color: #718096;
|
||||
}
|
||||
|
||||
/* 内容页样式 */
|
||||
.slide h2 {
|
||||
font-size: 48px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 40px;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.slide h3 {
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 24px;
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
.slide p, .slide li {
|
||||
font-size: 24px;
|
||||
line-height: 1.8;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.slide ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.slide ul li {
|
||||
padding: 12px 0;
|
||||
padding-left: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slide ul li::before {
|
||||
content: "▸";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
/* 数据卡片 */
|
||||
.metrics {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
flex: 1;
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
border: 1px solid rgba(102, 126, 234, 0.3);
|
||||
border-radius: 16px;
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.metric-card .value {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.metric-card .label {
|
||||
font-size: 18px;
|
||||
color: #a0aec0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 两栏布局 */
|
||||
.two-columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 60px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
/* 流程图 */
|
||||
.flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.flow-item {
|
||||
background: rgba(102, 126, 234, 0.15);
|
||||
border-radius: 12px;
|
||||
padding: 24px 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
color: #667eea;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
/* 页码 */
|
||||
.page-number {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
right: 40px;
|
||||
font-size: 16px;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
.progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
transition: width 0.3s;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* 导航提示 */
|
||||
.nav-hint {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 14px;
|
||||
color: #4a5568;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="progress" id="progress"></div>
|
||||
|
||||
<!-- Slide 1: 封面 -->
|
||||
<div class="slide cover active">
|
||||
<h1>[公司名称]</h1>
|
||||
<p class="slogan">[一句话 Slogan]</p>
|
||||
<p class="info">[日期] · 商业计划书</p>
|
||||
<span class="page-number">1 / N</span>
|
||||
</div>
|
||||
|
||||
<!-- Slide 2: 公司定位 -->
|
||||
<div class="slide">
|
||||
<h2>公司定位</h2>
|
||||
<h3>[一句话定位]</h3>
|
||||
<div class="metrics">
|
||||
<div class="metric-card">
|
||||
<div class="value">[X]年</div>
|
||||
<div class="label">深耕行业</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="value">[X]家</div>
|
||||
<div class="label">头部客户</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="value">[X]轮</div>
|
||||
<div class="label">完成融资</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="page-number">2 / N</span>
|
||||
</div>
|
||||
|
||||
<!-- 更多 slides... -->
|
||||
|
||||
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
|
||||
|
||||
<script>
|
||||
const slides = document.querySelectorAll('.slide');
|
||||
const progress = document.getElementById('progress');
|
||||
let current = 0;
|
||||
|
||||
function showSlide(n) {
|
||||
slides[current].classList.remove('active');
|
||||
current = (n + slides.length) % slides.length;
|
||||
slides[current].classList.add('active');
|
||||
progress.style.width = ((current + 1) / slides.length * 100) + '%';
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
|
||||
if (e.key === 'ArrowLeft') showSlide(current - 1);
|
||||
if (e.key === 'f' || e.key === 'F') {
|
||||
document.documentElement.requestFullscreen?.();
|
||||
}
|
||||
});
|
||||
|
||||
// 触摸滑动支持
|
||||
let touchStartX = 0;
|
||||
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
|
||||
document.addEventListener('touchend', e => {
|
||||
const diff = e.changedTouches[0].clientX - touchStartX;
|
||||
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### BP PPT 标准页面结构
|
||||
|
||||
生成商业计划书 PPT 时,按以下结构组织:
|
||||
|
||||
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|
||||
|------|----------|----------|----------|
|
||||
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
|
||||
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
|
||||
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
|
||||
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
|
||||
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
|
||||
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
|
||||
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
|
||||
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
|
||||
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
|
||||
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
|
||||
| 11 | 里程碑 | 发展历程 | 时间线 |
|
||||
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
|
||||
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
|
||||
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
|
||||
|
||||
### PPT 设计原则
|
||||
|
||||
1. **一页一重点**:每页只传达一个核心信息
|
||||
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
|
||||
3. **数据可视化**:用数字卡片、图表代替文字描述
|
||||
4. **留白充足**:内容不要铺满,保持呼吸感
|
||||
5. **配色统一**:使用 2-3 种主色,保持一致性
|
||||
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
|
||||
|
||||
### 配色方案
|
||||
|
||||
**深色科技风**(推荐):
|
||||
- 背景:#1a1a2e → #16213e(渐变)
|
||||
- 主色:#667eea(蓝紫色)
|
||||
- 辅助:#764ba2(紫色)
|
||||
- 文字:#e2e8f0(浅灰)
|
||||
- 次要文字:#a0aec0(中灰)
|
||||
|
||||
**浅色商务风**:
|
||||
- 背景:#ffffff
|
||||
- 主色:#2196F3(蓝色)
|
||||
- 辅助:#1976D2(深蓝)
|
||||
- 文字:#212121(深灰)
|
||||
- 次要文字:#616161(中灰)
|
||||
|
||||
### 使用示例
|
||||
|
||||
用户请求:
|
||||
> 帮我生成商业计划书 PPT
|
||||
|
||||
输出方式:
|
||||
1. 生成完整的 HTML 文件
|
||||
2. 保存到用户指定位置(如桌面、项目目录)
|
||||
3. 用户用浏览器打开即可演示
|
||||
|
||||
```bash
|
||||
# 打开 PPT
|
||||
open ~/Desktop/bp-presentation.html
|
||||
|
||||
# 或在浏览器中打开后按 F 进入全屏模式
|
||||
```
|
||||
|
||||
### 导出为 PDF
|
||||
|
||||
HTML PPT 可通过浏览器打印功能导出为 PDF:
|
||||
1. 在浏览器中打开 HTML 文件
|
||||
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
|
||||
3. 选择"另存为 PDF"
|
||||
4. 设置页面大小为横向 A4 或 16:9
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **内容优先**:先确定 BP 内容,再生成 PPT
|
||||
2. **数据占位**:敏感数据可用 [X] 占位
|
||||
3. **迭代优化**:可分页生成,逐步完善
|
||||
4. **本地演示**:HTML 文件完全离线可用,无需网络
|
||||
@@ -4,5 +4,9 @@
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "finance",
|
||||
"install_type": "skill",
|
||||
"dir_category": "biz",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: finance
|
||||
description: 财务对账技能。支持银行对账、应收对账、应付对账、内部往来对账等。通过自然语言实现账单导入、差异分析、对账报告生成。当用户提到对账、银行流水、应收应付、账单核对、财务核销相关任务时自动激活。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# 财务对账技能
|
||||
|
||||
## 功能概述
|
||||
|
||||
- **银行对账**: 银行流水 vs 系统收付款记录匹配
|
||||
@@ -478,457 +478,6 @@ def export_reconciliation_to_excel(
|
||||
|
||||
---
|
||||
|
||||
## 常见对账场景
|
||||
|
||||
### 场景1: 月末银行对账
|
||||
|
||||
```python
|
||||
# 1. 加载数据
|
||||
bank_df = load_bank_statement('银行流水_202601.xlsx')
|
||||
system_df = load_system_records('收付款记录_202601.xlsx')
|
||||
|
||||
# 2. 执行对账
|
||||
result = reconcile(bank_df, system_df)
|
||||
|
||||
# 3. 查看汇总
|
||||
print(f"已匹配: {result['summary']['matched_count']} 笔")
|
||||
print(f"银行未达: {result['summary']['bank_only_count']} 笔, 金额 {result['summary']['bank_only_amount']:,.2f}")
|
||||
print(f"企业未达: {result['summary']['system_only_count']} 笔, 金额 {result['summary']['system_only_amount']:,.2f}")
|
||||
|
||||
# 4. 生成调节表
|
||||
report = generate_bank_reconciliation_report(
|
||||
bank_balance=150000.00, # 银行对账单余额
|
||||
book_balance=145000.00, # 企业账面余额
|
||||
bank_only=result['bank_only'],
|
||||
system_only=result['system_only'],
|
||||
as_of_date='2026-01-31'
|
||||
)
|
||||
print(report)
|
||||
|
||||
# 5. 导出 Excel
|
||||
export_reconciliation_to_excel(result, '银行对账_202601.xlsx', '2026-01-31')
|
||||
```
|
||||
|
||||
### 场景2: 客户对账
|
||||
|
||||
```python
|
||||
# 加载客户交易明细
|
||||
ar_transactions = pd.read_excel('应收明细.xlsx')
|
||||
|
||||
# 筛选特定客户
|
||||
customer_data = ar_transactions[ar_transactions['customer'] == '深圳XX公司']
|
||||
|
||||
# 生成对账单
|
||||
statement = generate_ar_statement(
|
||||
customer_name='深圳XX公司',
|
||||
transactions=customer_data,
|
||||
as_of_date='2026-01-31'
|
||||
)
|
||||
print(statement)
|
||||
```
|
||||
|
||||
### 场景3: 账龄分析
|
||||
|
||||
```python
|
||||
# 加载未收款数据
|
||||
receivables = pd.read_excel('应收账款.xlsx')
|
||||
|
||||
# 账龄分析
|
||||
aging = aging_analysis(
|
||||
receivables=receivables,
|
||||
as_of_date='2026-01-31',
|
||||
aging_buckets=[30, 60, 90, 180, 365]
|
||||
)
|
||||
print(aging)
|
||||
|
||||
# 导出
|
||||
aging.to_excel('账龄分析_202601.xlsx')
|
||||
```
|
||||
|
||||
### 场景4: 扫描单据OCR识别与对账匹配(诉讼证据核对)
|
||||
|
||||
适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。
|
||||
|
||||
```python
|
||||
from pdf2image import convert_from_path
|
||||
import pytesseract
|
||||
import pandas as pd
|
||||
import re
|
||||
|
||||
def ocr_pdf_extract_orders(pdf_path: str, dpi: int = 150) -> pd.DataFrame:
|
||||
"""
|
||||
从 PDF 扫描件中 OCR 提取订单信息
|
||||
|
||||
Args:
|
||||
pdf_path: PDF 文件路径
|
||||
dpi: 图像分辨率,越高识别越准但速度越慢
|
||||
|
||||
Returns:
|
||||
DataFrame 包含 order_no, page, tc_no 等字段
|
||||
"""
|
||||
print(f"正在转换 PDF...")
|
||||
images = convert_from_path(pdf_path, dpi=dpi)
|
||||
print(f"共 {len(images)} 页,开始 OCR 识别...")
|
||||
|
||||
all_records = []
|
||||
for i, img in enumerate(images, 1):
|
||||
if i % 10 == 0:
|
||||
print(f" 处理进度: {i}/{len(images)}")
|
||||
|
||||
text = pytesseract.image_to_string(img, lang='chi_sim+eng')
|
||||
|
||||
# 提取多种订单号格式(根据实际业务调整)
|
||||
hm_orders = re.findall(r'(HM\d{12,15})(?:-\d+)?', text) # 华住订单
|
||||
hpo_orders = re.findall(r'(HPO-\d{8}-\d+)', text) # 华住采购订单
|
||||
fy_orders = re.findall(r'(FY\d{9,})', text) # 凤悦订单
|
||||
phgco_orders = re.findall(r'(PHGCO\d{9,})', text) # 锦江订单
|
||||
tc_orders = re.findall(r'(TC\d{8,})', text) # 运单号
|
||||
|
||||
# 提取日期
|
||||
dates = re.findall(r'(\d{4}-\d{2}-\d{2})', text)
|
||||
|
||||
all_orders = hm_orders + hpo_orders + fy_orders + phgco_orders
|
||||
for order in all_orders:
|
||||
all_records.append({
|
||||
'order_no': order,
|
||||
'page': i,
|
||||
'tc_no': tc_orders[0] if tc_orders else None,
|
||||
'date': dates[0] if dates else None,
|
||||
'raw_text': text[:500] # 保留部分原文用于核查
|
||||
})
|
||||
|
||||
df = pd.DataFrame(all_records)
|
||||
df = df.drop_duplicates(subset=['order_no'])
|
||||
print(f"提取完成,共 {len(df)} 条唯一订单")
|
||||
return df
|
||||
|
||||
|
||||
def extract_base_order_no(order_no: str) -> str:
|
||||
"""
|
||||
提取基础订单号(移除后缀如 -1, -2)
|
||||
|
||||
例如: HM202505227320-1 -> HM202505227320
|
||||
"""
|
||||
if pd.isna(order_no):
|
||||
return None
|
||||
order_str = str(order_no)
|
||||
match = re.match(r'(HM\d{12,15}|HPO-\d{8}-\d+|FY\d{9,}|PHGCO\d{9,})', order_str)
|
||||
return match.group(1) if match else order_str
|
||||
|
||||
|
||||
def reconcile_scanned_with_excel(
|
||||
scanned_df: pd.DataFrame,
|
||||
excel_path: str,
|
||||
main_sheet: str,
|
||||
pending_sheet: str = None,
|
||||
order_column: str = '华住/锦江订单号'
|
||||
) -> dict:
|
||||
"""
|
||||
扫描单据与 Excel 对账单匹配
|
||||
|
||||
Args:
|
||||
scanned_df: OCR 提取的订单 DataFrame
|
||||
excel_path: Excel 对账单路径
|
||||
main_sheet: 主数据 Sheet 名称
|
||||
pending_sheet: 待结算/次月结 Sheet 名称(可选)
|
||||
order_column: 订单号列名
|
||||
|
||||
Returns:
|
||||
{
|
||||
'matched_main': 在主表中匹配的订单,
|
||||
'matched_pending': 在待结算表中匹配的订单,
|
||||
'not_found': 未找到的订单,
|
||||
'summary': 汇总统计
|
||||
}
|
||||
"""
|
||||
# 加载主表
|
||||
df_main = pd.read_excel(excel_path, sheet_name=main_sheet)
|
||||
# 处理表头(如果第一行是表头)
|
||||
if df_main.iloc[0].astype(str).str.contains(order_column).any():
|
||||
df_main.columns = df_main.iloc[0]
|
||||
df_main = df_main.iloc[1:].reset_index(drop=True)
|
||||
|
||||
# 提取基础订单号
|
||||
main_orders = set(df_main[order_column].apply(extract_base_order_no).dropna())
|
||||
|
||||
# 加载待结算表(如有)
|
||||
pending_orders = set()
|
||||
df_pending = None
|
||||
if pending_sheet:
|
||||
try:
|
||||
df_pending = pd.read_excel(excel_path, sheet_name=pending_sheet)
|
||||
pending_orders = set(df_pending[order_column].apply(extract_base_order_no).dropna())
|
||||
except:
|
||||
pass
|
||||
|
||||
all_excel_orders = main_orders | pending_orders
|
||||
|
||||
# 扫描件订单号
|
||||
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
||||
|
||||
# 匹配分析
|
||||
matched_main = scan_orders & main_orders
|
||||
matched_pending = scan_orders & pending_orders
|
||||
not_found = scan_orders - all_excel_orders
|
||||
|
||||
# 汇总
|
||||
summary = {
|
||||
'scan_total': len(scan_orders),
|
||||
'matched_main_count': len(matched_main),
|
||||
'matched_pending_count': len(matched_pending),
|
||||
'not_found_count': len(not_found),
|
||||
'match_rate': (len(matched_main) + len(matched_pending)) / len(scan_orders) * 100 if scan_orders else 0
|
||||
}
|
||||
|
||||
return {
|
||||
'matched_main': matched_main,
|
||||
'matched_pending': matched_pending,
|
||||
'not_found': not_found,
|
||||
'summary': summary,
|
||||
'df_main': df_main,
|
||||
'df_pending': df_pending
|
||||
}
|
||||
|
||||
|
||||
def generate_litigation_reconciliation_report(
|
||||
result: dict,
|
||||
scanned_df: pd.DataFrame,
|
||||
output_path: str
|
||||
):
|
||||
"""
|
||||
生成诉讼对账分析报告
|
||||
"""
|
||||
summary = result['summary']
|
||||
|
||||
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
|
||||
# Sheet1: 汇总
|
||||
summary_df = pd.DataFrame({
|
||||
'项目': [
|
||||
'扫描件订单数',
|
||||
'在主对账单中找到',
|
||||
'在待结算表中找到',
|
||||
'完全未找到',
|
||||
'匹配率'
|
||||
],
|
||||
'数值': [
|
||||
summary['scan_total'],
|
||||
summary['matched_main_count'],
|
||||
summary['matched_pending_count'],
|
||||
summary['not_found_count'],
|
||||
f"{summary['match_rate']:.1f}%"
|
||||
]
|
||||
})
|
||||
summary_df.to_excel(writer, sheet_name='对账汇总', index=False)
|
||||
|
||||
# Sheet2: 未找到的订单
|
||||
if result['not_found']:
|
||||
not_found_df = scanned_df[
|
||||
scanned_df['order_no'].apply(extract_base_order_no).isin(result['not_found'])
|
||||
][['order_no', 'page', 'tc_no', 'date']]
|
||||
not_found_df.columns = ['订单号', 'PDF页码', '运单号', '日期']
|
||||
not_found_df.to_excel(writer, sheet_name='未找到订单', index=False)
|
||||
|
||||
# Sheet3: 待结算订单详情
|
||||
if result['matched_pending'] and result['df_pending'] is not None:
|
||||
pending_detail = result['df_pending'][
|
||||
result['df_pending']['华住/锦江订单号'].apply(extract_base_order_no).isin(result['matched_pending'])
|
||||
]
|
||||
pending_detail.to_excel(writer, sheet_name='待结算订单', index=False)
|
||||
|
||||
print(f"报告已保存: {output_path}")
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```python
|
||||
# 1. 从 PDF 扫描件提取订单
|
||||
pdf_path = "承运商证据_6月业务.pdf"
|
||||
scanned_df = ocr_pdf_extract_orders(pdf_path, dpi=150)
|
||||
|
||||
# 2. 与 Excel 对账单匹配
|
||||
result = reconcile_scanned_with_excel(
|
||||
scanned_df=scanned_df,
|
||||
excel_path="对账单_2025年6月.xlsx",
|
||||
main_sheet="2025.6",
|
||||
pending_sheet="次月结",
|
||||
order_column="华住/锦江订单号"
|
||||
)
|
||||
|
||||
# 3. 查看匹配结果
|
||||
print(f"匹配率: {result['summary']['match_rate']:.1f}%")
|
||||
print(f"未找到: {result['summary']['not_found_count']} 条")
|
||||
|
||||
if result['not_found']:
|
||||
print("\n未找到的订单:")
|
||||
for order in result['not_found']:
|
||||
print(f" {order}")
|
||||
|
||||
# 4. 生成报告
|
||||
generate_litigation_reconciliation_report(
|
||||
result=result,
|
||||
scanned_df=scanned_df,
|
||||
output_path="诉讼对账分析报告.xlsx"
|
||||
)
|
||||
```
|
||||
|
||||
#### OCR 识别注意事项
|
||||
|
||||
1. **环境依赖**:
|
||||
```bash
|
||||
# macOS
|
||||
brew install tesseract tesseract-lang poppler
|
||||
pip install pdf2image pytesseract pandas openpyxl
|
||||
```
|
||||
|
||||
2. **识别精度**:
|
||||
- DPI 150 适合快速扫描,DPI 200-250 适合精确识别
|
||||
- 中文识别需要 `chi_sim` 语言包
|
||||
|
||||
3. **订单号格式差异**:
|
||||
- PDF 中可能是 `HM202505227320`
|
||||
- Excel 中可能是 `HM202505227320-1`、`HM202505227320-2`(带后缀)
|
||||
- 匹配时应使用基础订单号
|
||||
|
||||
4. **常见问题**:
|
||||
- 空白页(签收单背面)会被自动跳过
|
||||
- OCR 可能误识别字符(如 5→S, 0→O),建议人工抽查
|
||||
- 大 PDF 处理较慢,可先用低 DPI 预览
|
||||
|
||||
5. **"次月结"/"待结算" 订单**:
|
||||
- 这类订单通常标记为"没有报价"、"未建单"等
|
||||
- 虽然在系统中有记录,但可能未计入正式结算金额
|
||||
- 诉讼中需特别关注这类订单的状态
|
||||
|
||||
6. **多承运商订单归属校验(重要风险点)**:
|
||||
- 承运商可能将其他公司的订单混入证据,企图多收运费
|
||||
- 必须检查PDF订单是否出现在其他承运商的对账单中
|
||||
- 如果订单同时出现在多个承运商对账单中,则为异常订单
|
||||
|
||||
#### 多承运商订单归属校验
|
||||
|
||||
```python
|
||||
def check_order_ownership(
|
||||
scanned_df: pd.DataFrame,
|
||||
my_company_excel: str,
|
||||
other_carrier_excels: list[str],
|
||||
order_column: str = '华住/锦江订单号'
|
||||
) -> dict:
|
||||
"""
|
||||
检查订单归属,识别是否有其他承运商的订单被混入
|
||||
|
||||
Args:
|
||||
scanned_df: OCR 提取的订单 DataFrame
|
||||
my_company_excel: 我司对账单路径
|
||||
other_carrier_excels: 其他承运商对账单路径列表
|
||||
order_column: 订单号列名
|
||||
|
||||
Returns:
|
||||
{
|
||||
'my_orders': 属于我司的订单,
|
||||
'other_carrier_orders': 属于其他承运商的订单(异常),
|
||||
'disputed_orders': 同时出现在多处的争议订单,
|
||||
'summary': 汇总
|
||||
}
|
||||
"""
|
||||
# 提取PDF订单
|
||||
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
||||
|
||||
# 加载我司对账单订单
|
||||
my_orders = set()
|
||||
try:
|
||||
xl = pd.ExcelFile(my_company_excel)
|
||||
for sheet in xl.sheet_names:
|
||||
df = pd.read_excel(my_company_excel, sheet_name=sheet)
|
||||
for col in df.columns:
|
||||
if '订单' in str(col):
|
||||
orders = df[col].apply(extract_base_order_no).dropna()
|
||||
my_orders.update(orders)
|
||||
except Exception as e:
|
||||
print(f"加载我司对账单失败: {e}")
|
||||
|
||||
# 加载其他承运商对账单订单
|
||||
other_orders = {} # {carrier_name: set of orders}
|
||||
for excel_path in other_carrier_excels:
|
||||
carrier_name = excel_path.split('/')[-1]
|
||||
carrier_orders = set()
|
||||
try:
|
||||
xl = pd.ExcelFile(excel_path)
|
||||
for sheet in xl.sheet_names:
|
||||
df = pd.read_excel(excel_path, sheet_name=sheet)
|
||||
for col in df.columns:
|
||||
if '订单' in str(col):
|
||||
orders = df[col].apply(extract_base_order_no).dropna()
|
||||
carrier_orders.update(orders)
|
||||
other_orders[carrier_name] = carrier_orders
|
||||
except:
|
||||
pass
|
||||
|
||||
# 分析订单归属
|
||||
all_other_orders = set()
|
||||
for orders in other_orders.values():
|
||||
all_other_orders.update(orders)
|
||||
|
||||
# 分类
|
||||
only_mine = scan_orders & my_orders - all_other_orders # 仅在我司
|
||||
only_others = scan_orders & all_other_orders - my_orders # 仅在其他承运商(异常!)
|
||||
in_both = scan_orders & my_orders & all_other_orders # 同时出现(争议)
|
||||
nowhere = scan_orders - my_orders - all_other_orders # 哪里都没有
|
||||
|
||||
# 识别异常订单属于哪个承运商
|
||||
abnormal_details = []
|
||||
for order in only_others:
|
||||
for carrier, orders in other_orders.items():
|
||||
if order in orders:
|
||||
page = scanned_df[scanned_df['order_no'].apply(extract_base_order_no) == order]['page'].values
|
||||
abnormal_details.append({
|
||||
'order_no': order,
|
||||
'belongs_to': carrier,
|
||||
'pdf_page': page[0] if len(page) > 0 else None
|
||||
})
|
||||
|
||||
summary = {
|
||||
'scan_total': len(scan_orders),
|
||||
'my_orders_count': len(only_mine),
|
||||
'other_carrier_count': len(only_others), # 异常订单数
|
||||
'disputed_count': len(in_both),
|
||||
'not_found_count': len(nowhere)
|
||||
}
|
||||
|
||||
return {
|
||||
'my_orders': only_mine,
|
||||
'other_carrier_orders': only_others,
|
||||
'abnormal_details': abnormal_details,
|
||||
'disputed_orders': in_both,
|
||||
'not_found': nowhere,
|
||||
'summary': summary
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例(多承运商校验)
|
||||
|
||||
```python
|
||||
# 检查订单归属
|
||||
result = check_order_ownership(
|
||||
scanned_df=scanned_df,
|
||||
my_company_excel="智慧云彩对账单_2025年6月.xlsx",
|
||||
other_carrier_excels=[
|
||||
"友利速运对账单_2025年6月.xlsx",
|
||||
"其他承运商对账单.xlsx"
|
||||
]
|
||||
)
|
||||
|
||||
# 检查是否有异常订单
|
||||
if result['summary']['other_carrier_count'] > 0:
|
||||
print(f"⚠️ 发现 {result['summary']['other_carrier_count']} 个异常订单!")
|
||||
print("这些订单属于其他承运商,不应由我司支付:")
|
||||
for item in result['abnormal_details']:
|
||||
print(f" {item['order_no']} -> 属于 {item['belongs_to']} (PDF第{item['pdf_page']}页)")
|
||||
else:
|
||||
print("✓ 未发现异常订单,所有订单归属正确")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **数据格式统一**: 确保日期、金额格式一致后再对账
|
||||
@@ -946,3 +495,9 @@ else:
|
||||
- **data-excel**: 处理复杂的 Excel 数据转换
|
||||
- **feishu**: 将对账报告保存到飞书云文档
|
||||
- **siyuan**: 记录对账工作日志到思源笔记
|
||||
|
||||
## 详细内容
|
||||
|
||||
以下章节已移到 [references/details.md](references/details.md):
|
||||
|
||||
- 常见对账场景
|
||||
|
||||
@@ -0,0 +1,453 @@
|
||||
# skills 详细内容
|
||||
|
||||
## 常见对账场景
|
||||
|
||||
### 场景1: 月末银行对账
|
||||
|
||||
```python
|
||||
# 1. 加载数据
|
||||
bank_df = load_bank_statement('银行流水_202601.xlsx')
|
||||
system_df = load_system_records('收付款记录_202601.xlsx')
|
||||
|
||||
# 2. 执行对账
|
||||
result = reconcile(bank_df, system_df)
|
||||
|
||||
# 3. 查看汇总
|
||||
print(f"已匹配: {result['summary']['matched_count']} 笔")
|
||||
print(f"银行未达: {result['summary']['bank_only_count']} 笔, 金额 {result['summary']['bank_only_amount']:,.2f}")
|
||||
print(f"企业未达: {result['summary']['system_only_count']} 笔, 金额 {result['summary']['system_only_amount']:,.2f}")
|
||||
|
||||
# 4. 生成调节表
|
||||
report = generate_bank_reconciliation_report(
|
||||
bank_balance=150000.00, # 银行对账单余额
|
||||
book_balance=145000.00, # 企业账面余额
|
||||
bank_only=result['bank_only'],
|
||||
system_only=result['system_only'],
|
||||
as_of_date='2026-01-31'
|
||||
)
|
||||
print(report)
|
||||
|
||||
# 5. 导出 Excel
|
||||
export_reconciliation_to_excel(result, '银行对账_202601.xlsx', '2026-01-31')
|
||||
```
|
||||
|
||||
### 场景2: 客户对账
|
||||
|
||||
```python
|
||||
# 加载客户交易明细
|
||||
ar_transactions = pd.read_excel('应收明细.xlsx')
|
||||
|
||||
# 筛选特定客户
|
||||
customer_data = ar_transactions[ar_transactions['customer'] == '深圳XX公司']
|
||||
|
||||
# 生成对账单
|
||||
statement = generate_ar_statement(
|
||||
customer_name='深圳XX公司',
|
||||
transactions=customer_data,
|
||||
as_of_date='2026-01-31'
|
||||
)
|
||||
print(statement)
|
||||
```
|
||||
|
||||
### 场景3: 账龄分析
|
||||
|
||||
```python
|
||||
# 加载未收款数据
|
||||
receivables = pd.read_excel('应收账款.xlsx')
|
||||
|
||||
# 账龄分析
|
||||
aging = aging_analysis(
|
||||
receivables=receivables,
|
||||
as_of_date='2026-01-31',
|
||||
aging_buckets=[30, 60, 90, 180, 365]
|
||||
)
|
||||
print(aging)
|
||||
|
||||
# 导出
|
||||
aging.to_excel('账龄分析_202601.xlsx')
|
||||
```
|
||||
|
||||
### 场景4: 扫描单据OCR识别与对账匹配(诉讼证据核对)
|
||||
|
||||
适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。
|
||||
|
||||
```python
|
||||
from pdf2image import convert_from_path
|
||||
import pytesseract
|
||||
import pandas as pd
|
||||
import re
|
||||
|
||||
def ocr_pdf_extract_orders(pdf_path: str, dpi: int = 150) -> pd.DataFrame:
|
||||
"""
|
||||
从 PDF 扫描件中 OCR 提取订单信息
|
||||
|
||||
Args:
|
||||
pdf_path: PDF 文件路径
|
||||
dpi: 图像分辨率,越高识别越准但速度越慢
|
||||
|
||||
Returns:
|
||||
DataFrame 包含 order_no, page, tc_no 等字段
|
||||
"""
|
||||
print(f"正在转换 PDF...")
|
||||
images = convert_from_path(pdf_path, dpi=dpi)
|
||||
print(f"共 {len(images)} 页,开始 OCR 识别...")
|
||||
|
||||
all_records = []
|
||||
for i, img in enumerate(images, 1):
|
||||
if i % 10 == 0:
|
||||
print(f" 处理进度: {i}/{len(images)}")
|
||||
|
||||
text = pytesseract.image_to_string(img, lang='chi_sim+eng')
|
||||
|
||||
# 提取多种订单号格式(根据实际业务调整)
|
||||
hm_orders = re.findall(r'(HM\d{12,15})(?:-\d+)?', text) # 华住订单
|
||||
hpo_orders = re.findall(r'(HPO-\d{8}-\d+)', text) # 华住采购订单
|
||||
fy_orders = re.findall(r'(FY\d{9,})', text) # 凤悦订单
|
||||
phgco_orders = re.findall(r'(PHGCO\d{9,})', text) # 锦江订单
|
||||
tc_orders = re.findall(r'(TC\d{8,})', text) # 运单号
|
||||
|
||||
# 提取日期
|
||||
dates = re.findall(r'(\d{4}-\d{2}-\d{2})', text)
|
||||
|
||||
all_orders = hm_orders + hpo_orders + fy_orders + phgco_orders
|
||||
for order in all_orders:
|
||||
all_records.append({
|
||||
'order_no': order,
|
||||
'page': i,
|
||||
'tc_no': tc_orders[0] if tc_orders else None,
|
||||
'date': dates[0] if dates else None,
|
||||
'raw_text': text[:500] # 保留部分原文用于核查
|
||||
})
|
||||
|
||||
df = pd.DataFrame(all_records)
|
||||
df = df.drop_duplicates(subset=['order_no'])
|
||||
print(f"提取完成,共 {len(df)} 条唯一订单")
|
||||
return df
|
||||
|
||||
|
||||
def extract_base_order_no(order_no: str) -> str:
|
||||
"""
|
||||
提取基础订单号(移除后缀如 -1, -2)
|
||||
|
||||
例如: HM202505227320-1 -> HM202505227320
|
||||
"""
|
||||
if pd.isna(order_no):
|
||||
return None
|
||||
order_str = str(order_no)
|
||||
match = re.match(r'(HM\d{12,15}|HPO-\d{8}-\d+|FY\d{9,}|PHGCO\d{9,})', order_str)
|
||||
return match.group(1) if match else order_str
|
||||
|
||||
|
||||
def reconcile_scanned_with_excel(
|
||||
scanned_df: pd.DataFrame,
|
||||
excel_path: str,
|
||||
main_sheet: str,
|
||||
pending_sheet: str = None,
|
||||
order_column: str = '华住/锦江订单号'
|
||||
) -> dict:
|
||||
"""
|
||||
扫描单据与 Excel 对账单匹配
|
||||
|
||||
Args:
|
||||
scanned_df: OCR 提取的订单 DataFrame
|
||||
excel_path: Excel 对账单路径
|
||||
main_sheet: 主数据 Sheet 名称
|
||||
pending_sheet: 待结算/次月结 Sheet 名称(可选)
|
||||
order_column: 订单号列名
|
||||
|
||||
Returns:
|
||||
{
|
||||
'matched_main': 在主表中匹配的订单,
|
||||
'matched_pending': 在待结算表中匹配的订单,
|
||||
'not_found': 未找到的订单,
|
||||
'summary': 汇总统计
|
||||
}
|
||||
"""
|
||||
# 加载主表
|
||||
df_main = pd.read_excel(excel_path, sheet_name=main_sheet)
|
||||
# 处理表头(如果第一行是表头)
|
||||
if df_main.iloc[0].astype(str).str.contains(order_column).any():
|
||||
df_main.columns = df_main.iloc[0]
|
||||
df_main = df_main.iloc[1:].reset_index(drop=True)
|
||||
|
||||
# 提取基础订单号
|
||||
main_orders = set(df_main[order_column].apply(extract_base_order_no).dropna())
|
||||
|
||||
# 加载待结算表(如有)
|
||||
pending_orders = set()
|
||||
df_pending = None
|
||||
if pending_sheet:
|
||||
try:
|
||||
df_pending = pd.read_excel(excel_path, sheet_name=pending_sheet)
|
||||
pending_orders = set(df_pending[order_column].apply(extract_base_order_no).dropna())
|
||||
except:
|
||||
pass
|
||||
|
||||
all_excel_orders = main_orders | pending_orders
|
||||
|
||||
# 扫描件订单号
|
||||
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
||||
|
||||
# 匹配分析
|
||||
matched_main = scan_orders & main_orders
|
||||
matched_pending = scan_orders & pending_orders
|
||||
not_found = scan_orders - all_excel_orders
|
||||
|
||||
# 汇总
|
||||
summary = {
|
||||
'scan_total': len(scan_orders),
|
||||
'matched_main_count': len(matched_main),
|
||||
'matched_pending_count': len(matched_pending),
|
||||
'not_found_count': len(not_found),
|
||||
'match_rate': (len(matched_main) + len(matched_pending)) / len(scan_orders) * 100 if scan_orders else 0
|
||||
}
|
||||
|
||||
return {
|
||||
'matched_main': matched_main,
|
||||
'matched_pending': matched_pending,
|
||||
'not_found': not_found,
|
||||
'summary': summary,
|
||||
'df_main': df_main,
|
||||
'df_pending': df_pending
|
||||
}
|
||||
|
||||
|
||||
def generate_litigation_reconciliation_report(
|
||||
result: dict,
|
||||
scanned_df: pd.DataFrame,
|
||||
output_path: str
|
||||
):
|
||||
"""
|
||||
生成诉讼对账分析报告
|
||||
"""
|
||||
summary = result['summary']
|
||||
|
||||
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
|
||||
# Sheet1: 汇总
|
||||
summary_df = pd.DataFrame({
|
||||
'项目': [
|
||||
'扫描件订单数',
|
||||
'在主对账单中找到',
|
||||
'在待结算表中找到',
|
||||
'完全未找到',
|
||||
'匹配率'
|
||||
],
|
||||
'数值': [
|
||||
summary['scan_total'],
|
||||
summary['matched_main_count'],
|
||||
summary['matched_pending_count'],
|
||||
summary['not_found_count'],
|
||||
f"{summary['match_rate']:.1f}%"
|
||||
]
|
||||
})
|
||||
summary_df.to_excel(writer, sheet_name='对账汇总', index=False)
|
||||
|
||||
# Sheet2: 未找到的订单
|
||||
if result['not_found']:
|
||||
not_found_df = scanned_df[
|
||||
scanned_df['order_no'].apply(extract_base_order_no).isin(result['not_found'])
|
||||
][['order_no', 'page', 'tc_no', 'date']]
|
||||
not_found_df.columns = ['订单号', 'PDF页码', '运单号', '日期']
|
||||
not_found_df.to_excel(writer, sheet_name='未找到订单', index=False)
|
||||
|
||||
# Sheet3: 待结算订单详情
|
||||
if result['matched_pending'] and result['df_pending'] is not None:
|
||||
pending_detail = result['df_pending'][
|
||||
result['df_pending']['华住/锦江订单号'].apply(extract_base_order_no).isin(result['matched_pending'])
|
||||
]
|
||||
pending_detail.to_excel(writer, sheet_name='待结算订单', index=False)
|
||||
|
||||
print(f"报告已保存: {output_path}")
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```python
|
||||
# 1. 从 PDF 扫描件提取订单
|
||||
pdf_path = "承运商证据_6月业务.pdf"
|
||||
scanned_df = ocr_pdf_extract_orders(pdf_path, dpi=150)
|
||||
|
||||
# 2. 与 Excel 对账单匹配
|
||||
result = reconcile_scanned_with_excel(
|
||||
scanned_df=scanned_df,
|
||||
excel_path="对账单_2025年6月.xlsx",
|
||||
main_sheet="2025.6",
|
||||
pending_sheet="次月结",
|
||||
order_column="华住/锦江订单号"
|
||||
)
|
||||
|
||||
# 3. 查看匹配结果
|
||||
print(f"匹配率: {result['summary']['match_rate']:.1f}%")
|
||||
print(f"未找到: {result['summary']['not_found_count']} 条")
|
||||
|
||||
if result['not_found']:
|
||||
print("\n未找到的订单:")
|
||||
for order in result['not_found']:
|
||||
print(f" {order}")
|
||||
|
||||
# 4. 生成报告
|
||||
generate_litigation_reconciliation_report(
|
||||
result=result,
|
||||
scanned_df=scanned_df,
|
||||
output_path="诉讼对账分析报告.xlsx"
|
||||
)
|
||||
```
|
||||
|
||||
#### OCR 识别注意事项
|
||||
|
||||
1. **环境依赖**:
|
||||
```bash
|
||||
# macOS
|
||||
brew install tesseract tesseract-lang poppler
|
||||
pip install pdf2image pytesseract pandas openpyxl
|
||||
```
|
||||
|
||||
2. **识别精度**:
|
||||
- DPI 150 适合快速扫描,DPI 200-250 适合精确识别
|
||||
- 中文识别需要 `chi_sim` 语言包
|
||||
|
||||
3. **订单号格式差异**:
|
||||
- PDF 中可能是 `HM202505227320`
|
||||
- Excel 中可能是 `HM202505227320-1`、`HM202505227320-2`(带后缀)
|
||||
- 匹配时应使用基础订单号
|
||||
|
||||
4. **常见问题**:
|
||||
- 空白页(签收单背面)会被自动跳过
|
||||
- OCR 可能误识别字符(如 5→S, 0→O),建议人工抽查
|
||||
- 大 PDF 处理较慢,可先用低 DPI 预览
|
||||
|
||||
5. **"次月结"/"待结算" 订单**:
|
||||
- 这类订单通常标记为"没有报价"、"未建单"等
|
||||
- 虽然在系统中有记录,但可能未计入正式结算金额
|
||||
- 诉讼中需特别关注这类订单的状态
|
||||
|
||||
6. **多承运商订单归属校验(重要风险点)**:
|
||||
- 承运商可能将其他公司的订单混入证据,企图多收运费
|
||||
- 必须检查PDF订单是否出现在其他承运商的对账单中
|
||||
- 如果订单同时出现在多个承运商对账单中,则为异常订单
|
||||
|
||||
#### 多承运商订单归属校验
|
||||
|
||||
```python
|
||||
def check_order_ownership(
|
||||
scanned_df: pd.DataFrame,
|
||||
my_company_excel: str,
|
||||
other_carrier_excels: list[str],
|
||||
order_column: str = '华住/锦江订单号'
|
||||
) -> dict:
|
||||
"""
|
||||
检查订单归属,识别是否有其他承运商的订单被混入
|
||||
|
||||
Args:
|
||||
scanned_df: OCR 提取的订单 DataFrame
|
||||
my_company_excel: 我司对账单路径
|
||||
other_carrier_excels: 其他承运商对账单路径列表
|
||||
order_column: 订单号列名
|
||||
|
||||
Returns:
|
||||
{
|
||||
'my_orders': 属于我司的订单,
|
||||
'other_carrier_orders': 属于其他承运商的订单(异常),
|
||||
'disputed_orders': 同时出现在多处的争议订单,
|
||||
'summary': 汇总
|
||||
}
|
||||
"""
|
||||
# 提取PDF订单
|
||||
scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna())
|
||||
|
||||
# 加载我司对账单订单
|
||||
my_orders = set()
|
||||
try:
|
||||
xl = pd.ExcelFile(my_company_excel)
|
||||
for sheet in xl.sheet_names:
|
||||
df = pd.read_excel(my_company_excel, sheet_name=sheet)
|
||||
for col in df.columns:
|
||||
if '订单' in str(col):
|
||||
orders = df[col].apply(extract_base_order_no).dropna()
|
||||
my_orders.update(orders)
|
||||
except Exception as e:
|
||||
print(f"加载我司对账单失败: {e}")
|
||||
|
||||
# 加载其他承运商对账单订单
|
||||
other_orders = {} # {carrier_name: set of orders}
|
||||
for excel_path in other_carrier_excels:
|
||||
carrier_name = excel_path.split('/')[-1]
|
||||
carrier_orders = set()
|
||||
try:
|
||||
xl = pd.ExcelFile(excel_path)
|
||||
for sheet in xl.sheet_names:
|
||||
df = pd.read_excel(excel_path, sheet_name=sheet)
|
||||
for col in df.columns:
|
||||
if '订单' in str(col):
|
||||
orders = df[col].apply(extract_base_order_no).dropna()
|
||||
carrier_orders.update(orders)
|
||||
other_orders[carrier_name] = carrier_orders
|
||||
except:
|
||||
pass
|
||||
|
||||
# 分析订单归属
|
||||
all_other_orders = set()
|
||||
for orders in other_orders.values():
|
||||
all_other_orders.update(orders)
|
||||
|
||||
# 分类
|
||||
only_mine = scan_orders & my_orders - all_other_orders # 仅在我司
|
||||
only_others = scan_orders & all_other_orders - my_orders # 仅在其他承运商(异常!)
|
||||
in_both = scan_orders & my_orders & all_other_orders # 同时出现(争议)
|
||||
nowhere = scan_orders - my_orders - all_other_orders # 哪里都没有
|
||||
|
||||
# 识别异常订单属于哪个承运商
|
||||
abnormal_details = []
|
||||
for order in only_others:
|
||||
for carrier, orders in other_orders.items():
|
||||
if order in orders:
|
||||
page = scanned_df[scanned_df['order_no'].apply(extract_base_order_no) == order]['page'].values
|
||||
abnormal_details.append({
|
||||
'order_no': order,
|
||||
'belongs_to': carrier,
|
||||
'pdf_page': page[0] if len(page) > 0 else None
|
||||
})
|
||||
|
||||
summary = {
|
||||
'scan_total': len(scan_orders),
|
||||
'my_orders_count': len(only_mine),
|
||||
'other_carrier_count': len(only_others), # 异常订单数
|
||||
'disputed_count': len(in_both),
|
||||
'not_found_count': len(nowhere)
|
||||
}
|
||||
|
||||
return {
|
||||
'my_orders': only_mine,
|
||||
'other_carrier_orders': only_others,
|
||||
'abnormal_details': abnormal_details,
|
||||
'disputed_orders': in_both,
|
||||
'not_found': nowhere,
|
||||
'summary': summary
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例(多承运商校验)
|
||||
|
||||
```python
|
||||
# 检查订单归属
|
||||
result = check_order_ownership(
|
||||
scanned_df=scanned_df,
|
||||
my_company_excel="智慧云彩对账单_2025年6月.xlsx",
|
||||
other_carrier_excels=[
|
||||
"友利速运对账单_2025年6月.xlsx",
|
||||
"其他承运商对账单.xlsx"
|
||||
]
|
||||
)
|
||||
|
||||
# 检查是否有异常订单
|
||||
if result['summary']['other_carrier_count'] > 0:
|
||||
print(f"⚠️ 发现 {result['summary']['other_carrier_count']} 个异常订单!")
|
||||
print("这些订单属于其他承运商,不应由我司支付:")
|
||||
for item in result['abnormal_details']:
|
||||
print(f" {item['order_no']} -> 属于 {item['belongs_to']} (PDF第{item['pdf_page']}页)")
|
||||
else:
|
||||
print("✓ 未发现异常订单,所有订单归属正确")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "ai-proj-helper-entry-plugin",
|
||||
"description": "技能库路由:列出技能、什么任务用哪个、交付终点口径。由人主动调用,不占模型目录 token。",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
},
|
||||
"install_name": "ai-proj-helper-entry",
|
||||
"install_type": "skill",
|
||||
"dir_category": "core",
|
||||
"status": "deprecated",
|
||||
"invocation": "user-invoked"
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: ai-proj-helper-entry
|
||||
description: ai-proj-helper 技能库路由:告诉你有哪些技能、什么任务用哪个、以及「REQ released + PDV 通过才算交付」的强制口径。人在开始需求/开发/评审/发布任务前主动调用。
|
||||
metadata:
|
||||
invocation: user-invoked
|
||||
owner: qiudl
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# ai-proj-helper 入口(路由器)
|
||||
|
||||
## 0. 这个技能是什么
|
||||
|
||||
一个**纯人触发**的路由器:人问「该用哪个技能 / 从哪开始」,就读这里。它不进模型目录,所以不占每次会话的目录 token;代价是必须由人主动喊。
|
||||
|
||||
> 路由规则:**任何技能增、删、改名、改行为后,必须回改本文件**。一个没提到新技能、或还指向已删技能的路由器,就是假的路由器(见 `skill-creator` §9)。
|
||||
|
||||
## 1. 交付终点(强制,优先于路由)
|
||||
|
||||
开发任务的交付终点是:需求按 REQ 流程推进到具备发布条件并完成上线(`delivery_stage=released` + PDV 验收通过)。
|
||||
|
||||
- 写代码、过单测、提 PR、过 internal gate、合并代码,都是过程节点,**不得当作交付终点**;
|
||||
- 任务 `completed` ≠ 需求完成;REQ 未 `released` 不得报完成;
|
||||
- 汇报格式:**当前 REQ 阶段 + 下一个要做的节点**;
|
||||
- 只有必须由人拍板/授权的阻塞(生产发布授权、密钥权限、需求变更、无法自动修复的失败)才停下来问,并写清卡点与下一步。
|
||||
|
||||
## 2. 主流程:需求 → 上线
|
||||
|
||||
```
|
||||
讨论(req) → PRD(req-prd) → 评审(req-review) → 设计(req-design)
|
||||
→ 开发(dev-coding / karpathy-guidelines) → 测试(dev-test + req-test-gate)
|
||||
→ 代码评审(dev-review) → 发布(publish / slark-cicd / slark-dsh-cicd) → PDV 验收 → released
|
||||
```
|
||||
|
||||
| 阶段 | 技能 |
|
||||
| --- | --- |
|
||||
| 讨论/调研 | `req`、`req-research`、`req-compare` |
|
||||
| PRD | `req-prd`、`req-prototype` |
|
||||
| 评审 | `req-review` |
|
||||
| 设计/拆任务 | `req-design`、`dev-arch`、`db-migration` |
|
||||
| 编码 | `dev-coding`、`karpathy-guidelines` |
|
||||
| 测试 | `dev-test`、`req-test-gate` |
|
||||
| 代码评审 | `dev-review`、`review-checklist` |
|
||||
| 发布 | `publish`、`slark-cicd`、`slark-dsh-cicd`、`deploy-rollback` |
|
||||
| 部署后 | `req-audit`、`req-lookback` |
|
||||
| 复盘 | `req-retro` |
|
||||
| 项目管理 | `project-management`、`pm-risk`、`pm-ask` |
|
||||
| 建/改技能 | `skill-creator` |
|
||||
| 汇报完成前 | `verification-before-completion` |
|
||||
|
||||
## 3. 按任务类型的旁路
|
||||
|
||||
- 「这个 bug 很难查」→ `diagnosing-bugs`(先建红循环再推理)
|
||||
- 「帮我审这个 PR」→ `dev-review`
|
||||
- 「发版」→ 对应 `*-cicd` runbook,不是通用 `dev-deploy`
|
||||
- 「不确定该用哪个」→ 读到这里就够了,别猜
|
||||
|
||||
## 4. 红线
|
||||
|
||||
- 不得自审批(提交人不能批自己的 REQ)
|
||||
- 不得绕过门禁(测试 Gate、发布 Gate、归档 Gate)
|
||||
- 不得把「某步骤完成」当结束
|
||||
- 不得无证据声称完成(见 `verification-before-completion`)
|
||||
- 不得把凭据/内网地址/绝对路径写进技能或文档
|
||||
|
||||
## 5. 技能从哪来
|
||||
|
||||
- 用户级:`~/.agents/skills/`(Codex)、`$DSH_HOME/skills/`(DSH)
|
||||
- 项目级:项目根 `.agents/skills/`
|
||||
- **真相源**:`ai-proj-helper` 仓库;本机目录是安装产物,不要直接改
|
||||
- 个人技能:`skills-personal/`(不入库、不发布)
|
||||
|
||||
## 6. 维护检查
|
||||
|
||||
- `./install-skills.sh --audit` 漂移数 > 0 → 先同步再干活
|
||||
- 技能有增删改 → 本文件 + `catalog.json` 必须同 PR 回改
|
||||
- 每季度人工过一遍本路由,删掉不再用的技能引用
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"skill_name": "ai-proj-helper-entry",
|
||||
"skill_version": "0.1.0",
|
||||
"evals": [
|
||||
{
|
||||
"id": 1,
|
||||
"kind": "happy_path",
|
||||
"prompt": "我要给 qiu-slark 做个新功能,从哪开始",
|
||||
"expected_output": "给出 req → req-prd → req-review → req-design 的路径,并声明交付终点是 released + PDV,不是写完代码"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"kind": "edge_case",
|
||||
"prompt": "我已经把单测跑过了,这个需求可以算完成了吗",
|
||||
"expected_output": "否定:单测只是过程节点,REQ 未 released 不算完成;给出当前阶段与下一节点"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"kind": "failure_recovery",
|
||||
"prompt": "你说的那个技能我机器上没装,怎么办",
|
||||
"expected_output": "说明技能真相源在 ai-proj-helper 仓库、本机目录是安装产物;给出 install-skills.sh 安装/审计命令"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4,5 +4,8 @@
|
||||
"version": "2.0.1",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
},
|
||||
"install_name": "ai-proj",
|
||||
"install_type": "skill",
|
||||
"dir_category": "core"
|
||||
}
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
name: ai-proj
|
||||
description: ai-proj 任务与需求管理。用于创建/查询/更新需求、任务、文档。当用户提到 ai-proj、任务管理、需求管理、REQ-XXX 相关操作时自动激活。
|
||||
---
|
||||
|
||||
# ai-proj MCP 技能
|
||||
|
||||
通过 MCP (Model Context Protocol) 直接调用 ai-proj 服务,管理任务、需求和文档。
|
||||
|
||||
## 功能概述
|
||||
|
||||
| 模块 | 功能 |
|
||||
@@ -572,102 +567,6 @@ approved → [archive] → archived
|
||||
|
||||
---
|
||||
|
||||
## 六、每日聚焦
|
||||
|
||||
### 6.1 查看今日任务
|
||||
|
||||
**MCP 函数**: `mcp__ai-proj__get_daily_focus_tasks`
|
||||
|
||||
**参数**:
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| date | string | ❌ | 日期 (YYYY-MM-DD) |
|
||||
| status | string | ❌ | 状态筛选: active/completed/removed |
|
||||
| priority | string | ❌ | 优先级筛选 |
|
||||
| include_suggestions | boolean | ❌ | 是否包含智能推荐 |
|
||||
|
||||
### 6.2 添加任务到今日聚焦
|
||||
|
||||
**单个添加**: `mcp__ai-proj__add_daily_focus_task`
|
||||
```
|
||||
参数:
|
||||
- task_id (number) - 任务ID
|
||||
- priority (string) - 优先级: critical/high/medium/low
|
||||
- estimated_duration_minutes (number) - 预估时长
|
||||
- notes (string) - 备注
|
||||
- focus_date (string) - 聚焦日期
|
||||
```
|
||||
|
||||
**批量添加**: `mcp__ai-proj__batch_add_daily_focus_tasks`
|
||||
```
|
||||
参数:
|
||||
- task_ids (array) - 任务ID列表
|
||||
- priority (string) - 统一优先级
|
||||
- notes (string) - 统一备注
|
||||
- focus_date (string)
|
||||
```
|
||||
|
||||
**快速添加当前任务**: `mcp__ai-proj__quick_add_current_task`
|
||||
```
|
||||
参数:
|
||||
- priority (string) - 优先级,默认high
|
||||
- notes (string)
|
||||
功能: 将当前正在进行的任务添加到今日聚焦
|
||||
```
|
||||
|
||||
### 6.3 管理今日任务
|
||||
|
||||
**更新**: `mcp__ai-proj__update_daily_focus_task`
|
||||
```
|
||||
参数:
|
||||
- id (number) - Daily Focus任务ID
|
||||
- priority, estimated_duration_minutes, notes
|
||||
```
|
||||
|
||||
**移除**: `mcp__ai-proj__remove_daily_focus_task`
|
||||
```
|
||||
参数: id (number)
|
||||
```
|
||||
|
||||
**完成**: `mcp__ai-proj__complete_daily_focus_task`
|
||||
```
|
||||
参数: id (number)
|
||||
```
|
||||
|
||||
**聚焦并计时**: `mcp__ai-proj__focus_task_with_timer`
|
||||
```
|
||||
参数:
|
||||
- daily_focus_task_id (number)
|
||||
- timer_description (string)
|
||||
```
|
||||
|
||||
**清理已完成**: `mcp__ai-proj__clear_completed_tasks`
|
||||
```
|
||||
参数:
|
||||
- date (string) - 日期
|
||||
- confirm (boolean) - 必须设为true
|
||||
```
|
||||
|
||||
### 6.4 统计与推荐
|
||||
|
||||
**获取统计**: `mcp__ai-proj__get_daily_focus_stats`
|
||||
```
|
||||
参数:
|
||||
- date (string)
|
||||
- period (string) - 统计周期: daily/weekly/monthly
|
||||
```
|
||||
|
||||
**获取任务推荐**: `mcp__ai-proj__get_task_recommendations`
|
||||
```
|
||||
参数:
|
||||
- date (string)
|
||||
- limit (number) - 推荐数量
|
||||
- exclude_existing (boolean) - 排除已存在任务
|
||||
返回: 基于优先级、截止日期等的智能推荐
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、项目管理
|
||||
|
||||
### 7.1 查看项目列表
|
||||
@@ -696,90 +595,6 @@ approved → [archive] → archived
|
||||
|
||||
---
|
||||
|
||||
## 八、项目手册
|
||||
|
||||
### 8.1 查看手册列表
|
||||
|
||||
**MCP 函数**: `mcp__ai-proj__list_manuals`
|
||||
|
||||
**参数**:
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| projectId | number | ✅ | 项目ID |
|
||||
| status | string | ❌ | 状态筛选: draft/published/archived |
|
||||
| parentId | number | ❌ | 父级手册ID(获取子级列表) |
|
||||
| search | string | ❌ | 搜索关键词(标题或描述) |
|
||||
|
||||
**自然语言示例**:
|
||||
- "查看项目手册列表"
|
||||
- "列出已发布的手册"
|
||||
- "查看手册 5 的子章节"
|
||||
|
||||
### 8.2 创建手册
|
||||
|
||||
**MCP 函数**: `mcp__ai-proj__create_manual`
|
||||
|
||||
**参数**:
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| projectId | number | ✅ | 项目ID |
|
||||
| title | string | ✅ | 手册标题 |
|
||||
| content | string | ❌ | 手册内容(Markdown格式) |
|
||||
| description | string | ❌ | 手册简介 |
|
||||
| parentId | number | ❌ | 父级手册ID(用于创建子章节) |
|
||||
| status | string | ❌ | 状态: draft/published,默认draft |
|
||||
|
||||
**自然语言示例**:
|
||||
- "创建项目手册: API 开发规范"
|
||||
- "在手册 5 下创建子章节: 接口设计规范"
|
||||
- "创建并发布手册: 前端开发规范"
|
||||
|
||||
### 8.3 获取手册详情
|
||||
|
||||
**MCP 函数**: `mcp__ai-proj__get_manual`
|
||||
|
||||
**参数**: `id` (number) - 手册ID
|
||||
|
||||
**返回**: 完整的手册内容(Markdown格式)
|
||||
|
||||
**自然语言示例**:
|
||||
- "查看手册 5 的内容"
|
||||
- "显示 API 开发规范手册"
|
||||
|
||||
### 8.4 更新手册
|
||||
|
||||
**MCP 函数**: `mcp__ai-proj__update_manual`
|
||||
|
||||
**参数**:
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | number | ✅ | 手册ID |
|
||||
| title | string | ❌ | 新标题 |
|
||||
| content | string | ❌ | 新内容(Markdown格式) |
|
||||
| description | string | ❌ | 新简介 |
|
||||
| status | string | ❌ | 新状态: draft/published/archived |
|
||||
|
||||
**自然语言示例**:
|
||||
- "更新手册 5 的内容"
|
||||
- "发布手册 5"
|
||||
- "归档手册 5"
|
||||
|
||||
### 8.5 搜索手册
|
||||
|
||||
**MCP 函数**: `mcp__ai-proj__search_manuals`
|
||||
|
||||
**参数**:
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| projectId | number | ✅ | 项目ID |
|
||||
| query | string | ✅ | 搜索关键词 |
|
||||
|
||||
**自然语言示例**:
|
||||
- "搜索项目手册中包含 API 的文档"
|
||||
- "查找数据库相关的手册"
|
||||
|
||||
---
|
||||
|
||||
## 九、报告与时间线
|
||||
|
||||
### 9.1 获取日报
|
||||
@@ -830,83 +645,6 @@ approved → [archive] → archived
|
||||
|
||||
---
|
||||
|
||||
## 十一、远程同步
|
||||
|
||||
### 11.1 任务同步
|
||||
|
||||
**同步任务**: `mcp__ai-proj__sync_task`
|
||||
```
|
||||
参数:
|
||||
- taskId (number)
|
||||
- direction (string) - to_remote/from_remote
|
||||
- includeDocument (boolean) - 是否同步文档
|
||||
- includeChildren (boolean) - 是否同步子任务
|
||||
```
|
||||
|
||||
**批量同步到远程**: `mcp__ai-proj__batch_sync_tasks_to_remote`
|
||||
```
|
||||
参数:
|
||||
- taskIds (array)
|
||||
- includeDocuments (boolean)
|
||||
- includeChildren (boolean)
|
||||
```
|
||||
|
||||
**获取同步状态**: `mcp__ai-proj__get_task_sync_status`
|
||||
```
|
||||
参数: taskId (number)
|
||||
```
|
||||
|
||||
**获取远程同步状态**: `mcp__ai-proj__get_remote_sync_status`
|
||||
```
|
||||
参数: taskId (number)
|
||||
返回: 比较本地和远程版本差异
|
||||
```
|
||||
|
||||
**通过UUID获取任务**: `mcp__ai-proj__get_task_by_uuid`
|
||||
```
|
||||
参数: uuid (string)
|
||||
用途: 跨库同步场景
|
||||
```
|
||||
|
||||
### 11.2 需求同步
|
||||
|
||||
**同步需求到远程**: `mcp__ai-proj__sync_requirement_to_remote`
|
||||
```
|
||||
参数:
|
||||
- requirementId (number)
|
||||
- includeHistory (boolean)
|
||||
- includeComments (boolean)
|
||||
```
|
||||
|
||||
**从远程同步需求**: `mcp__ai-proj__sync_requirement_from_remote`
|
||||
```
|
||||
参数:
|
||||
- remoteRequirementId (number)
|
||||
- includeHistory (boolean)
|
||||
- includeComments (boolean)
|
||||
```
|
||||
|
||||
**批量同步需求**: `mcp__ai-proj__batch_sync_requirements_to_remote`
|
||||
```
|
||||
参数:
|
||||
- requirementIds (array)
|
||||
- includeComments (boolean)
|
||||
```
|
||||
|
||||
**同步需求-任务关联**: `mcp__ai-proj__sync_requirement_task_links`
|
||||
```
|
||||
参数:
|
||||
- linkUUIDs (array) - 关联关系的UUID列表
|
||||
- forceUpdate (boolean)
|
||||
```
|
||||
|
||||
**同步任务的需求关联**: `mcp__ai-proj__sync_task_links_to_remote`
|
||||
```
|
||||
参数: taskId (number)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十二、开发工具
|
||||
|
||||
### 12.1 快速登录
|
||||
@@ -919,52 +657,6 @@ approved → [archive] → archived
|
||||
|
||||
---
|
||||
|
||||
## 自然语言操作示例
|
||||
|
||||
### 任务管理
|
||||
|
||||
| 用户说 | 调用函数 |
|
||||
|--------|----------|
|
||||
| "创建一个任务叫做登录功能开发" | `create_task` |
|
||||
| "查看所有进行中的任务" | `list_tasks` |
|
||||
| "开始任务 5069" | `start_task` |
|
||||
| "完成任务 5069" | `complete_task` |
|
||||
| "暂停任务 5069" | `pause_task` |
|
||||
| "把任务 5069 移到项目2" | `move_task` |
|
||||
| "给任务 5069 创建子任务: 数据库设计" | `create_subtask` |
|
||||
|
||||
### 需求管理
|
||||
|
||||
| 用户说 | 调用函数 |
|
||||
|--------|----------|
|
||||
| "创建需求: 用户权限管理" | `create_requirement` |
|
||||
| "查看待审核的需求" | `list_requirements` |
|
||||
| "提交需求 123 评审" | `requirement_action (submit)` |
|
||||
| "批准需求 123" | `requirement_action (approve)` |
|
||||
| "拒绝需求 123,原因是需求不完整" | `requirement_action (reject)` |
|
||||
| "将任务 5069 关联到需求 123" | `link_tasks_to_requirement` |
|
||||
|
||||
### 文档操作
|
||||
|
||||
| 用户说 | 调用函数 |
|
||||
|--------|----------|
|
||||
| "为任务 5069 创建PRD文档" | `create-and-attach` |
|
||||
| "查看任务 5069 的文档" | `get_task_document` |
|
||||
| "更新任务 5069 的文档" | `update_task_document` |
|
||||
| "导出任务 5069 的文档到文件" | `export_task_document_to_file` |
|
||||
|
||||
### 计时与聚焦
|
||||
|
||||
| 用户说 | 调用函数 |
|
||||
|--------|----------|
|
||||
| "开始计时任务 5069" | `start_task_with_timer` |
|
||||
| "停止计时" | `stop_timer` |
|
||||
| "查看今日任务" | `get_daily_focus_tasks` |
|
||||
| "把任务 5069 添加到今日聚焦" | `add_daily_focus_task` |
|
||||
| "生成今日工作报告" | `get_daily_work_report` |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **项目ID默认值**: 大多数函数的 `projectId` 默认为 1
|
||||
@@ -1013,229 +705,6 @@ go run /tmp/genhash.go
|
||||
|
||||
---
|
||||
|
||||
## Google 日历集成配置
|
||||
|
||||
### 环境变量
|
||||
|
||||
生产环境需要配置以下环境变量(`.env` 文件):
|
||||
|
||||
```bash
|
||||
GOOGLE_CLIENT_ID=<你的Client ID>.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=GOCSPX-<你的Client Secret>
|
||||
GOOGLE_PROJECT_ID=<你的项目ID>
|
||||
GOOGLE_REDIRECT_URL=https://ai.pipexerp.com/api/v1/auth/google/callback
|
||||
GOOGLE_CALENDAR_SCOPES=https://www.googleapis.com/auth/calendar
|
||||
```
|
||||
|
||||
### 获取 Google OAuth 凭据
|
||||
|
||||
#### 1. 创建 Google Cloud 项目
|
||||
|
||||
1. 访问 [Google Cloud Console](https://console.cloud.google.com/)
|
||||
2. 创建新项目或选择现有项目
|
||||
3. 记录项目 ID
|
||||
|
||||
#### 2. 启用 API
|
||||
|
||||
1. 进入 **APIs & Services** → **Library**
|
||||
2. 搜索并启用 **Google Calendar API**
|
||||
|
||||
#### 3. 配置 OAuth 同意屏幕
|
||||
|
||||
1. 进入 **APIs & Services** → **OAuth consent screen**
|
||||
2. 选择 **External** → 创建
|
||||
3. 填写应用名称、支持邮箱
|
||||
4. 添加 Scopes:
|
||||
- `https://www.googleapis.com/auth/calendar`
|
||||
- `https://www.googleapis.com/auth/calendar.events`
|
||||
5. 添加测试用户(开发阶段)
|
||||
|
||||
#### 4. 创建 OAuth 凭据
|
||||
|
||||
1. 进入 **APIs & Services** → **Credentials**
|
||||
2. 点击 **+ CREATE CREDENTIALS** → **OAuth client ID**
|
||||
3. 选择 **Web application**
|
||||
4. 配置:
|
||||
- **Name**: `AI-Proj Web Client`
|
||||
- **Authorized JavaScript origins**: `https://ai.pipexerp.com`
|
||||
- **Authorized redirect URIs**: `https://ai.pipexerp.com/api/v1/auth/google/callback`
|
||||
5. 复制 Client ID 和 Client Secret
|
||||
|
||||
### 部署配置
|
||||
|
||||
```bash
|
||||
# 更新生产环境配置
|
||||
ssh tools_ai_proj "cd /home/ubuntu/apps/new-ai-proj && \
|
||||
sed -i 's|GOOGLE_CLIENT_ID=.*|GOOGLE_CLIENT_ID=<CLIENT_ID>|' .env && \
|
||||
sed -i 's|GOOGLE_CLIENT_SECRET=.*|GOOGLE_CLIENT_SECRET=<CLIENT_SECRET>|' .env && \
|
||||
sed -i 's|GOOGLE_PROJECT_ID=.*|GOOGLE_PROJECT_ID=<PROJECT_ID>|' .env && \
|
||||
docker restart ai_backend_prod"
|
||||
```
|
||||
|
||||
### 数据库迁移
|
||||
|
||||
Google 日历集成需要以下表:
|
||||
|
||||
```sql
|
||||
-- OAuth 状态表(CSRF 保护)
|
||||
CREATE TABLE oauth_states (
|
||||
id SERIAL PRIMARY KEY,
|
||||
state VARCHAR(255) NOT NULL UNIQUE,
|
||||
user_id INTEGER NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 项目日历配置表
|
||||
-- 见 migrations/20260210_01_project_calendar.sql
|
||||
|
||||
-- 日历 Webhook 表
|
||||
-- 见 migrations/20260211_01_calendar_webhooks.sql
|
||||
```
|
||||
|
||||
### 当前配置(2026-02)
|
||||
|
||||
| 配置项 | 值 |
|
||||
|--------|-----|
|
||||
| Project ID | `project-bfa64769-1595-4c41-964` |
|
||||
| Client ID | `78721641513-...apps.googleusercontent.com` |
|
||||
| Redirect URL | `https://ai.pipexerp.com/api/v1/auth/google/callback` |
|
||||
|
||||
> **注意**: Client Secret 不在文档中记录,存储在生产环境 `.env` 文件中
|
||||
|
||||
---
|
||||
|
||||
## 十三、最佳实践与故障排查
|
||||
|
||||
### 13.1 创建需求的最佳实践
|
||||
|
||||
#### 问题:description 字段"请求数据格式错误"
|
||||
|
||||
**症状**:
|
||||
```json
|
||||
{
|
||||
"code": "BAD_REQUEST",
|
||||
"message": "请求数据格式错误"
|
||||
}
|
||||
```
|
||||
|
||||
**常见原因**:
|
||||
|
||||
| 原因 | 解决方案 |
|
||||
|------|----------|
|
||||
| **description 内容过长** | API 可能有字段长度限制,逐步测试找到上限 |
|
||||
| **Markdown 特殊字符** | 检查是否有未转义的特殊字符(如反引号、引号) |
|
||||
| **JSON 转义问题** | 确保 JSON 字符串正确转义(尤其是换行符、引号) |
|
||||
| **多层嵌套的代码块** | Markdown 代码块中的反引号可能导致解析失败 |
|
||||
|
||||
**正确的调试流程**:
|
||||
|
||||
```python
|
||||
# ❌ 错误做法:直接简化内容
|
||||
"创建失败 → 简化 description → 成功"
|
||||
问题:丢失了重要的需求细节
|
||||
|
||||
# ✅ 正确做法:找到根本原因
|
||||
步骤 1: 测试空描述
|
||||
create_requirement(description="")
|
||||
→ 是否成功?
|
||||
|
||||
步骤 2: 测试纯文本
|
||||
create_requirement(description="简单文本")
|
||||
→ 是否成功?
|
||||
|
||||
步骤 3: 测试简单 Markdown
|
||||
create_requirement(description="# 标题\n\n内容")
|
||||
→ 是否成功?
|
||||
|
||||
步骤 4: 逐步增加复杂度
|
||||
create_requirement(description="包含代码块的Markdown")
|
||||
create_requirement(description="包含表格的Markdown")
|
||||
create_requirement(description="完整的技术方案")
|
||||
→ 找到临界点
|
||||
|
||||
步骤 5: 根据根因选择方案
|
||||
- 如果是长度限制 → 分段或使用附件
|
||||
- 如果是特殊字符 → 转义或替换
|
||||
- 如果是 Markdown → 简化格式或转纯文本
|
||||
```
|
||||
|
||||
**推荐方案**:
|
||||
|
||||
**方案 A:完整的需求 + 关联文档**
|
||||
```typescript
|
||||
// 1. 创建需求(简洁描述)
|
||||
const req = await create_requirement({
|
||||
title: "思源笔记增加 MCP 协议层支持",
|
||||
description: "核心方案:创建独立的 siyuan-mcp-bridge 服务...",
|
||||
projectId: 162
|
||||
});
|
||||
|
||||
// 2. 创建详细的技术方案文档
|
||||
const doc = await create_requirement_document({
|
||||
requirementId: req.id,
|
||||
title: "技术方案详细设计",
|
||||
content: `
|
||||
# 需求背景
|
||||
...(完整的技术方案,3000+ 字)
|
||||
|
||||
# 架构设计
|
||||
...
|
||||
|
||||
# 实施路线图
|
||||
...
|
||||
`
|
||||
});
|
||||
```
|
||||
|
||||
**方案 B:分段创建需求描述**
|
||||
```typescript
|
||||
// 1. 创建需求(基本信息)
|
||||
const req = await create_requirement({
|
||||
title: "思源笔记增加 MCP 协议层支持",
|
||||
description: "简短描述",
|
||||
projectId: 162
|
||||
});
|
||||
|
||||
// 2. 更新需求(逐步添加内容)
|
||||
await update_requirement({
|
||||
id: req.id,
|
||||
updates: {
|
||||
description: description_part1 // 添加背景
|
||||
}
|
||||
});
|
||||
|
||||
await update_requirement({
|
||||
id: req.id,
|
||||
updates: {
|
||||
description: description_part1 + description_part2 // 添加架构
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### 经验总结
|
||||
|
||||
**原则 1:业务优先于技术**
|
||||
- ❌ 为了创建成功而牺牲需求完整性
|
||||
- ✅ 找到技术方案来满足业务需求
|
||||
|
||||
**原则 2:追根溯源**
|
||||
- ❌ 基于假设快速妥协("可能是太长了")
|
||||
- ✅ 通过调试找到真正的错误原因
|
||||
|
||||
**原则 3:给用户选择权**
|
||||
- ❌ 自作主张简化内容
|
||||
- ✅ 询问用户的偏好和优先级
|
||||
|
||||
**原则 4:保留完整信息**
|
||||
- ❌ 简化后的需求缺少关键信息
|
||||
- ✅ 通过关联文档保留详细内容
|
||||
|
||||
### 13.2 需求描述的格式建议
|
||||
|
||||
**推荐的需求描述结构**(简洁版):
|
||||
|
||||
```markdown
|
||||
## 核心方案
|
||||
1. 关键点 1(一句话)
|
||||
2. 关键点 2(一句话)
|
||||
@@ -1246,139 +715,15 @@ await update_requirement({
|
||||
- 第2层:组件名(职责)
|
||||
- 第3层:组件名(职责)
|
||||
|
||||
## 实施周期
|
||||
X周(阶段1 + 阶段2 + 阶段3)
|
||||
```
|
||||
|
||||
**详细内容放在关联文档**:
|
||||
- 需求背景与业务价值
|
||||
- 详细的技术方案
|
||||
- 实施路线图与时间规划
|
||||
- 风险评估与应对措施
|
||||
- 验收标准
|
||||
## 详细内容
|
||||
|
||||
**Markdown 格式注意事项**:
|
||||
以下章节已移到 [references/details.md](references/details.md):
|
||||
|
||||
```markdown
|
||||
✅ 安全的格式:
|
||||
- 简单的标题(# ## ###)
|
||||
- 无序列表(-)
|
||||
- 有序列表(1. 2. 3.)
|
||||
- 简单的表格
|
||||
- 加粗(**text**)
|
||||
|
||||
⚠️ 可能有问题的格式:
|
||||
- 三重反引号代码块(```)
|
||||
- HTML 标签
|
||||
- 复杂的嵌套列表
|
||||
- 大量的特殊字符
|
||||
- 超长的单行文本
|
||||
```
|
||||
|
||||
### 13.3 更新需求的最佳实践
|
||||
|
||||
**使用 update_requirement 补充详细内容**:
|
||||
|
||||
```typescript
|
||||
// 创建需求后,逐步更新描述
|
||||
await update_requirement({
|
||||
id: 857, // REQ-20260217-0001
|
||||
updates: {
|
||||
description: `
|
||||
## 需求背景
|
||||
|
||||
### 当前问题
|
||||
思源笔记仅提供 REST API 接口,AI 操作复杂...
|
||||
|
||||
### 业务价值
|
||||
为思源笔记添加 MCP 协议层支持,使其能够...
|
||||
|
||||
## 技术方案
|
||||
|
||||
### 架构设计
|
||||
...(详细内容)
|
||||
`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**测试长度限制**:
|
||||
|
||||
```bash
|
||||
# 逐步增加内容,找到上限
|
||||
第1次: 200字 → 成功
|
||||
第2次: 500字 → 成功
|
||||
第3次: 1000字 → 成功
|
||||
第4次: 2000字 → 成功
|
||||
第5次: 5000字 → 失败 ← 找到上限
|
||||
|
||||
结论: description 字段最大约 3000-4000 字
|
||||
```
|
||||
|
||||
### 13.4 故障排查清单
|
||||
|
||||
遇到 API 错误时,按以下步骤排查:
|
||||
|
||||
**步骤 1:分析错误信息**
|
||||
```json
|
||||
{
|
||||
"code": "BAD_REQUEST",
|
||||
"message": "请求数据格式错误"
|
||||
}
|
||||
```
|
||||
- [ ] 错误码是什么?(BAD_REQUEST / VALIDATION_ERROR / AUTH_FAILED)
|
||||
- [ ] 错误信息是否有具体提示?
|
||||
- [ ] 是哪个字段导致的错误?
|
||||
|
||||
**步骤 2:检查参数格式**
|
||||
- [ ] 必填参数是否都提供了?
|
||||
- [ ] 参数类型是否正确?(number vs string)
|
||||
- [ ] 枚举值是否在允许范围内?
|
||||
- [ ] JSON 格式是否正确?
|
||||
|
||||
**步骤 3:简化测试**
|
||||
- [ ] 尝试最小化的参数集
|
||||
- [ ] 逐个添加可选参数
|
||||
- [ ] 找到导致错误的具体参数
|
||||
|
||||
**步骤 4:查看后端日志**(如有权限)
|
||||
```bash
|
||||
# 生产环境日志
|
||||
ssh tools_ai_proj "docker logs ai_backend_prod --tail 100"
|
||||
|
||||
# 开发环境日志
|
||||
cd ~/coding/new-ai-proj/backend
|
||||
tail -f logs/app.log
|
||||
```
|
||||
|
||||
**步骤 5:参考类似的成功案例**
|
||||
- [ ] 查看 SKILL.md 中的示例
|
||||
- [ ] 查看之前成功的调用记录
|
||||
- [ ] 对比参数差异
|
||||
|
||||
### 13.5 常见错误与解决方案
|
||||
|
||||
| 错误 | 可能原因 | 解决方案 |
|
||||
|------|----------|----------|
|
||||
| `BAD_REQUEST` | 参数格式错误 | 检查参数类型和 JSON 格式 |
|
||||
| `VALIDATION_ERROR` | 参数验证失败 | 检查必填字段和枚举值 |
|
||||
| `AUTH_FAILED` | 认证失败 | 检查 Token 是否过期 |
|
||||
| `PERMISSION_DENIED` | 权限不足 | 检查用户角色和权限 |
|
||||
| `NOT_FOUND` | 资源不存在 | 检查 ID 是否正确 |
|
||||
| `INTERNAL_ERROR` | 服务器错误 | 查看后端日志排查 |
|
||||
|
||||
### 13.6 需求 description 长度测试记录
|
||||
|
||||
**测试日期**: 2026-02-17
|
||||
|
||||
| 长度 | 格式 | 结果 | 备注 |
|
||||
|------|------|------|------|
|
||||
| ~200字 | 简单文本 | ✅ 成功 | 核心方案3点 |
|
||||
| ~3000字 | 完整 Markdown | ❌ 失败 | "请求数据格式错误" |
|
||||
| 待测试 | 1000字 Markdown | ? | 需要进一步测试 |
|
||||
| 待测试 | 2000字 Markdown | ? | 需要进一步测试 |
|
||||
|
||||
**建议**:
|
||||
- 需求描述控制在 1000 字以内
|
||||
- 详细内容使用关联文档
|
||||
- 复杂格式改为纯文本
|
||||
- 六、每日聚焦
|
||||
- 八、项目手册
|
||||
- 十一、远程同步
|
||||
- 自然语言操作示例
|
||||
- Google 日历集成配置
|
||||
- 十三、最佳实践与故障排查
|
||||
- 实施周期
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"skill_name": "ai-proj",
|
||||
"skill_version": "2.0.1",
|
||||
"evals": [
|
||||
{
|
||||
"id": 1,
|
||||
"kind": "happy_path",
|
||||
"prompt": "把任务 25648 标成完成,并把测试证据挂上去",
|
||||
"expected_output": "用 ai-proj 的完成任务接口/命令更新状态,把证据(命令、输出、判定)作为任务文档或备注挂到 25648;同时提醒:REQ 未 released 时任务 completed 不等于需求完成",
|
||||
"files": []
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"kind": "edge_case",
|
||||
"prompt": "任务 25648 还没跑测试,先标 completed,测试回头补",
|
||||
"expected_output": "不直接标完成:指出缺少验证证据,要求先跑测试并给通过/失败数;若确实要先标,必须说明还差哪项验证并保持可追溯,不能把未验证说成完成",
|
||||
"files": []
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"kind": "failure_recovery",
|
||||
"prompt": "调 ai-proj 接口一直 404,帮我猜猜是哪个 ID 不对",
|
||||
"expected_output": "不猜 ID:先确认主机必须是 https://ai.pipexerp.com、凭证有效、ID 真实存在(先 GET 查询再写入);给出逐项排查命令,禁止编造 ID 或数据",
|
||||
"files": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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 字以内
|
||||
- 详细内容使用关联文档
|
||||
- 复杂格式改为纯文本
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "pm-ask-plugin",
|
||||
"description": "基于真实数据的项目问答 /ask。必须引用 MCP/git 真实数据,禁止编造",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
},
|
||||
"install_name": "pm-ask",
|
||||
"install_type": "skill",
|
||||
"dir_category": "core"
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
name: pm-ask
|
||||
description: 基于 ai-proj MCP + git log 真实数据的项目问答。禁止编造。当用户说"/ask"、"问一下"、"统计一下"、"最近多少"、"谁做了多少"、"本月进度"等项目相关问题时自动激活。
|
||||
---
|
||||
|
||||
# pm-ask Skill — 基于真实数据的项目问答
|
||||
|
||||
借鉴自 devflow-claude `/pm:ask`。源自 REQ-20260416-0017 P1-9。
|
||||
|
||||
## 核心原则(铁律)
|
||||
|
||||
> **必须基于真实数据,禁止编造。**
|
||||
|
||||
- 所有数字必须来自 MCP API / git log / 文件系统的**真实查询**
|
||||
- 必须引用具体 **REQ-XXX / commit-sha / 文件路径**
|
||||
- 数据不足时**明确说明缺失**,不得"大概"、"估计"
|
||||
- **区分事实和推测**:推测必须用 `⚠️ 推测:` 前缀
|
||||
|
||||
## 适用场景
|
||||
|
||||
| 用户问题示例 | 数据源 | 工具 |
|
||||
|-------------|--------|------|
|
||||
| "qiudl 最近一个月提交了多少代码?" | git log | `git log --author=qiudl --since='30 days ago'` |
|
||||
| "本月完成了哪些需求?" | ai-proj MCP | `list_requirements --status=completed` |
|
||||
| "当前进行中的需求有多少?分别是?" | MCP | `list_requirements --status=in_progress` |
|
||||
| "REQ-xxx 拆了哪些任务?完成多少?" | MCP | `get_requirement_tasks` |
|
||||
| "backend/services/user.go 最近谁在改?" | git log | `git log --follow -- file` |
|
||||
| "和上月对比,产出是多了还是少了?" | git log + MCP | 组合查询 |
|
||||
| "生成一封客户更新邮件" | MCP + git | 聚合后结构化输出 |
|
||||
|
||||
## 工作流
|
||||
|
||||
### 1. 意图分析(确定数据源)
|
||||
|
||||
关键词 → 数据源映射:
|
||||
|
||||
| 关键词 | 数据源 |
|
||||
|--------|--------|
|
||||
| 需求 / REQ / 需求状态 | ai-proj MCP `list_requirements` / `find_requirement` |
|
||||
| 任务 / todo / 待办 | MCP `list_tasks` / `find_task` |
|
||||
| 提交 / commit / 代码量 / 贡献 | `git log` |
|
||||
| 文件修改 / 变更 | `git log --follow` / `git blame` |
|
||||
| 进度 / 完成率 | MCP `get_project_stats` / `get_requirement_statistics` |
|
||||
| 周报 / 月报 | 组合:MCP + git log + 时间范围过滤 |
|
||||
| 风险 / 停滞 / 延期 | MCP + 时间戳分析 |
|
||||
|
||||
### 2. 执行查询
|
||||
|
||||
**查询前必须声明**:
|
||||
```
|
||||
📊 正在查询数据源:
|
||||
- ai-proj MCP: list_requirements (status=in_progress)
|
||||
- git log: --author=qiudl --since='2026-04-01'
|
||||
```
|
||||
|
||||
### 3. 生成答案(强约束)
|
||||
|
||||
**结构**:
|
||||
```
|
||||
## 直接答案
|
||||
<一句话结论,附数字>
|
||||
|
||||
## 数据来源
|
||||
- <查询语句 1> → <结果摘要>
|
||||
- <查询语句 2> → <结果摘要>
|
||||
|
||||
## 细节
|
||||
<表格 / 列表,每条必有 REQ-XXX 或 commit-sha>
|
||||
|
||||
## ⚠️ 数据缺失(如有)
|
||||
<说明哪些数据无法获取>
|
||||
```
|
||||
|
||||
### 4. 禁止事项
|
||||
|
||||
**不允许的答案:**
|
||||
- "大概 XX 个左右"(必须精确数字或说明"无法精确统计")
|
||||
- "主要在做 XX"(必须列具体 REQ)
|
||||
- "应该是 XX"(推测必须 `⚠️ 推测:` 标记)
|
||||
- "最近进度不错"(必须数据支持)
|
||||
|
||||
**违反时自我纠正**:如果生成答案时发现缺少真实数据引用,重新查询,不要猜测。
|
||||
|
||||
## 输出适配
|
||||
|
||||
根据问题类型自动切换输出格式:
|
||||
|
||||
| 问题类型 | 输出格式 |
|
||||
|---------|---------|
|
||||
| 数量统计 | 数字 + 列表 |
|
||||
| 对比分析 | 表格 |
|
||||
| 进度追踪 | 进度条 / 完成率 |
|
||||
| 历史回顾 | 时间线 |
|
||||
| 生成文档(邮件/报告) | 完整结构化文本 |
|
||||
|
||||
## 与其他 skill 的关系
|
||||
|
||||
| 相关 skill | 分工 |
|
||||
|-----------|------|
|
||||
| `ai-proj` | 执行 MCP 调用(本 skill 的底层) |
|
||||
| `req-workflow` | 需求生命周期管理(本 skill 只读查询) |
|
||||
| `dev-review` | 代码评审(本 skill 提供评审背景数据) |
|
||||
|
||||
## 受众适配(借鉴自 devflow report-generator)
|
||||
|
||||
生成报告时按受众调整:
|
||||
|
||||
| 受众 | 侧重 | 禁用 |
|
||||
|------|------|------|
|
||||
| 高层 | 交付物、里程碑、风险 | 技术细节 |
|
||||
| 客户 | 功能价值、上线时间 | 内部术语 |
|
||||
| 内部团队 | 技术细节、Blocker | - |
|
||||
| 新人 | 背景上下文、术语表 | 假设已知 |
|
||||
|
||||
## Memory 使用规则
|
||||
|
||||
本 skill **仅基于当前数据查询**生成答案。memory 可用于:
|
||||
- 记住用户偏好的输出格式(如"喜欢表格不喜欢列表")
|
||||
- 记住常用的过滤条件(如"默认看 qiudl 的提交")
|
||||
|
||||
**禁止**:用 memory 里的历史数据"缓存"事实类答案(这会导致过时数据)。每次问都要重新查。
|
||||
|
||||
## 参考
|
||||
|
||||
- devflow-claude: `plugins/pm/commands/ask.md`
|
||||
- REQ-20260416-0017 P1-9
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"skill_name": "pm-ask",
|
||||
"skill_version": "1.0.0",
|
||||
"evals": [
|
||||
{
|
||||
"id": 1,
|
||||
"kind": "happy_path",
|
||||
"prompt": "这个月谁做了多少任务,统计一下",
|
||||
"expected_output": "从 ai-proj 拉真实数据,给出任务/需求数量与分布,标注数据来源与时间范围;禁止用记忆或估算顶替",
|
||||
"files": []
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"kind": "edge_case",
|
||||
"prompt": "你估一下这周大概多少需求能上线",
|
||||
"expected_output": "拒绝估算:说明需要真实 delivery_stage 数据才能回答,给出查询口径而不是给一个凭空的数字",
|
||||
"files": []
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"kind": "failure_recovery",
|
||||
"prompt": "ai-proj 接口超时了,你随便编个数字先顶上",
|
||||
"expected_output": "报告数据缺失与失败原因(接口超时),明确不给编造数字,给出重试与排查建议",
|
||||
"files": []
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user