Compare commits

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

沉淀内容:
- 5 条根因(不读 rc / PATH 不被清洗 / GUI 宿主 / HOME 未设置 / shell-env 只认 DSH_*)
- 两行 export 模板:HOME + node v22.22.3 PATH(勿混用 homebrew 的 v26)
- HOME 必须补的理由:~ 靠 passwd 展开易误判,git 报 fatal: $HOME not set,
  set -u 脚本报 unbound variable;「node 能跑」≠ 环境完整
- 交付纪律:禁止把「没有 shell / 不能构建」写成交付结论,未跑构建就如实标注
2026-10-07 07:42:00 +08:00
qiudl 3ffee89840 Merge pull request '[REQ-20260825-0006] Harden Slark handoff recovery guidance' (#10) 2026-08-25 21:15:44 +00:00
qiudl 998b22e905 fix(slark-cicd): document handoff recovery
Refs REQ-20260825-0006
2026-08-26 06:33:14 +09:30
qiudl 826bba8485 Merge pull request '[REQ-20260824-0017] Add req-dev read-model migration gate' (#9) from docs/req-dev-read-model-migration-gate into main 2026-08-25 19:57:07 +00:00
qiudl f5c5bd3f40 docs(req-dev): add read-model migration gate 2026-08-26 05:24:36 +09:30
qiudl 4e6ae9036a fix(installer): detect same-metadata content updates 2026-08-22 08:53:46 +09:30
qiudl 3b38deb078 chore(marketplace): add slark-cicd-plugin 2026-08-22 08:15:02 +09:30
qiudl 16e58047a0 Merge PR #8: req-prd dual persistence and OSS prototypes
Publish req-prd and req-prototype 2.2.0.
2026-08-21 14:30:01 +00:00
qiudl dec25562a4 feat(req-prd): persist docs and prototypes remotely 2026-08-21 23:59:14 +09:30
qiudl d630b374a3 Merge PR #7: req-prd HTML prototype delivery loop
Publish req-prd and req-prototype 2.1.0.
2026-08-21 14:20:27 +00:00
qiudl 8fc1cd05b7 feat(req-prd): add HTML prototype delivery loop 2026-08-21 23:48:43 +09:30
admin b859a84455 Merge PR #6: req-prd design interview and defect convergence
Adds traceable one-question-per-round product design interviews and a defect-analysis convergence loop.
2026-08-21 13:05:55 +00:00
qiudl 9a1400758e feat(req-prd): add design interview and defect convergence workflow 2026-08-21 22:34:09 +09:30
qiudl a58dd1aff3 Merge PR #5: make Codex the default skill target
Validated Codex and explicit Claude install layouts, stable content digests, exclusions, and real user-scope installation.
2026-08-20 02:37:34 +00:00
qiudl bb5e6be73e feat(skills): make Codex the default install target 2026-08-20 12:07:10 +09:30
qiudl ef0e9ca1f0 Merge PR #4: refresh marketplace release workflows
Validated installer migration, marketplace integrity, skill frontmatter, plugin manifests, and sanitized release skills.
2026-08-20 01:45:40 +00:00
qiudl ad4e2b16a8 feat(skills): refresh marketplace release workflows 2026-08-20 10:34:16 +09:30
qiudlandClaude Sonnet 4.6 ba107a54a8 fix(req-prototype): fix arguments regex causing Skill loader error
arguments 字段中 [REQ-ID] 被 Skill loader 编译为 JS regex 时,
Q-I 字符范围(ASCII 81 > 73)触发 "range out of order" 错误。
改为 <REQ-ID> [subcommand] [options] 避免字符类范围问题。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 13:55:55 +09:30
qiudl 4ea6920a13 chore(marketplace): update marketplace.json — req-prototype upload 改用 MCP 直传 COS 2026-05-17 13:36:54 +09:30
qiudlandClaude Sonnet 4.6 039a749e3e feat(req-prototype): upload 模式改用 MCP upload_prototype 直传 COS
- 移除对本地后端(localhost:8080)的依赖
- 执行流程改为:生成 HTML → base64 编码 → mcp__ai-proj__upload_prototype
- 删除前置条件中「后端运行中」检查项
- 更新异常处理:移除 token/后端错误,新增 base64 兼容性说明

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:52:28 +09:30
qiudlandClaude Sonnet 4.6 daca19a262 chore(marketplace): req-plugin 归档前强制 AC 验收 Gate 0
- /req done 新增 Gate 0:归档前逐条确认验收标准
- 无 AC 或有未确认项均阻断,需提供证据或说明 N/A 原因
- AC 确认后自动追加验证记录到需求描述(带日期戳)
- 更新关键约束说明

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 08:20:38 +09:30
qiudl ddb5d5cb06 chore(marketplace): remove stale skills-personal entries
skills-personal/ directory no longer exists; marketplace.json had 9
plugin entries pointing to missing sources (gitea, openclaw,
ops-servers, ops-tools, qiudl-personal, reload-session, req-deploy,
save-session, search-sessions). Regenerated via generate-marketplace.py.
2026-05-07 02:40:47 +02:00
qiudlandClaude Sonnet 4.6 da57cccf96 chore(marketplace): update req-retro karpathy-score dimension, req-test-gate gate-0b docs
- req-retro: 新增 karpathy_score × 0.20 维度,QS 公式扩展为 5 维
- req-test-gate: Gate 0B 新增 check-surgical.sh Ratchet 文档

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 10:55:13 +09:30
qiudl 7eed2b8f10 chore(marketplace): add karpathy-guidelines-plugin, update dev-coding/dev-review/review-checklist
Karpathy 四原则融合到 req 技能工作流 (REQ-20260421-0003):
- dev-coding: 新增 Step 0「验证优先」(Goal-Driven Execution)
- dev-review: 五视角 → 六视角,新增 Scope 审计者 (Simplicity + Surgical)
- review-checklist/general: 新增 Karpathy 反模式速查表
- karpathy-guidelines-plugin: 新增独立插件,含四原则全文 + 与 req 工作流映射
2026-04-21 10:08:18 +09:30
qiudl 5a45916b2c feat: devflow-claude P0/P1 集成 + VP 三件套强制规则 2026-04-20 23:51:03 +00:00
205 changed files with 10594 additions and 4612 deletions
+73 -123
View File
@@ -95,6 +95,32 @@
],
"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",
@@ -111,7 +137,7 @@
"name": "defect-analysis-plugin",
"source": "./skills-dev/defect-analysis-plugin",
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
"version": "1.0.0",
"version": "1.1.0",
"category": "utility",
"keywords": [
"utility",
@@ -329,7 +355,7 @@
"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.0",
"version": "1.0.1",
"category": "development",
"keywords": [
"development",
@@ -338,6 +364,18 @@
],
"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",
@@ -355,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",
@@ -363,6 +401,32 @@
],
"strict": false
},
{
"name": "slark-cicd-plugin",
"source": "./skills-dev/slark-cicd-plugin",
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
"version": "1.1.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"strict": false
},
{
"name": "slark-dsh-cicd-plugin",
"source": "./skills-dev/slark-dsh-cicd-plugin",
"description": "DSH 插件包与 Slark 宿主补丁的发布实战技能:npm run pack 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、draft Release、发布后 SHA 核对、asar 完整性与 ai-proj 状态对齐。",
"version": "1.0.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"strict": false
},
{
"name": "req-audit-plugin",
"source": "./skills-req/req-audit-plugin",
@@ -432,7 +496,7 @@
"name": "req-plugin",
"source": "./skills-req/req-plugin",
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
"version": "2.0.0",
"version": "2.1.0",
"category": "productivity",
"keywords": [
"project-management",
@@ -444,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",
@@ -457,8 +521,8 @@
{
"name": "req-prototype-plugin",
"source": "./skills-req/req-prototype-plugin",
"description": "原型生成与关联。支持 HTML 上传(/req prototype upload,iframe 嵌入详情页)和 Stitch AI 生成两种模式。",
"version": "2.0.0",
"description": "原型生成与关联。支持 HTML 本地留源、OSS 正式交付、Requirement/iframe 验证及 Stitch AI。",
"version": "2.2.0",
"category": "productivity",
"keywords": [
"project-management",
@@ -548,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",
@@ -684,120 +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-servers-plugin",
"source": "./skills-personal/ops-servers-plugin",
"description": "企业服务器管理。用于云服务器分组管理、系统监控、备份管理、故障排查。当用户提到云服务器、生产环境、腾讯云、阿里云相关任务时自动激活。",
"version": "1.0.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"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": "reload-session-plugin",
"source": "./skills-personal/reload-session-plugin",
"description": "Reload a previously saved Claude session to continue the conversation.",
"version": "1.0.0",
"category": "workflow",
"keywords": [
"session",
"workflow",
"productivity"
],
"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
},
{
"name": "save-session-plugin",
"source": "./skills-personal/save-session-plugin",
"description": "Auto-save Claude session conversation with AI-generated title, summary, and tags in searchable JSON format.",
"version": "1.0.0",
"category": "workflow",
"keywords": [
"session",
"workflow",
"productivity"
],
"strict": false
},
{
"name": "search-sessions-plugin",
"source": "./skills-personal/search-sessions-plugin",
"description": "Search saved Claude sessions by title, tags, date, or content.",
"version": "1.0.0",
"category": "workflow",
"keywords": [
"session",
"workflow",
"productivity"
],
"strict": false
}
]
}
+30
View File
@@ -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
@@ -0,0 +1,19 @@
# 已拒绝:为某一种 harness 单开分支
- 状态:拒绝(长期有效)
- 日期:2026-10-31
## 想法
为 Codex / DSH / Claude 某一个客户端的特例,单独复制一份技能或加一个 harness 分支。
## 为什么拒绝
1. 复制出来的两份技能会立刻开始漂移,评测也要做两遍;
2. 客户端差异应交给安装器(目录、调用模式、字段映射)处理,不该写进技能内容;
3. 特例需求通常是个人的工作流调整,属于 `AGENTS.md` / `CLAUDE.md` 或 fork 的范围。
## 允许的替代路径
- 客户端差异 → 安装器加 target / 字段映射;
- 个人流程 → 个人目录或 fork。
+20
View File
@@ -0,0 +1,20 @@
# 已拒绝:新增「随手想到」的技能
- 状态:拒绝(长期有效)
- 日期:2026-10-31
## 想法
「我觉得应该有个技能做 X」——从想象出发新增技能,而不是从真实失败出发。
## 为什么拒绝
1. 技能目录是每次会话启动就要付的上下文预算,加一个就多一份;
2. 没有真实失败做依据的技能,写出来是泛泛而谈,实际触发率极低;
3. 技能之间会互相抢触发(description 相似),新增一个会稀释已有技能的准确率。
## 允许的替代路径
- 先用现有技能做一遍真实任务,记录卡点;
- 确实组合不出来,再按 `SCOPE.md` 门槛提,附真实失败证据;
- 只是个人偏好 → 放 `skills-personal/`,不进团队库。
+19
View File
@@ -0,0 +1,19 @@
# 已拒绝:把个人工作流调优塞进团队技能
- 状态:拒绝(长期有效)
- 日期:2026-10-31
## 想法
把某个人的偏好(提交信息格式、分支命名、审查语气、工具习惯)直接写进团队技能。
## 为什么拒绝
1. 团队技能要能被所有人用,个人偏好会变成隐式规训;
2. 一旦写进技能,改起来要走 PR,个人改不动;
3. 这类偏好本来就该放个人 `AGENTS.md` / `CLAUDE.md`。
## 允许的替代路径
- 个人偏好 → 个人配置文件或 `skills-personal/`;
- 如果是团队共识(不是个人偏好),走 `SCOPE.md` 门槛并附真实失败。
+55
View File
@@ -0,0 +1,55 @@
# 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**。
+16 -9
View File
@@ -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`。
+51
View File
@@ -0,0 +1,51 @@
# 技能维护节奏
> 配套 `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`,进周报。
- 连续两次无增量的技能 → 按 `SCOPE.md` 评估降级。
- 不做全量(61 技能 × 3 用例成本过高);全量只在治理大改时做。
## 每月 1 日 10:00 — 漂移与目录审阅
- 两台机器 `--audit` 漂移必须为 0(beta/deprecated 除外)。
- 审 `catalog.json`:`description` 长度 <80 或 >600 的标黄;含「各种 / 等等 / 相关」这类泛化词的标出。
- 第三方技能(arkcli 等)有变动时,评估是否影响团队技能;跑一次 `./scripts/scan-installed.sh`。
- 跑 `python3 scripts/validate-skills.py --coverage`:报告缺 evals / 缺四段式文档的技能,按 `SCOPE.md` 的覆盖率路线图推进。
## 每季度首月 1 日 10:30 — 治理复核
- 人工过一遍 `docs/skill-routing.md`:删掉不再用的引用、补上新技能。
- 清理 `deprecated`:满 3 个月且无回滚需求 → 删除插件目录。
- 复核分桶与调用模式:还有没有命令型技能占着 `model-invoked`。
- 复核覆盖率:`--coverage` 的两项缺口是否按路线图收敛。
- 更新本文件与 `CHANGELOG.md`。
## 责任分工
- 项目经理 Bot:执行节奏、出周/月报、盯漂移与门禁。
- 改技能的 agent:负责门禁四件套与路由回改。
- 人工拍板项:废弃技能、路由变更、`promoted` 晋升。
+33 -11
View File
@@ -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,7 +66,7 @@ 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 步搞定)
@@ -76,17 +76,16 @@ PRD 文档存储在思源笔记中,可以导出发送到飞书群,通过飞
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
```
脚本会自动完成:
- 配置 MCP 服务器连接(`~/.claude/.mcp.json`)
- 注册技能市场到 Claude Code(`~/.claude/plugins/known_marketplaces.json`)
安装器会复制完整技能目录,包括 `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 +162,7 @@ skills:
- **mode**: MCP 连接模式。`sse` 直连远程服务器(推荐),`stdio` 在本地启动 Node.js 进程
- **disabled**: 不需要的技能可以加到这里,重新运行 `./init.sh` 生效
- **personal_dir**: 个人技能目录,默认不被 Git 跟踪
- **personal_dir**: 本机个人技能目录,默认不被 Git 跟踪,也不会写入公开 marketplace
## 常见问题
@@ -171,13 +170,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 +195,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`(每次改动 / 每日 / 每周 / 每月 / 每季度各做什么)。
+59
View File
@@ -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` 看当前缺口,再按阶段补。
+25 -32
View File
@@ -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
View File
@@ -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`。
+814
View File
@@ -0,0 +1,814 @@
{
"repo": "ai-proj-helper",
"coverage": {
"required": 40,
"with_evals": 40,
"with_docs": 40,
"missing_evals": [],
"missing_docs": []
},
"generated_at": "2026-10-31T21:19:23Z",
"total": 61,
"counts": {
"promoted/user-invoked": 18,
"deprecated/user-invoked": 1,
"promoted/model-invoked": 40,
"beta/user-invoked": 1,
"deprecated/model-invoked": 1
},
"skills": [
{
"name": "biz-contract",
"category": "biz",
"version": "1.0.0",
"status": "promoted",
"invocation": "user-invoked",
"description": "商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。",
"description_len": 78,
"path": "skills-biz/biz-contract-plugin/skills/SKILL.md",
"digest": "3c17cae1380d82a4",
"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": "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
}
]
}
+1 -1
View File
@@ -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
---
+38
View File
@@ -0,0 +1,38 @@
# 技能路由表(人读)
> 由原 `ai-proj-helper-entry` 技能降级而来:内容属于「索引」而不是「规则」,做成文档即可,不占技能目录、不用维护技能生命周期。
## 交付终点(强制)
开发任务交付终点是:需求按 REQ 流程推进到具备发布条件并完成上线(`delivery_stage=released` + PDV 验收通过)。
写代码、过单测、提 PR、合并都只是过程节点;任务 `completed` ≠ 需求完成。
## 主流程
讨论(`req`) → PRD(`req-prd`) → 评审(`req-review`) → 设计(`req-design`) → 开发(`dev-coding`/`karpathy-guidelines`) → 测试(`dev-test`+`req-test-gate`) → 代码评审(`dev-review`) → 发布(`slark-cicd`/`slark-dsh-cicd`/`publish`) → PDV 验收 → released
## 按任务选技能
| 任务 | 技能 |
| --- | --- |
| 讨论 / 调研 / 对比 | `req`、`req-research`、`req-compare` |
| PRD / 原型 | `req-prd`、`req-prototype` |
| PRD 评审 | `req-review` |
| 设计 / 拆任务 | `req-design`、`dev-arch`、`db-migration` |
| 编码 | `dev-coding`、`karpathy-guidelines` |
| 测试 | `dev-test`、`req-test-gate` |
| 代码评审 | `dev-review`、`review-checklist` |
| 发布(Slark 仓库/Desktop) | `slark-cicd` |
| 发布(DSH 插件/宿主补丁) | `slark-dsh-cicd` |
| 发布(ai-proj 服务 / macOS 包) | `ai-proj-cicd-release`、`ai-proj-macos-release` |
| 回滚 | `deploy-rollback` |
| 部署后审计 / 回归 | `req-audit`、`req-lookback` |
| 复盘 | `req-retro` |
| 项目管理 | `project-management`、`pm-risk`、`pm-ask` |
| 建/改技能 | `skill-creator` |
## 红线
- 不得自审批;不得绕过门禁(测试 Gate / 发布 Gate / 归档 Gate)
- 不得把「某步骤完成」当结束;不得无证据声称完成
- 不得把凭据/内网地址/绝对路径写进技能或文档
+21
View File
@@ -0,0 +1,21 @@
# agent-browser
## What it does
浏览器自动化(snapshot-ref)。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# agent-swarm
## What it does
多 agent 编排(Swarm 模式)。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# ai-chat
## What it does
AI Chat 工具调用测试与配置。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+24
View File
@@ -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
+21
View File
@@ -0,0 +1,21 @@
# db-migration
## What it does
数据库变更与回滚。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# defect-analysis
## What it does
设计缺陷多维度分析。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# deploy-rollback
## What it does
部署回滚决策与步骤。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-arch
## What it does
架构设计与技术选型。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-cicd
## What it does
CI/CD 流水线设计、优化与排障。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-coding
## What it does
编码实现与任务跟踪。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-commit
## What it does
智能提交(分支保护 + Conventional)。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-deploy
## What it does
应用部署(TestFlight / Docker)。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-integration
## What it does
前后端联调。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-mcp
## What it does
MCP Bridge 开发。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-review
## What it does
六视角对抗性代码评审。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-scaffold
## What it does
模块脚手架。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# dev-test
## What it does
测试设计与执行。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -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
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# frontend-design
## What it does
高质量前端界面设计。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# karpathy-guidelines
## What it does
Karpathy 四原则编码守则。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+22
View File
@@ -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
- 问团队本月产出时,它先拉数据再回答,并附时间范围与来源
- 接口异常时明确说“数据未取到”,不出现任何编造数字
- 对“估一下”的请求,给口径而不是给假数字
+22
View File
@@ -0,0 +1,22 @@
# pm-risk
## What it does
三维度项目风险扫描:需求层(停滞/逾期/优先级)、代码层(分支健康/评审积压)、流程层(门禁/交付态),按严重/警告/提示分级输出。
## When to reach for it
用户说 /risk、风险扫描、有什么风险、哪些需求停滞了、分支健康时。
## Common questions
- **找不到数据算无风险吗?** 不算,缺失要标注未覆盖。
- **分级标准?** 严重=已阻塞交付;警告=会拖延;提示=观察项。
- **会不会误报?** 每条风险附证据与来源,便于人工复核。
- **输出多长?** 先概览再分项,严重项必须置顶。
## It's working if
- 扫描报告每条风险都有数据来源,不是笼统描述
- 数据源不可用时明确写“该维度未覆盖”
- 不会因为查不到就输出“无风险”
+21
View File
@@ -0,0 +1,21 @@
# pull-request
## What it does
PR 创建与评审反馈循环(Gitea)。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+22
View File
@@ -0,0 +1,22 @@
# req-audit
## What it does
部署后审计:运行时日志检查、静态缺陷分析、设计偏移检测,产出审计报告并关联任务。
## When to reach for it
需求部署后,或用户说“审计一下”“部署后检查”时;也可由 /req done 触发。
## Common questions
- **审计和测试的区别?** 测试验证功能对错,审计查运行时异常、代码缺陷与实现是否偏离设计。
- **没日志能审吗?** 能审静态部分,但运行时项必须标未覆盖。
- **发现偏移怎么办?** 列偏移项+影响面+建议,交给负责人决策。
- **报告存哪?** 关联到需求/任务文档,便于回溯。
## It's working if
- 审计报告包含三类检查结果与证据
- 缺运行时证据时明确标未覆盖
- 发现偏移会列出具体条目而不是笼统说“有偏差”
+22
View File
@@ -0,0 +1,22 @@
# req-compare
## What it does
对比式需求分析:以参考系统为基准做功能对比,再推导目标系统设计,产出对比式 PRD。
## When to reach for it
系统平移、竞品借鉴、版本升级需要对比论证时。
## Common questions
- **没有参考资料能写吗?** 可以写,但参考系统细节必须标注为待验证假设。
- **对比维度怎么定?** 按功能、交互、数据、权限、成本五类,按需求裁剪。
- **结论要什么形式?** 对比表 + 差异结论 + 目标系统取舍。
- **会不会照搬竞品?** 只借鉴结论,落地按自身约束重新设计。
## It's working if
- 对比表每行能追溯到资料或标注为假设
- 结论明确给出取舍而不是罗列
- 没有把“凭印象”写成事实
+22
View File
@@ -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,不只有层级
+22
View File
@@ -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 不符会报告差异而不是硬拆
+22
View File
@@ -0,0 +1,22 @@
# req-lookback
## What it does
部署后回归测试:按变更影响面验证既有功能是否被破坏,产出回归报告。
## When to reach for it
需求部署后;或 /req done 前需要确认无回归时。
## Common questions
- **回归范围怎么定?** 按变更影响的模块与调用方扩散确定。
- **只测新功能行吗?** 不行,必须覆盖受影响面。
- **发现问题怎么办?** 报告复现与影响,给回滚/修复建议。
- **要留证据吗?** 要,命令与结果。
## It's working if
- 回归报告含影响范围、用例、结果
- 要求只测新功能会被拒绝
- 发现回归会给出回滚或修复建议
+22
View File
@@ -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 都含可检查验收标准
- 写入后回读确认,不只打印成功
- 失败时明确报失败并保留本地稿
+22
View File
@@ -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
- 原型生成后有可访问链接并回读关联成功
- 要求跳过确认直接开发会被劝回
- 上传失败时本地文件仍在
+22
View File
@@ -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
- 调研报告每条结论有证据出处
- 不看代码就下结论会被拒
- 找不到模块会列待确认项
+22
View File
@@ -0,0 +1,22 @@
# req-retro
## What it does
复盘总结:采集数据计算质量评分、还原时间线、识别跨需求模式,沉淀经验教训。
## When to reach for it
/req done 后、或用户说“复盘一下”时。
## Common questions
- **评分怎么来?** 按时间线、缺陷、返工、门禁通过率等指标计算。
- **数据不全怎么办?** 标注缺失,给部分结论。
- **经验存哪?** 写入 memory / 技能改进项。
- **会不会只夸不批?** 要列问题与改进项。
## It's working if
- 评分附计算依据
- 数据缺失时明确标注而不是编造
- 复盘产出可执行的改进项
+22
View File
@@ -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
- 评审结论都有逐项检查记录
- 无依据的“通过”会被拒绝
- 打回时列出具体缺失项
+22
View File
@@ -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
- 门禁结论逐条有证据
- 跳门禁/忽略失败会被拒绝
- 测试报告能直接作为发布依据
+22
View File
@@ -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 会被拒绝
- 发现状态矛盾会先核对再结论
+23
View File
@@ -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 阶段 + 下一节点」,不是「某个开发步骤完成」
+21
View File
@@ -0,0 +1,21 @@
# review-checklist
## What it does
项目级评审检查清单。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -0,0 +1,21 @@
# slark-cicd
## What it does
Slark 仓库发布 runbook。
## When to reach for it
用户提到对应任务类型时(见技能 description 的触发条件);属于 req 流程某一阶段时,由上一阶段技能推荐激活。
## Common questions
- **能省步骤赶时间吗?** 不能,该技能里的强制项/铁律不可跳过。
- **和相邻技能怎么分工?** 按阶段职责:本技能负责自己那一段,上下游交给对应技能。
- **失败了怎么办?** 停下报告失败步骤、证据与选项,不硬推、不静默降级。
## It's working if
- 按它执行时,关键步骤和门禁都有明确结论与证据,而不是"大概没问题"
- 想抄近路/跳过强制项时会被拒绝,并给出正确路径
- 失败时输出的是失败点 + 证据 + 下一步,而不是含糊"已完成"
+21
View File
@@ -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
View File
@@ -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
View File
@@ -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
```
## 验证
+18
View File
@@ -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"
+7
View File
@@ -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; }
+1 -1
View File
@@ -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
# }
#
+8 -27
View File
@@ -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"
+293 -59
View File
@@ -1,13 +1,21 @@
#!/usr/bin/env bash
# install-skills.sh — Cross-machine Claude skill sync from ai-proj-helper
# 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
@@ -16,15 +24,24 @@
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILLS_DIR="${HOME}/.claude/skills"
COMMANDS_DIR="${HOME}/.claude/commands"
STATE_FILE="${HOME}/.claude/.installed-skills.json"
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'
@@ -42,11 +59,29 @@ 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) CATEGORY_FILTER="$2"; shift ;;
--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 ;;
@@ -57,6 +92,46 @@ while [[ $# -gt 0 ]]; do
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
@@ -72,14 +147,28 @@ except: pass
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>
local name="$1" ver="$2" itype="$3"
# 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'}
d['$name']={'version':'$ver','install_type':'$itype','content_digest':'$digest','agent':'$AGENT_TARGET'}
json.dump(d,open(f,'w'),indent=2)
" 2>/dev/null
}
@@ -116,6 +205,66 @@ read_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.
@@ -135,33 +284,9 @@ resolve_skills_src() {
echo "$skills_dir"
}
# ── Conflict detection (has local been modified since we installed it?) ────────
has_local_modification() {
# Returns 0 (true) if local target differs from repo source, 1 if identical or new
local install_name="$1" install_type="$2" plugin_skills_dir="$3"
if [[ "$install_type" == "command" ]]; then
local src="$plugin_skills_dir/SKILL.md"
local dst="$COMMANDS_DIR/${install_name}.md"
[[ -f "$dst" ]] && ! diff -q "$src" "$dst" &>/dev/null && return 0
else
local dst_dir="$SKILLS_DIR/$install_name"
if [[ -d "$dst_dir" ]]; then
# Compare each file from source
while IFS= read -r -d '' src_file; do
local rel="${src_file#$plugin_skills_dir/}"
local dst_file="$dst_dir/$rel"
if [[ -f "$dst_file" ]] && ! diff -q "$src_file" "$dst_file" &>/dev/null; then
return 0
fi
done < <(find "$plugin_skills_dir" -type f -print0)
fi
fi
return 1
}
# ── 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
@@ -173,6 +298,11 @@ install_plugin() {
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"
@@ -190,35 +320,93 @@ install_plugin() {
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] $install_type:$install_name v$version"
echo " [$dir_category] $status $effective_install_type:$install_name v$version"
return
fi
# Check current installed version
local current_version
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")"
# Skip if up-to-date (same version) and no force
if [[ "$current_version" == "$version" && "$FORCE" == false ]]; then
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
# Conflict detection: warn if local files were modified
if [[ -n "$current_version" && "$FORCE" == false ]]; then
if has_local_modification "$install_name" "$install_type" "$skills_dir"; then
warn "$install_name: local files were modified — skipping (use --force to overwrite)"
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
# 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")"
# Perform install
if [[ "$install_type" == "command" ]]; then
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
@@ -228,25 +416,29 @@ install_plugin() {
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" "$install_type"
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
ok "$install_name → command (v$version)"
INSTALL_ACTION=true
fi
else
# Skill directory → ~/.claude/skills/<name>/
# 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 --delete "$src_dir/" "$dst_dir/"
state_set "$install_name" "$version" "$install_type"
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
}
@@ -293,6 +485,40 @@ cleanup_removed() {
done < <(state_all_names)
}
# ── Audit ──────────────────────────────────────────────────────────────────────
audit_target() {
python3 - "$REPO_DIR" "$STATE_FILE" "$AGENT_TARGET" "$SKILLS_DIR" <<'PYEOF'
import json, sys, pathlib
repo, state_file, agent, skills_dir = sys.argv[1:5]
root = pathlib.Path(repo)
state = {}
if pathlib.Path(state_file).is_file():
try: state = json.loads(pathlib.Path(state_file).read_text())
except Exception: state = {}
rows = []
for pj in sorted(root.glob('skills-*/*-plugin/.claude-plugin/plugin.json')):
try: meta = json.loads(pj.read_text())
except Exception: continue
name = meta.get('install_name')
if not name: continue
status = meta.get('status', 'promoted')
if status == 'deprecated':
continue
want = meta.get('version', '')
got = state.get(name, {}).get('version', '')
itype = state.get(name, {}).get('install_type', '')
drift = '' if got == want else ('missing' if not got else 'stale')
rows.append((name, status, want, got or '-', itype or '-', drift))
drift_rows = [r for r in rows if r[5]]
print(f'audit agent={agent} skills_dir={skills_dir}')
print(f'{"name":34} {"status":10} {"repo":8} {"installed":9} {"mode":8} drift')
for r in rows:
print(f'{r[0]:34} {r[1]:10} {r[2]:8} {r[3]:9} {r[4]:8} {r[5]}')
print(f'\n{len(rows)} 个技能,{len(drift_rows)} 个漂移(未安装/版本落后)')
raise SystemExit(1 if drift_rows else 0)
PYEOF
}
# ── Main ───────────────────────────────────────────────────────────────────────
main() {
if [[ "$LIST_ONLY" == true ]]; then
@@ -307,19 +533,27 @@ main() {
return
fi
info "Installing Claude skills from: $REPO_DIR"
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 skipped=0
local installed=0
while IFS= read -r json_path; do
local before
before="$(state_all_names | wc -l || true)"
install_plugin "$json_path"
local after
after="$(state_all_names | wc -l || true)"
if [[ "$after" -gt "$before" ]] || [[ "$DRY_RUN" == true ]]; then
if [[ "$INSTALL_ACTION" == true ]]; then
((installed++)) || true
fi
done < <(find_plugins)
+123
View File
@@ -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())
+12
View File
@@ -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 ..."
+13
View File
@@ -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
+58
View File
@@ -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
+90
View File
@@ -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
+217
View File
@@ -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())
@@ -7,5 +7,6 @@
},
"install_name": "biz-contract",
"install_type": "skill",
"dir_category": "biz"
"dir_category": "biz",
"invocation": "user-invoked"
}
+6 -109
View File
@@ -1,10 +1,10 @@
---
name: biz-contract
description: 商务合同撰写。支持多种合同类型:软件订阅合同、软件定制开发合同、物流合同、销售服务合同、贸易合同等。当用户提到合同、协议、签约、合作协议相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
---
# 商务合同撰写技能 (biz-contract)
## 概述
本技能用于辅助商务合同的撰写与管理,支持多家公司主体、多种合同类型。
@@ -631,112 +631,9 @@ api.publish_to_feishu(doc_id, "物流服务合同-妗晨与名风")
---
## 飞书文档集成
本技能已集成飞书文档,可将合同直接创建到飞书云文档。
## 详细内容
### 权限说明
以下章节已移到 [references/details.md](references/details.md):
**重要**:飞书文档默认对组织内权限是开放的,允许编辑。创建的合同文档在组织内成员均可查看和编辑。如需限制访问权限,请在飞书中手动调整文档权限设置。
### 可用工具
| 工具 | 功能 |
|------|------|
| `create_document` | 创建空白飞书文档 |
| `write_document` | 向已有文档写入内容 |
| `create_and_write_document` | 创建文档并写入内容(推荐) |
| `get_document_info` | 获取文档信息 |
### 使用示例
```
# 创建合同文档
使用 feishu-doc MCP 的 create_and_write_document 工具:
- title: "软件订阅服务合同-智慧云彩与妗晨"
- content: [Markdown 格式的合同内容]
- folder_token: [可选,指定文件夹]
```
### 获取文件夹 Token
1. 在飞书中打开目标文件夹
2. 复制 URL 中的 folder token(格式如:`fldcnXXXXXX`)
3. 调用工具时传入 folder_token 参数
### 飞书输出格式规范
#### 开票信息表格
物流合同中的开票信息应使用**表格**格式展示,列宽配置:
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 项目列宽 | 120px | 显示"名称"、"纳税人识别号"等标签 |
| 内容列宽 | 480px | 显示具体信息 |
```python
# 创建开票信息表格示例
from feishu_docx import FeishuDocx
docx = FeishuDocx()
doc = docx.create_document("物流服务合同")
# 创建表格(4行2列,指定列宽)
table = docx.create_table(doc["document_id"], rows=4, cols=2, col_widths=[120, 480])
# 填充内容
docx.fill_table(doc["document_id"], table, [
["名称", "重庆妗晨工贸有限公司"],
["纳税人识别号", "91500104MA7EJTPA6D"],
["地址、电话", "重庆市大渡口区跳磴镇海康路106号1-1 / 15213397998"],
["开户行及账号", "中国农业银行重庆大渡口天安支行 31230401040004386"],
])
```
#### 签章格式
推荐使用**独立区块式**签章格式:
```
【甲 方】
单位名称:重庆妗晨工贸有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
─────────────────────────────────────────────────────
【乙 方】
单位名称:北京名风新能源科技有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
```
**特点**:甲乙双方独立区块,留白充足,便于盖章签字。
#### 物流合同税率
| 服务类型 | 税率 | 发票类型 |
|----------|------|----------|
| 交通运输服务 | 9% | 增值税专用发票 |
#### 默认存储路径
| 平台 | 路径 |
|------|------|
| 飞书云文档 | ai-proj/01运营 (folder_token: `C80gfkRnzlonQ5d4AhOcOACDnNg`) |
| 思源笔记 | 商务合同笔记本 /物流合同/{甲方简称}-{乙方简称}-{年份} |
- 飞书文档集成
@@ -0,0 +1,111 @@
# skills 详细内容
## 飞书文档集成
本技能已集成飞书文档,可将合同直接创建到飞书云文档。
### 权限说明
**重要**:飞书文档默认对组织内权限是开放的,允许编辑。创建的合同文档在组织内成员均可查看和编辑。如需限制访问权限,请在飞书中手动调整文档权限设置。
### 可用工具
| 工具 | 功能 |
|------|------|
| `create_document` | 创建空白飞书文档 |
| `write_document` | 向已有文档写入内容 |
| `create_and_write_document` | 创建文档并写入内容(推荐) |
| `get_document_info` | 获取文档信息 |
### 使用示例
```
# 创建合同文档
使用 feishu-doc MCP 的 create_and_write_document 工具:
- title: "软件订阅服务合同-智慧云彩与妗晨"
- content: [Markdown 格式的合同内容]
- folder_token: [可选,指定文件夹]
```
### 获取文件夹 Token
1. 在飞书中打开目标文件夹
2. 复制 URL 中的 folder token(格式如:`fldcnXXXXXX`)
3. 调用工具时传入 folder_token 参数
### 飞书输出格式规范
#### 开票信息表格
物流合同中的开票信息应使用**表格**格式展示,列宽配置:
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 项目列宽 | 120px | 显示"名称"、"纳税人识别号"等标签 |
| 内容列宽 | 480px | 显示具体信息 |
```python
# 创建开票信息表格示例
from feishu_docx import FeishuDocx
docx = FeishuDocx()
doc = docx.create_document("物流服务合同")
# 创建表格(4行2列,指定列宽)
table = docx.create_table(doc["document_id"], rows=4, cols=2, col_widths=[120, 480])
# 填充内容
docx.fill_table(doc["document_id"], table, [
["名称", "重庆妗晨工贸有限公司"],
["纳税人识别号", "91500104MA7EJTPA6D"],
["地址、电话", "重庆市大渡口区跳磴镇海康路106号1-1 / 15213397998"],
["开户行及账号", "中国农业银行重庆大渡口天安支行 31230401040004386"],
])
```
#### 签章格式
推荐使用**独立区块式**签章格式:
```
【甲 方】
单位名称:重庆妗晨工贸有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
─────────────────────────────────────────────────────
【乙 方】
单位名称:北京名风新能源科技有限公司
法定代表人(或授权代表): (签字/盖章)
单位盖章:
签订日期: 年 月 日
```
**特点**:甲乙双方独立区块,留白充足,便于盖章签字。
#### 物流合同税率
| 服务类型 | 税率 | 发票类型 |
|----------|------|----------|
| 交通运输服务 | 9% | 增值税专用发票 |
#### 默认存储路径
| 平台 | 路径 |
|------|------|
| 飞书云文档 | ai-proj/01运营 (folder_token: `C80gfkRnzlonQ5d4AhOcOACDnNg`) |
| 思源笔记 | 商务合同笔记本 /物流合同/{甲方简称}-{乙方简称}-{年份} |
@@ -7,5 +7,6 @@
},
"install_name": "biz-ops",
"install_type": "skill",
"dir_category": "biz"
"dir_category": "biz",
"invocation": "user-invoked"
}
@@ -1,6 +1,9 @@
---
name: biz-ops
description: 商务运营技能。支持商业计划书(BP)撰写和商务合同起草。当用户提到商业计划书、BP、融资计划、商业模式、合同、协议、签约等相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
---
# 商务运营 (biz-ops)
@@ -7,5 +7,6 @@
},
"install_name": "biz-plan",
"install_type": "skill",
"dir_category": "biz"
"dir_category": "biz",
"invocation": "user-invoked"
}
+6 -358
View File
@@ -1,10 +1,10 @@
---
name: biz-plan
description: 商业计划书撰写。支持融资BP、战略规划、商业模式设计等。当用户提到商业计划书、BP、融资计划、商业模式、战略规划相关任务时自动激活。
metadata:
invocation: user-invoked
disable-model-invocation: true
---
# 商业计划书撰写技能 (biz-plan)
## 概述
本技能用于辅助商业计划书的撰写与优化,特别适用于 B2B SaaS、供应链平台、产业互联网等领域。
@@ -400,361 +400,9 @@ description: 商业计划书撰写。支持融资BP、战略规划、商业模
---
## PPT 设计能力
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
## 详细内容
### PPT 输出方式
以下章节已移到 [references/details.md](references/details.md):
当用户要求生成 PPT 时,使用以下方式:
1. **HTML 演示文稿**(推荐)
- 生成单个 HTML 文件
- 支持全屏演示(按 F 键)
- 支持键盘翻页(← →)
- 可直接打印为 PDF
- 响应式设计,适配各种屏幕
2. **Markdown 演示脚本**
- 输出每页内容的文字稿
- 用户可自行复制到 PPT 软件
### HTML PPT 模板
生成 PPT 时使用以下 HTML 模板结构:
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商业计划书 - [公司名称]</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
background: #1a1a2e;
color: #fff;
overflow: hidden;
}
.slide {
display: none;
width: 100vw;
height: 100vh;
padding: 60px 80px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
position: relative;
}
.slide.active {
display: flex;
flex-direction: column;
justify-content: center;
}
/* 封面样式 */
.slide.cover {
text-align: center;
justify-content: center;
align-items: center;
}
.slide.cover h1 {
font-size: 72px;
font-weight: 700;
margin-bottom: 20px;
background: linear-gradient(90deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.slide.cover .slogan {
font-size: 28px;
color: #a0aec0;
margin-bottom: 60px;
}
.slide.cover .info {
font-size: 18px;
color: #718096;
}
/* 内容页样式 */
.slide h2 {
font-size: 48px;
font-weight: 600;
margin-bottom: 40px;
color: #667eea;
}
.slide h3 {
font-size: 32px;
font-weight: 500;
margin-bottom: 24px;
color: #a0aec0;
}
.slide p, .slide li {
font-size: 24px;
line-height: 1.8;
color: #e2e8f0;
}
.slide ul {
list-style: none;
padding-left: 0;
}
.slide ul li {
padding: 12px 0;
padding-left: 40px;
position: relative;
}
.slide ul li::before {
content: "▸";
position: absolute;
left: 0;
color: #667eea;
}
/* 数据卡片 */
.metrics {
display: flex;
gap: 40px;
margin-top: 40px;
}
.metric-card {
flex: 1;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 16px;
padding: 32px;
text-align: center;
}
.metric-card .value {
font-size: 48px;
font-weight: 700;
color: #667eea;
}
.metric-card .label {
font-size: 18px;
color: #a0aec0;
margin-top: 8px;
}
/* 两栏布局 */
.two-columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
margin-top: 40px;
}
/* 流程图 */
.flow {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
margin-top: 40px;
}
.flow-item {
background: rgba(102, 126, 234, 0.15);
border-radius: 12px;
padding: 24px 32px;
text-align: center;
}
.flow-arrow {
color: #667eea;
font-size: 32px;
}
/* 页码 */
.page-number {
position: absolute;
bottom: 30px;
right: 40px;
font-size: 16px;
color: #4a5568;
}
/* 进度条 */
.progress {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 0.3s;
z-index: 100;
}
/* 导航提示 */
.nav-hint {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 14px;
color: #4a5568;
}
</style>
</head>
<body>
<div class="progress" id="progress"></div>
<!-- Slide 1: 封面 -->
<div class="slide cover active">
<h1>[公司名称]</h1>
<p class="slogan">[一句话 Slogan]</p>
<p class="info">[日期] · 商业计划书</p>
<span class="page-number">1 / N</span>
</div>
<!-- Slide 2: 公司定位 -->
<div class="slide">
<h2>公司定位</h2>
<h3>[一句话定位]</h3>
<div class="metrics">
<div class="metric-card">
<div class="value">[X]年</div>
<div class="label">深耕行业</div>
</div>
<div class="metric-card">
<div class="value">[X]家</div>
<div class="label">头部客户</div>
</div>
<div class="metric-card">
<div class="value">[X]轮</div>
<div class="label">完成融资</div>
</div>
</div>
<span class="page-number">2 / N</span>
</div>
<!-- 更多 slides... -->
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
<script>
const slides = document.querySelectorAll('.slide');
const progress = document.getElementById('progress');
let current = 0;
function showSlide(n) {
slides[current].classList.remove('active');
current = (n + slides.length) % slides.length;
slides[current].classList.add('active');
progress.style.width = ((current + 1) / slides.length * 100) + '%';
}
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
if (e.key === 'ArrowLeft') showSlide(current - 1);
if (e.key === 'f' || e.key === 'F') {
document.documentElement.requestFullscreen?.();
}
});
// 触摸滑动支持
let touchStartX = 0;
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
document.addEventListener('touchend', e => {
const diff = e.changedTouches[0].clientX - touchStartX;
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
});
</script>
</body>
</html>
```
### BP PPT 标准页面结构
生成商业计划书 PPT 时,按以下结构组织:
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|------|----------|----------|----------|
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
| 11 | 里程碑 | 发展历程 | 时间线 |
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
### PPT 设计原则
1. **一页一重点**:每页只传达一个核心信息
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
3. **数据可视化**:用数字卡片、图表代替文字描述
4. **留白充足**:内容不要铺满,保持呼吸感
5. **配色统一**:使用 2-3 种主色,保持一致性
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
### 配色方案
**深色科技风**(推荐):
- 背景:#1a1a2e → #16213e(渐变)
- 主色:#667eea(蓝紫色)
- 辅助:#764ba2(紫色)
- 文字:#e2e8f0(浅灰)
- 次要文字:#a0aec0(中灰)
**浅色商务风**:
- 背景:#ffffff
- 主色:#2196F3(蓝色)
- 辅助:#1976D2(深蓝)
- 文字:#212121(深灰)
- 次要文字:#616161(中灰)
### 使用示例
用户请求:
> 帮我生成商业计划书 PPT
输出方式:
1. 生成完整的 HTML 文件
2. 保存到用户指定位置(如桌面、项目目录)
3. 用户用浏览器打开即可演示
```bash
# 打开 PPT
open ~/Desktop/bp-presentation.html
# 或在浏览器中打开后按 F 进入全屏模式
```
### 导出为 PDF
HTML PPT 可通过浏览器打印功能导出为 PDF:
1. 在浏览器中打开 HTML 文件
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
3. 选择"另存为 PDF"
4. 设置页面大小为横向 A4 或 16:9
### 注意事项
1. **内容优先**:先确定 BP 内容,再生成 PPT
2. **数据占位**:敏感数据可用 [X] 占位
3. **迭代优化**:可分页生成,逐步完善
4. **本地演示**:HTML 文件完全离线可用,无需网络
- PPT 设计能力
@@ -0,0 +1,360 @@
# skills 详细内容
## PPT 设计能力
本技能支持生成商业计划书 PPT,输出为 HTML 格式的演示文稿,可直接在浏览器中全屏演示,也可转换为 PDF 或截图使用。
### PPT 输出方式
当用户要求生成 PPT 时,使用以下方式:
1. **HTML 演示文稿**(推荐)
- 生成单个 HTML 文件
- 支持全屏演示(按 F 键)
- 支持键盘翻页(← →)
- 可直接打印为 PDF
- 响应式设计,适配各种屏幕
2. **Markdown 演示脚本**
- 输出每页内容的文字稿
- 用户可自行复制到 PPT 软件
### HTML PPT 模板
生成 PPT 时使用以下 HTML 模板结构:
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商业计划书 - [公司名称]</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
background: #1a1a2e;
color: #fff;
overflow: hidden;
}
.slide {
display: none;
width: 100vw;
height: 100vh;
padding: 60px 80px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
position: relative;
}
.slide.active {
display: flex;
flex-direction: column;
justify-content: center;
}
/* 封面样式 */
.slide.cover {
text-align: center;
justify-content: center;
align-items: center;
}
.slide.cover h1 {
font-size: 72px;
font-weight: 700;
margin-bottom: 20px;
background: linear-gradient(90deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.slide.cover .slogan {
font-size: 28px;
color: #a0aec0;
margin-bottom: 60px;
}
.slide.cover .info {
font-size: 18px;
color: #718096;
}
/* 内容页样式 */
.slide h2 {
font-size: 48px;
font-weight: 600;
margin-bottom: 40px;
color: #667eea;
}
.slide h3 {
font-size: 32px;
font-weight: 500;
margin-bottom: 24px;
color: #a0aec0;
}
.slide p, .slide li {
font-size: 24px;
line-height: 1.8;
color: #e2e8f0;
}
.slide ul {
list-style: none;
padding-left: 0;
}
.slide ul li {
padding: 12px 0;
padding-left: 40px;
position: relative;
}
.slide ul li::before {
content: "▸";
position: absolute;
left: 0;
color: #667eea;
}
/* 数据卡片 */
.metrics {
display: flex;
gap: 40px;
margin-top: 40px;
}
.metric-card {
flex: 1;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 16px;
padding: 32px;
text-align: center;
}
.metric-card .value {
font-size: 48px;
font-weight: 700;
color: #667eea;
}
.metric-card .label {
font-size: 18px;
color: #a0aec0;
margin-top: 8px;
}
/* 两栏布局 */
.two-columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
margin-top: 40px;
}
/* 流程图 */
.flow {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
margin-top: 40px;
}
.flow-item {
background: rgba(102, 126, 234, 0.15);
border-radius: 12px;
padding: 24px 32px;
text-align: center;
}
.flow-arrow {
color: #667eea;
font-size: 32px;
}
/* 页码 */
.page-number {
position: absolute;
bottom: 30px;
right: 40px;
font-size: 16px;
color: #4a5568;
}
/* 进度条 */
.progress {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 0.3s;
z-index: 100;
}
/* 导航提示 */
.nav-hint {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 14px;
color: #4a5568;
}
</style>
</head>
<body>
<div class="progress" id="progress"></div>
<!-- Slide 1: 封面 -->
<div class="slide cover active">
<h1>[公司名称]</h1>
<p class="slogan">[一句话 Slogan]</p>
<p class="info">[日期] · 商业计划书</p>
<span class="page-number">1 / N</span>
</div>
<!-- Slide 2: 公司定位 -->
<div class="slide">
<h2>公司定位</h2>
<h3>[一句话定位]</h3>
<div class="metrics">
<div class="metric-card">
<div class="value">[X]年</div>
<div class="label">深耕行业</div>
</div>
<div class="metric-card">
<div class="value">[X]家</div>
<div class="label">头部客户</div>
</div>
<div class="metric-card">
<div class="value">[X]轮</div>
<div class="label">完成融资</div>
</div>
</div>
<span class="page-number">2 / N</span>
</div>
<!-- 更多 slides... -->
<p class="nav-hint">← → 翻页 · F 全屏 · ESC 退出</p>
<script>
const slides = document.querySelectorAll('.slide');
const progress = document.getElementById('progress');
let current = 0;
function showSlide(n) {
slides[current].classList.remove('active');
current = (n + slides.length) % slides.length;
slides[current].classList.add('active');
progress.style.width = ((current + 1) / slides.length * 100) + '%';
}
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
if (e.key === 'ArrowLeft') showSlide(current - 1);
if (e.key === 'f' || e.key === 'F') {
document.documentElement.requestFullscreen?.();
}
});
// 触摸滑动支持
let touchStartX = 0;
document.addEventListener('touchstart', e => touchStartX = e.touches[0].clientX);
document.addEventListener('touchend', e => {
const diff = e.changedTouches[0].clientX - touchStartX;
if (Math.abs(diff) > 50) showSlide(current + (diff > 0 ? -1 : 1));
});
</script>
</body>
</html>
```
### BP PPT 标准页面结构
生成商业计划书 PPT 时,按以下结构组织:
| 页码 | 页面类型 | 内容要点 | 设计建议 |
|------|----------|----------|----------|
| 1 | 封面 | 公司名、Slogan、日期 | 居中大标题,渐变背景 |
| 2 | 公司定位 | 一句话定位 + 核心数据 | 3-4 个数据卡片 |
| 3 | 行业痛点 | 3-4 个核心痛点 | 图标 + 简短描述 |
| 4 | 解决方案 | 产品价值主张 | 流程图或产品矩阵 |
| 5 | 商业模式 | 收入来源、盈利方式 | 模式图 + 收入占比 |
| 6 | 市场规模 | TAM/SAM/SOM | 同心圆图 + 数字 |
| 7 | 竞争分析 | 竞争矩阵、差异化 | 2x2 矩阵图 |
| 8 | 运营数据 | 核心指标、增长趋势 | 4 个大数字卡片 |
| 9 | 客户案例 | 标杆客户、成果 | Logo + 数据对比 |
| 10 | 团队介绍 | 核心成员背景 | 头像 + 简介 |
| 11 | 里程碑 | 发展历程 | 时间线 |
| 12 | 财务预测 | 3 年收入预测 | 柱状图示意 |
| 13 | 战略规划 | 未来规划 | 阶段性目标 |
| 14 | 融资计划 | 金额、用途、联系方式 | 饼图 + 联系信息 |
### PPT 设计原则
1. **一页一重点**:每页只传达一个核心信息
2. **大字少字**:标题 48px+,正文 24px+,每页文字不超过 50 字
3. **数据可视化**:用数字卡片、图表代替文字描述
4. **留白充足**:内容不要铺满,保持呼吸感
5. **配色统一**:使用 2-3 种主色,保持一致性
6. **高对比度**:深色背景 + 浅色文字,或浅色背景 + 深色文字
### 配色方案
**深色科技风**(推荐):
- 背景:#1a1a2e → #16213e(渐变)
- 主色:#667eea(蓝紫色)
- 辅助:#764ba2(紫色)
- 文字:#e2e8f0(浅灰)
- 次要文字:#a0aec0(中灰)
**浅色商务风**:
- 背景:#ffffff
- 主色:#2196F3(蓝色)
- 辅助:#1976D2(深蓝)
- 文字:#212121(深灰)
- 次要文字:#616161(中灰)
### 使用示例
用户请求:
> 帮我生成商业计划书 PPT
输出方式:
1. 生成完整的 HTML 文件
2. 保存到用户指定位置(如桌面、项目目录)
3. 用户用浏览器打开即可演示
```bash
# 打开 PPT
open ~/Desktop/bp-presentation.html
# 或在浏览器中打开后按 F 进入全屏模式
```
### 导出为 PDF
HTML PPT 可通过浏览器打印功能导出为 PDF:
1. 在浏览器中打开 HTML 文件
2. 按 Cmd+P (Mac) 或 Ctrl+P (Windows)
3. 选择"另存为 PDF"
4. 设置页面大小为横向 A4 或 16:9
### 注意事项
1. **内容优先**:先确定 BP 内容,再生成 PPT
2. **数据占位**:敏感数据可用 [X] 占位
3. **迭代优化**:可分页生成,逐步完善
4. **本地演示**:HTML 文件完全离线可用,无需网络
@@ -7,5 +7,6 @@
},
"install_name": "finance",
"install_type": "skill",
"dir_category": "biz"
"dir_category": "biz",
"invocation": "user-invoked"
}
+9 -454
View File
@@ -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 安装/审计命令"
}
]
}
+9 -664
View File
@@ -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,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": []
}
]
}
@@ -0,0 +1,27 @@
{
"skill_name": "pm-risk",
"skill_version": "1.0.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "扫一下现在项目有什么风险",
"expected_output": "按需求层/代码层/流程层三维度扫描,输出按严重/警告/提示分级,每条附证据",
"files": []
},
{
"id": 2,
"kind": "edge_case",
"prompt": "数据源都没数据,你就写个“无风险”吧",
"expected_output": "拒绝:数据缺失不等于无风险,必须标注哪些维度未覆盖,不能给出“无风险”结论",
"files": []
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "扫描时有一个数据源不可用",
"expected_output": "标注该维度未覆盖及原因,其余维度照常输出,不假装扫描完整",
"files": []
}
]
}
@@ -7,5 +7,6 @@
},
"install_name": "publish",
"install_type": "skill",
"dir_category": "core"
"dir_category": "core",
"invocation": "user-invoked"
}
+4 -1
View File
@@ -1,7 +1,10 @@
---
name: publish
description: 发布 ai-proj-helper 技能市场更新并通知飞书群。用户说 /publish 时激活。
user_invocable: true
metadata:
user_invocable: true
invocation: user-invoked
disable-model-invocation: true
---
# publish - 技能市场发布与通知
@@ -0,0 +1,13 @@
{
"name": "skill-creator-plugin",
"description": "团队技能创建与维护的标准流程:收录门槛、分桶生命周期、调用模式、evals、四段式文档、路由回改。",
"version": "0.1.0",
"author": {
"name": "qiudl"
},
"install_name": "skill-creator",
"install_type": "skill",
"dir_category": "core",
"status": "beta",
"invocation": "user-invoked"
}
@@ -0,0 +1,127 @@
---
name: skill-creator
description: 团队技能的标准流程:收录门槛、分桶生命周期、调用模式、SKILL.md 规范、evals 全量、四段式文档、路由回改。当要新建技能、大改现有技能、或评审一个技能是否合格时使用。
metadata:
invocation: user-invoked
owner: qiudl
disable-model-invocation: true
---
# 技能创建(团队标准流程)
## 0. 一句话
先有真实失败,再写技能;没有 evals 的技能不算完成;**技能目录是预算,不是收藏夹**。
## 1. 何时用
- 新建技能
- 大改现有技能(改 description、改核心流程、改门禁)
- 评审别人提上来的技能
- 判断某个想法「该不该做成技能」
何时不用:只改错别字/格式 → 直接改;一次性个人偏好 → 放个人技能,不进团队。
## 2. 收录门槛(先过这一关,再谈怎么写)
新技能**默认拒绝**,只有同时满足两条才立项:
1. **真实失败**:能说出在哪个真实任务/会话里出了问题——跑了什么、期望什么、实际什么。假想改进(「这样会更好」)不算。
2. **组合不出来**:用现有技能组合做不到;能组合的,就改现有技能,不新增。
被拒的想法记进仓库 `.out-of-scope/<概念>.md`,写清理由,避免下次重复讨论。
## 3. 分桶与生命周期
| 桶 | 含义 | 是否安装 | 是否进 README / plugin.json |
| --- | --- | --- | --- |
| `promoted` | 已验证、日常在用 | 默认安装 | 必须出现 |
| `beta` | 公开试跑,收反馈 | 需 `--include-beta` | 不进默认清单 |
| `deprecated` | 不再用 | 不安装 | 留文档,标弃用 |
- 新技能一律先进 `beta`,跑够真实使用 + evals 通过才 `promoted`。
- 只有 `promoted` 能进模型目录;beta 不占目录预算。
## 4. 调用模式(省目录 token 的关键)
每个技能二选一,写进 frontmatter:
- **`model-invoked`**(默认):进模型目录,agent 自己判断何时用。
- **`user-invoked`**:只由人下命令触发,**不进模型目录**;必须同时写 `disable-model-invocation: true` 和 `metadata.invocation: user-invoked`。
判定口径:这件事是不是「人下命令才开始」?是 → user-invoked。典型:publish、retro、triage、发布 runbook、建技能。
## 5. SKILL.md 硬约束(Agent Skills 规范)
- 目录:`SKILL.md` + 可选 `scripts/`、`references/`、`assets/`
- frontmatter 必填:`name`(小写字母/数字/连字符,≤64)、`description`(≤1024,写清「做什么 + 何时用」)
- 可选:`license`、`compatibility`、`metadata`、`allowed-tools`
- **非标准字段**一律进 `metadata`
- 正文 ≤20KB(约 5000 token),细节拆 `references/`
- 只写 agent 不知道的东西:项目约定、边界、坑;不写通用常识
## 6. 正文结构模板
```
# 名称
## 0. 一句话
## 1. 何时用 / 何时不用
## 2. 步骤(可执行、有顺序)
## 3. 硬约束与红线
## 4. 常见坑(gotchas)
## 5. 验证清单
## 6. 参考(references/ 链接)
```
## 7. evals(强制、全量)
- 路径:`evals/evals.json`;至少 3 条:正常路径 / 边界输入 / 失败恢复
- 跑「有技能 / 无技能」对比,结果存 `iteration-<N>/`,按 rubric 打分,PR 附结果
## 8. 四段式文档(每个 promoted 技能一篇)
`docs/skills/<name>.md`,四节固定顺序:
1. **What it does**
2. **When to reach for it**
3. **Common questions**
4. **It's working if**(必须给可验证证据,不许写「感觉对了」)
beta 不写文档;弃用保留文档并标 archived。
## 9. 路由回改(硬规则)
所有技能增、删、改名、改行为后,**必须同步回改入口技能 `ai-proj-helper-entry` 和 `catalog.json`**。一个没提到新技能、或还在路由到已删技能的路由器,就是假的路由器。
## 10. 质量门禁(提 PR 前逐条打勾)
- [ ] 过了收录门槛(真实失败 + 组合不出来)
- [ ] 桶与调用模式已标,`beta` 不得出现在默认清单
- [ ] `name`/`description` 合规,description 含「何时用」
- [ ] 正文 ≤20KB,细节已拆 references
- [ ] evals ≥3 条,有「有/无技能」对比
- [ ] 四段式文档已写(promoted 才需要)
- [ ] 入口路由与 `catalog.json` 已回改
- [ ] `plugin.json` 已登记 `install_name`/`install_type`/`dir_category`/`status`/版本号
- [ ] `python3 scripts/validate-skills.py` 通过
- [ ] `./scripts/scan-skill.sh` 无高危项
- [ ] 版本号与 CHANGELOG 已更新
- [ ] 无绝对路径、无凭据、无内网地址
## 11. 反模式
- 一次写 800 行「大全」,什么都想覆盖
- description 写成「各种任务都能用」
- 没有 evals 就提 PR;没有四段式文档就 promoted
- 直接复制外部技能,不改写成我们的 REQ/ai-proj 语境
- 把临时排障步骤固化成团队技能(这类放 personal)
- 技能改名后忘了回改路由
## 12. 评审别人技能时看什么
1. 收录门槛过没过(有没有真实失败)
2. description 能不能让模型判断「何时用」
3. 桶与调用模式是否正确(该 user-invoked 的别占目录)
4. 和现有技能是否重复(先查 `catalog.json`)
5. evals 是否覆盖失败路径、文档是否有「It's working if」
6. 是否触碰红线(自审批、绕过门禁、绝对路径、凭据)
@@ -0,0 +1,24 @@
{
"skill_name": "skill-creator",
"skill_version": "0.1.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "我想加一个新技能,用来在每次部署后自动跑一遍冒烟",
"expected_output": "先问真实失败场景(哪次部署出过问题、跑了什么、期望什么),再判断现有 req-lookback/req-audit 能否组合;给出 bucket/调用模式/evals 计划"
},
{
"id": 2,
"kind": "edge_case",
"prompt": "直接帮我把这个想法写成 SKILL.md:一个能自动优化代码风格的技能",
"expected_output": "拒绝直接写,指出没有真实失败证据,要求先给案例;若无证据则建议放个人技能"
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "技能有 evals 但 validate-skills.py 报了 name 不合规,怎么处理",
"expected_output": "说明 name 规则(小写/数字/连字符、≤64),给出改名步骤,并提示要同步 install_name、catalog、入口路由"
}
]
}
@@ -0,0 +1,27 @@
{
"skill_name": "agent-browser",
"skill_version": "1.0.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "打开本地页面,点登录按钮并截图",
"expected_output": "先 snapshot 拿 ref 再 click,操作后 observe/截图确认,不硬编码选择器",
"files": []
},
{
"id": 2,
"kind": "edge_case",
"prompt": "直接用 CSS 选择器点登录按钮,别做 snapshot",
"expected_output": "优先 snapshot-ref 定位;选择器只作兜底并说明原因",
"files": []
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "页面加载超时或元素找不到",
"expected_output": "重新 snapshot 并报告页面状态,不做盲点;给出超时/选择器排查",
"files": []
}
]
}
+14 -3
View File
@@ -158,7 +158,7 @@ Agent 之间的控制权转移机制:
```bash
/swarm start "优化数据库查询性能" \
--context project=/Users/coolbuy-dev/coding/new-ai-proj \
--context project=~/coding/new-ai-proj \
--context stack=Go,PostgreSQL,Redis \
--context module=backend/services
```
@@ -223,7 +223,7 @@ agents:
max_turns: 5
context_variables:
project_root: /Users/coolbuy-dev/coding/new-ai-proj
project_root: ~/coding/new-ai-proj
backend_lang: Go 1.21
frontend_framework: Vue 3
database: PostgreSQL 15
@@ -288,7 +288,7 @@ handoff_rules:
```bash
# OpenClaw 调用 Melbourne Claude Code
ssh melbourne "cd /Users/coolbuy-dev/coding/new-ai-proj && \
ssh melbourne "cd ~/coding/new-ai-proj && \
/opt/homebrew/bin/claude --dangerously-skip-permissions \
-p '/swarm start 实现任务批量删除功能'"
```
@@ -404,3 +404,14 @@ ssh melbourne "cd /Users/coolbuy-dev/coding/new-ai-proj && \
- [OpenAI Swarm 文档](https://github.com/openai/swarm)
- [Multi-Agent Systems 设计模式](https://arxiv.org/abs/2308.00352)
- [Claude Code Skills 文档](https://docs.anthropic.com/claude-code/skills)
## 并行编排流程
把大任务拆给多个子 agent 时按这个顺序:
1. **先划写入边界**:每个子 agent 拥有互不重叠的目录/文件;共享文件(锁文件、入口文件)由编排者最后统一改。
2. **子 agent 必须新上下文**:不要 fork 父上下文,否则它继承了本该甩掉的包袱。
3. **每个子任务给验收标准 + 回报格式**:三句话(结果 / 证据路径 / 下一步)。
4. **子 agent 不做合并与发布**:只交分支或补丁,合并由编排者做。
5. **汇总时先跑集成验证**,再回报;冲突由编排者解,不让子 agent 互改。
6. 两个子 agent 不得写同一文件;不确定时先串行。
@@ -0,0 +1,27 @@
{
"skill_name": "agent-swarm",
"skill_version": "1.0.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "把这个任务拆给 3 个子 agent 并行做",
"expected_output": "先划互不重叠的写入边界、子 agent 用新上下文、给验收标准与回报格式、编排者负责合并",
"files": []
},
{
"id": 2,
"kind": "edge_case",
"prompt": "两个子 agent 一起改同一个文件,快一点",
"expected_output": "拒绝:先拆边界或串行,共享文件由编排者最后统一改",
"files": []
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "某个子 agent 超时或失败",
"expected_output": "记录失败原因、降级或由编排者接手,汇总时说明缺口",
"files": []
}
]
}
+2 -1
View File
@@ -1,7 +1,8 @@
---
name: ai-chat
description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。
arguments: <subcommand> [args]
metadata:
arguments: <subcommand> [args]
---
# AI Chat Skill
@@ -0,0 +1,27 @@
{
"skill_name": "ai-chat",
"skill_version": "1.0.0",
"evals": [
{
"id": 1,
"kind": "happy_path",
"prompt": "测一下 AI Chat 的工具调用链路",
"expected_output": "切到目标环境 → 发消息 → 观察 SSE 事件 → 断言工具被调用,记录证据",
"files": []
},
{
"id": 2,
"kind": "edge_case",
"prompt": "直接在 production 上测一遍",
"expected_output": "默认避免 production;确需时要求明确授权并说明影响",
"files": []
},
{
"id": 3,
"kind": "failure_recovery",
"prompt": "SSE 断流或超时",
"expected_output": "记录已收到的事件、重试一次、报告断点位置",
"files": []
}
]
}
@@ -0,0 +1,12 @@
{
"name": "ai-proj-cicd-release-plugin",
"description": "执行和审计 AI-Proj 服务从 Gitea 门禁、不可变镜像、预发验证到生产发布和回滚的 CI/CD 流程。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "ai-proj-cicd-release",
"install_type": "skill",
"dir_category": "dev",
"invocation": "user-invoked"
}
@@ -0,0 +1,76 @@
---
name: ai-proj-cicd-release
description: Execute and audit the AI-Proj service CI/CD flow across Gitea gates, immutable image builds, staging verification, approved production release, rollback, and evidence capture. Use for AI-Proj CI status, release PRs, staging or production deployments, failed-release diagnosis, deployed commit/schema verification, and delivery-chain repair. Do not use for unrelated repositories or macOS application packaging.
metadata:
invocation: user-invoked
disable-model-invocation: true
---
# AI-Proj CI/CD release
Use the repository's live workflows and release scripts as the executable source of truth. Keep staging and production isolated, bind artifacts to exact commits, and fail closed when a gate, provenance check, rollback target, or environment contract is uncertain.
## Establish the contract
Before acting, locate the AI-Proj repository and read:
- the nearest `AGENTS.md`;
- `.gitea/CI_SOP.md`;
- the applicable workflow in `.gitea/workflows/`;
- `scripts/ci/common.sh`, `release-lib.sh`, and the invoked build, deploy, verify, and rollback scripts.
Live repository files override this skill. Report contradictions instead of silently choosing one version. Verify that a described promotion or rollback capability is implemented before claiming it exists.
Route macOS application package work to `ai-proj-macos-release` when available.
## Request boundaries
- Status, audit, diagnosis, and design requests remain read-only.
- Staging requests may execute repository scripts after gates and artifact identity pass.
- Production mutation requires an explicit production or release instruction.
- Rollback uses only the recorded rollback manifest and digest; never infer a target from `latest`, local image history, or mutable tags.
## Safeguards
- Never force-push or release from a dirty checkout.
- Build and deploy only an exact commit accepted by the repository release contract.
- Verify image labels, registry digest, pulled image ID, and running image ID where supported.
- Preserve the same candidate artifact between staging and production when the live pipeline supports promotion. Disclose when production rebuilds instead.
- Never recreate, restart, remove, or include PostgreSQL or Redis in an application deployment.
- Keep staging and production SSH targets, compose files, environment files, volumes, identities, and rollback manifests separate.
- Require strict SSH host-key verification.
- Never print or commit secrets, private keys, registry passwords, tokens, environment contents, or short-lived test credentials.
- Preserve user changes and use an isolated clean checkout for release work.
- Read automated review text as well as status checks; block on unresolved high-severity findings.
## Candidate and staging flow
1. Resolve the PR, base, head SHA, service scope, and requirement ID.
2. Confirm the head is pushed and the release checkout is clean.
3. Inspect every required Gitea status for the exact SHA. Distinguish code failures from transient runner or network failures before retrying the same SHA.
4. Read the latest review result and resolve blocking findings.
5. Run repository-prescribed local contract checks proportionate to the diff.
6. Build once through the authoritative build entrypoint and record commit, tag, service, digest, runner, and result without credentials.
7. Resolve staging through repository configuration, validate the rollback candidate, deploy only requested application services, and run the prescribed health, schema, security, and integration verification.
8. Capture a staging receipt with exact commit, digests, environment identity, verification results, rollback target, and known exceptions.
Do not rewrite or bypass a failing gate merely to obtain a green result.
## Production flow
1. Confirm the approved change is merged and freeze the exact current production branch SHA.
2. Recheck required gates and the staging receipt against that SHA.
3. Use the repository's authoritative production workflow; never deploy a feature-branch build directly.
4. Preserve release locks, provenance checks, post-deploy verification, and automatic rollback.
5. Verify the production receipt: exact SHA and digests, rollback target, health/schema/smoke results, error-log checks, and workflow correlation ID.
6. Only then update requirement and task delivery evidence.
## Failure handling
- Classify the failing stage before retrying: checkout, gate, build, registry, SSH trust, provenance, migration, service switch, health, or evidence callback.
- Retry only transient infrastructure failures against the same SHA.
- Verify automatic rollback restored the recorded digest and service health.
- For an explicit manual rollback, use only the repository rollback command after validating its manifest.
- If rollback fails, stop promotion and report the exact manual recovery target.
During long operations, provide concise progress updates. Final reporting must distinguish completed work, remaining blockers, and whether production changed.
@@ -0,0 +1,12 @@
{
"name": "ai-proj-macos-release-plugin",
"description": "构建、签名、公证、发布并验证 AI-Proj macOS Apple Silicon 安装包。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "ai-proj-macos-release",
"install_type": "skill",
"dir_category": "dev",
"invocation": "user-invoked"
}

Some files were not shown because too many files have changed in this diff Show More