Author SHA1 Message Date
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
qiudl bcea648e3c merge: resolve conflict in req-prototype SKILL.md, keep iframe display rule from main 2026-04-21 09:20:55 +09:30
qiudlandClaude Sonnet 4.6 e3513f137b feat(req-prd): PRD 模板新增验收标准章节强制含 VP 三件套(REQ-20260421-0002)
- 模板第7节新增「验收标准」章节,含 VP-Data/VP-Steps/VP-Pass 完整示例
- 检查清单「验收标准可测试」更新说明要求 VP 三件套
- 原第7节「风险评估」顺移为第8节

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 09:19:08 +09:30
qiudlandClaude Sonnet 4.6 011916ceb9 feat(req-review): 新增 VP 三件套强制检查(REQ-20260421-0002)
- 结构检查表新增「VP 三件套」必须项
- 新增「VP 三件套强制检查」章节,含四项检查指标和驳回条件
- 常见驳回原因更新:验收不明包含 VP 缺失场景

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 09:17:11 +09:30
dongliangandClaude Sonnet 4.6 48b792fb5a feat(req-prototype): 原型展示必须用 iframe,禁止图片方式 (REQ-20260420-0031)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 01:07:10 +09:30
qiudlandClaude Sonnet 4.6 2ab0a61eb9 fix(install): handle nested skills/ subdirectory (e.g. dev-test)
resolve_skills_src() detects when SKILL.md is one level deeper than skills/
and uses that subdirectory as rsync source instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 00:00:01 +09:30
qiudlandClaude Sonnet 4.6 de25f096e7 feat(sync): add install-skills.sh + install metadata to all 62 plugins
- Add install_name, install_type, dir_category fields to all 62 plugin.json files
  to resolve name-mapping and skill-vs-command routing issues
- Add install-skills.sh: idempotent cross-machine skill sync script
  - Routes skill→~/.claude/skills/<name>/, command→~/.claude/commands/<name>.md
  - rsync full skills/ directory (preserves multi-file skills like dev-test, req-deploy)
  - State file ~/.claude/.installed-skills.json tracks installed versions
  - Conflict detection: warns before overwriting locally modified files
  - --dry-run, --category, --force, --cleanup, --list flags
- Add 9 new plugins migrated from local ~/.claude (agent-swarm, ai-chat,
  defect-analysis, executing-plans, finishing-branch, frontend-design,
  req-audit, req-lookback, req-retro)
- Add update-plugin-meta.py helper used to bulk-update plugin.json
- Fix siyuan SKILL.md: remove hardcoded server credentials, use env vars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 23:55:26 +09:30
qiudlandClaude Sonnet 4.6 84d4e35a42 feat(req-prototype): 新增 HTML 上传模式,版本升至 v2.0.0
- 添加 /req prototype upload 子命令(推荐模式)
- 含完整 curl 上传流程、HTML 设计规范及模板
- 更新错误处理与版本管理说明
- marketplace 更新(49 个插件)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 18:06:32 +09:30
qiudl e5805cbb51 feat: P1-10/12/13/14 风险扫描 + 粒度判断 + Issue 集成 + PRD 校验 (REQ-20260416-0017)
P1-10: pm-risk skill — 三维度风险扫描
  需求层: 停滞/草稿滞留/开发无提交/测试超期/批准未开发
  代码层: 未合并分支/频繁修复文件/提交频率下降
  流程层: 跳过评审/PR 无 review/测试门禁跳过/直接推 main
  三级风险: 🔴 严重 / 🟡 警告 / 🔵 提示

P1-12: req-prd 需求粒度 AI 判断
  创建前启发式检查:标题过宽建议拆分、过窄建议合并或改 task
  粒度参考表 + 已有需求扩展决策表 + 前后端拆分规则

P1-13: dev-commit issue 集成规范
  分支名 -iN 后缀传递 issue 编号
  commit message 自动追加 closes #N

P1-14: hooks/validate-prd.sh — PRD 章节校验
  PostToolUse hook 自动检查 10 个必需章节
  缺失时给出明确提示

marketplace: 48 → 49 plugins (新增 pm-risk-plugin)
2026-04-19 13:33:26 +09:30
qiudl 79c4e55719 feat: P1-8 客户场景章节 + P1-9 pm-ask skill (REQ-20260416-0017)
P1-8: req-prd 模板增加 "1.4 客户原始诉求" 章节
  - 强制保留客户原话,不做 AI 加工转述
  - 标注提出人/时间/出处
  - 补充"客户原话原则"说明
  借鉴 devflow-claude requirement-template.md 的"1.3 客户场景"

P1-9: 新 skill pm-ask-plugin
  - 基于 ai-proj MCP + git log 真实数据的项目问答
  - 铁律:禁止编造,必须引用 REQ-XXX/commit-sha/文件路径
  - 数据不足时明确说明缺失
  - 区分事实和推测(⚠️ 推测:前缀)
  - 受众适配(高层/客户/内部/新人)
  借鉴 devflow-claude /pm:ask 命令

marketplace: 47 → 48 plugins
2026-04-16 23:04:54 +09:30
qiudl 23ea8fdca5 feat: 融合 devflow-claude P0 批机制 (REQ-20260416-0017)
P0-1: SessionStart Hook — hooks/session-context.sh
  从分支名解析 REQ-ID,调 MCP API 查询需求详情注入 system-reminder

P0-2: PreToolUse Hook — hooks/pre-tool-confirm.sh
  拦截生产推送、force push、docker prod 容器操作、git reset --hard 等

P0-3: Release Draft 闸门设计文档 — docs/design/release-draft-gate.md
  完整架构 + 渐进式落地路径(拆 7 个子任务延后)
  附最小可用脚本 hooks/release-draft.sh 创建 Gitea draft release

P0-4: Memory 隔离规则 — 写入 req-prd / req-design / req-workflow
  禁止 auto-memory 污染模板产出物(章节结构、字段定义、文档结构)

P0-5: CLAUDE.md 架构检查 + 架构片段库
  dev-coding skill 执行前检查架构关键词
  新增 templates/claude-md-snippets/ 含 Go+Gin / React+AntD / Vue+Element /
  MCP+TS / generic 五套骨架

P0-6: /commit 分支保护自动化 — 新 skill dev-commit-plugin
  保护分支自动建功能分支 + Conventional Commits + REQ-XXX 自动关联

安装:
  bash hooks/install.sh

后续:
  P0-3 完整实现拆 7 个子任务(P0-3.1 ~ P0-3.7)
  建议先部署 hooks 跑 1-2 周观察,再推进 Release 机制落地
2026-04-16 21:02:29 +09:30
112 changed files with 7093 additions and 618 deletions
+206 -68
View File
@@ -23,6 +23,30 @@
],
"strict": false
},
{
"name": "pm-ask-plugin",
"source": "./skills-core/pm-ask-plugin",
"description": "基于真实数据的项目问答 /ask。必须引用 MCP/git 真实数据,禁止编造",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "pm-risk-plugin",
"source": "./skills-core/pm-risk-plugin",
"description": "三维度项目风险扫描:需求层/代码层/流程层。当用户说'/risk'、'风险扫描'、'有什么风险'时自动激活",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "publish-plugin",
"source": "./skills-core/publish-plugin",
@@ -47,6 +71,56 @@
],
"strict": false
},
{
"name": "agent-swarm-plugin",
"source": "./skills-dev/agent-swarm-plugin",
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "ai-chat-plugin",
"source": "./skills-dev/ai-chat-plugin",
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "ai-proj-cicd-release-plugin",
"source": "./skills-dev/ai-proj-cicd-release-plugin",
"description": "执行和审计 AI-Proj 服务从 Gitea 门禁、不可变镜像、预发验证到生产发布和回滚的 CI/CD 流程。",
"version": "1.0.0",
"category": "productivity",
"keywords": [
"project-management",
"tasks",
"requirements"
],
"strict": false
},
{
"name": "ai-proj-macos-release-plugin",
"source": "./skills-dev/ai-proj-macos-release-plugin",
"description": "构建、签名、公证、发布并验证 AI-Proj macOS Apple Silicon 安装包。",
"version": "1.0.0",
"category": "productivity",
"keywords": [
"project-management",
"tasks",
"requirements"
],
"strict": false
},
{
"name": "db-migration-plugin",
"source": "./skills-dev/db-migration-plugin",
@@ -59,6 +133,18 @@
],
"strict": false
},
{
"name": "defect-analysis-plugin",
"source": "./skills-dev/defect-analysis-plugin",
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
"version": "1.1.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "deploy-rollback-plugin",
"source": "./skills-dev/deploy-rollback-plugin",
@@ -124,6 +210,19 @@
],
"strict": false
},
{
"name": "dev-commit-plugin",
"source": "./skills-dev/dev-commit-plugin",
"description": "智能 /commit 命令:分支保护 + 自动建功能分支 + Conventional Commits 生成",
"version": "1.0.0",
"category": "development",
"keywords": [
"development",
"coding",
"workflow"
],
"strict": false
},
{
"name": "dev-deploy-plugin",
"source": "./skills-dev/dev-deploy-plugin",
@@ -228,6 +327,55 @@
],
"strict": false
},
{
"name": "executing-plans-plugin",
"source": "./skills-dev/executing-plans-plugin",
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints.",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "finishing-branch-plugin",
"source": "./skills-dev/finishing-branch-plugin",
"description": "Use when implementation is complete and all tests pass - verifies and creates PR.",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "frontend-design-plugin",
"source": "./skills-dev/frontend-design-plugin",
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
"version": "1.0.1",
"category": "development",
"keywords": [
"development",
"coding",
"workflow"
],
"strict": false
},
{
"name": "karpathy-guidelines-plugin",
"source": "./skills-dev/karpathy-guidelines-plugin",
"description": "Karpathy 四原则编码行为守则(Think Before Coding / Simplicity First / Surgical Changes / Goal-Driven Execution)。已深度融合到 req 技能工作流各阶段,可独立激活用于任意编码场景。",
"version": "1.0.1",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "pull-request-plugin",
"source": "./skills-dev/pull-request-plugin",
@@ -245,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",
@@ -253,6 +401,32 @@
],
"strict": false
},
{
"name": "slark-cicd-plugin",
"source": "./skills-dev/slark-cicd-plugin",
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
"version": "1.0.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"strict": false
},
{
"name": "req-audit-plugin",
"source": "./skills-req/req-audit-plugin",
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
"version": "1.0.0",
"category": "productivity",
"keywords": [
"project-management",
"tasks",
"requirements"
],
"strict": false
},
{
"name": "req-compare-plugin",
"source": "./skills-req/req-compare-plugin",
@@ -292,6 +466,19 @@
],
"strict": false
},
{
"name": "req-lookback-plugin",
"source": "./skills-req/req-lookback-plugin",
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
"version": "1.0.0",
"category": "productivity",
"keywords": [
"project-management",
"tasks",
"requirements"
],
"strict": false
},
{
"name": "req-plugin",
"source": "./skills-req/req-plugin",
@@ -308,8 +495,8 @@
{
"name": "req-prd-plugin",
"source": "./skills-req/req-prd-plugin",
"description": "产品需求设计技能。PRD 文档编写、需求分析、用户故事、对比式分析。纯产品视角,不含技术实现。",
"version": "2.0.0",
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
"version": "2.2.0",
"category": "productivity",
"keywords": [
"project-management",
@@ -321,8 +508,8 @@
{
"name": "req-prototype-plugin",
"source": "./skills-req/req-prototype-plugin",
"description": "Stitch 原型生成与迭代。基于 PRD 文档自动生成 UI 原型。",
"version": "1.0.0",
"description": "原型生成与关联。支持 HTML 本地留源、OSS 正式交付、Requirement/iframe 验证及 Stitch AI。",
"version": "2.2.0",
"category": "productivity",
"keywords": [
"project-management",
@@ -344,6 +531,19 @@
],
"strict": false
},
{
"name": "req-retro-plugin",
"source": "./skills-req/req-retro-plugin",
"description": "复盘总结。自动采集数据、计算质量评分、跨需求模式识别、技能自动进化。可独立调用或由 /req done 自动触发。",
"version": "1.0.0",
"category": "productivity",
"keywords": [
"project-management",
"tasks",
"requirements"
],
"strict": false
},
{
"name": "req-review-plugin",
"source": "./skills-req/req-review-plugin",
@@ -399,7 +599,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",
@@ -535,68 +735,6 @@
"tools"
],
"strict": false
},
{
"name": "gitea-plugin",
"source": "./skills-personal/gitea-plugin",
"description": "Gitea 代码托管与 CI/CD 管理。用于 Gitea Actions workflow 管理、Runner 管理、PR 操作、仓库配置。",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "openclaw-plugin",
"source": "./skills-personal/openclaw-plugin",
"description": "OpenClaw (龙虾) 远程 AI 计算调度系统 - 概念设计与运维管理",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "ops-tools-plugin",
"source": "./skills-personal/ops-tools-plugin",
"description": "Plugin for ops-tools",
"version": "1.0.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"strict": false
},
{
"name": "qiudl-personal-plugin",
"source": "./skills-personal/qiudl-personal-plugin",
"description": "Plugin for qiudl-personal",
"version": "1.0.0",
"category": "utility",
"keywords": [
"utility",
"tools"
],
"strict": false
},
{
"name": "req-deploy-plugin",
"source": "./skills-personal/req-deploy-plugin",
"description": "Plugin for req-deploy",
"version": "1.0.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"strict": false
}
]
}
+9 -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,4 @@ skills:
- `skills-personal/` 不被 Git 跟踪,用于存放个人配置和工具
- 其余 `skills-*` 目录均由 Git 版本控制
- 所有目录会被 `generate-marketplace.py` 自动扫描并加入 marketplace.json
- 只有受 Git 跟踪的分类目录会被 `generate-marketplace.py` 扫描并加入公开 marketplace`skills-personal/` 始终排除
+14 -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: 如何禁用不需要的技能?**
+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`
+41 -190
View File
@@ -1,218 +1,69 @@
# 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
## 将本机技能发布到仓库
```bash
git add .
git commit -m "Update skill: description of changes"
git push
```
不要批量复制整个 `~/.agents/skills/` 或其他 Agent 的安装目录。系统技能、第三方托管技能、包含机器路径或凭据的技能不应发布。
### 5. Team Updates
1. 选择确实属于本仓库、可供团队复用的技能。
2. 在对应 `skills-*/<name>-plugin/` 下放置 `.claude-plugin/plugin.json` 和完整 `skills/` 目录。
3. 清除用户名、绝对路径、内网地址、密钥标识和历史凭据;把环境差异改为从仓库配置解析。
4. 更新插件版本并运行:
Team members update with:
```bash
/plugin marketplace update coolbuy-claude-plugins
/plugin update <plugin-name>@coolbuy-claude-plugins
```
```bash
python3 generate-marketplace.py
claude plugin validate .
git diff --check
```
## Automated Sync (Optional)
5. 审核变更后通过分支和 PR 发布。
### Git Hook (Pre-commit)
## 本地个人技能
Auto-sync when committing changes to skills:
`skills-personal/` 受 `.gitignore` 保护,仅供当前机器使用。生成器明确排除此目录,避免
`marketplace.json` 引用公开克隆中不存在的文件。若个人技能要转为团队技能,应先按上面的发布流程完成脱敏和审核。
```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)
先比较仓库源和 `~/.agents/skills/<name>/`。保留本地修改时将其整理成插件变更;确认丢弃时再对该次安装使用 `--force`。Claude 目标改查 `~/.claude/skills/`。
Sync daily at 9 AM:
**marketplace 没更新?**
```bash
crontab -e
运行 `python3 generate-marketplace.py`,然后检查 `.claude-plugin/marketplace.json` 是否只包含受 Git 跟踪且真实存在的 source。
# 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.
删除插件目录、重新生成 marketplace、提交变更。使用者随后执行 `./install-skills.sh --dry-run --cleanup`,确认后再去掉 `--dry-run`。
+253
View File
@@ -0,0 +1,253 @@
# Release Draft 闸门机制 — 设计文档
**REQ**: REQ-20260416-0017 P0-3
**状态**: 设计阶段
**创建时间**: 2026-04-16
---
## 1. 背景
### 当前问题
ai-proj 当前 CI/CD 流程(`.gitea/workflows/build.yaml`):
```
push 到 main → 自动 build 镜像 → 自动部署生产
```
**这个链路没有"最后一道人工闸门"**。merge develop→main 的 PR 一合并,生产就开始部署。一旦合并,只能事后回滚。
**典型事故场景**:PR 评审时漏了某个改动,merge 后立即推到线上,5 分钟后发现问题,但已经影响用户 5 分钟。
### 借鉴方案
**devflow-claude `/req:release` 命令的核心做法:**
1. merge PR 不直接部署
2. 创建一个 **draft release**(草稿)
3. **需要人工在 Gitea/GitHub 平台点 "Publish" 按钮** 才触发部署
4. Draft 可以审查 SQL migration、回滚脚本、changelog 等"产物清单"
5. Publish 后才打 tag + 触发 build.yaml
---
## 2. 目标
- 在 merge main 和"真正部署"之间插入 **人工 publish 闸门**
- 提供 **release 产物清单预览**SQL migration / 回滚脚本 / changelog
- 支持 **一键回滚**(从 draft release 追溯回滚脚本)
- 与现有 ai-proj MCP 工具链集成
---
## 3. 架构设计
### 3.1 流程对比
**当前流程:**
```
[feat/xxx] → PR → merge develop → 测试
[develop] → PR → merge main → ✗ 立即触发 build.yaml → 生产部署
```
**新流程:**
```
[feat/xxx] → PR → merge develop → 测试
[develop] → PR → merge main → Gitea draft release(待审查)
人工 publish
tag 推送 + 触发 build.yaml → 生产部署
```
### 3.2 组件职责
| 组件 | 新增/改造 | 职责 |
|------|---------|------|
| **ai-proj backend** | 新增 | `/api/v1/releases` REST API(创建 draft、publish、查询、回滚) |
| **mcp-task-bridge** | 新增工具 | `create_release_draft` / `publish_release` / `list_releases` / `get_release` / `rollback_release` |
| **Gitea Actions** | 改造 | `build.yaml` 触发条件改为 `release.published` 事件 |
| **`release` skill** | 新增 | AI 侧命令封装(`/release new`, `/release publish`, `/release rollback` |
| **release-draft.sh** | 新增脚本 | 本地命令行工具,用于在 CI 外手动创建 draft |
### 3.3 数据模型
**新增 `releases` 表:**
```sql
CREATE TABLE releases (
id SERIAL PRIMARY KEY,
display_id VARCHAR(64) UNIQUE NOT NULL, -- e.g. "RELEASE-20260416-001"
version VARCHAR(64) NOT NULL, -- e.g. "v1.2.0"
title VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(32) NOT NULL, -- draft / published / rolled_back
git_base_ref VARCHAR(255) NOT NULL, -- e.g. previous tag "v1.1.9"
git_head_ref VARCHAR(255) NOT NULL, -- e.g. commit sha after merge
git_tag VARCHAR(64), -- 生成的 tagpublish 后才有
changelog_md TEXT NOT NULL, -- 自动生成的 changelog
sql_migration_paths TEXT[], -- 合并的 SQL 文件路径列表
sql_rollback_md TEXT, -- 回滚脚本
gitea_release_id BIGINT, -- Gitea 上的 release ID
gitea_release_url VARCHAR(512),
created_by INT REFERENCES users(id),
published_by INT REFERENCES users(id),
published_at TIMESTAMP,
rolled_back_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
### 3.4 API 设计
| Endpoint | 作用 | 权限 |
|----------|------|------|
| `POST /api/v1/releases/drafts` | 创建 draft release(自动推导版本、生成 changelog) | 开发者 |
| `POST /api/v1/releases/:id/publish` | 发布(推 tag、触发部署) | 需确认 |
| `GET /api/v1/releases` | 列表 | 所有人 |
| `GET /api/v1/releases/:id` | 详情 | 所有人 |
| `POST /api/v1/releases/:id/rollback` | 回滚到上一版本 | 管理员 |
### 3.5 Gitea 集成
**创建 draft release(调 Gitea API):**
```http
POST /api/v1/repos/{owner}/{repo}/releases
{
"tag_name": "v1.2.0",
"target_commitish": "main",
"name": "v1.2.0",
"body": "...<changelog>...",
"draft": true
}
```
**build.yaml 改造:**
```yaml
# Before
on:
push:
branches: [main]
# After
on:
release:
types: [published]
workflow_dispatch: # 保留手动触发
```
**关键变化**`release.published` 事件只在草稿被 publish 时触发,merge main 不再自动触发。
---
## 4. 用户流程(典型场景)
### 4.1 开发者发版
```
1. 合并 PR 到 main(照旧)
2. 在 Claude Code 里:/release new
- AI 读 git log,自动推导版本号
- 汇总 SQL migration
- 生成 changelog
- 生成回滚脚本
- 展示产物清单,等用户确认
3. 确认后:MCP create_release_draft → 后端 API → Gitea draft
4. 收到 Slack/企微通知:"draft release v1.2.0 已创建,请审查"
5. 打开 Gitea 查看产物清单
6. 点 "Publish release" 按钮
7. CI/CD 自动触发 → 生产部署
```
### 4.2 回滚
```
1. /release rollback v1.2.0
2. MCP rollback_release → 后端执行回滚 SQL → 重新部署上一版本镜像
3. 记录到 releases 表:status=rolled_back
```
---
## 5. 渐进式落地路径(P0-3 拆分子任务)
由于 P0-3 完整实现涉及后端 + MCP + CI/CD + skill 四个层面,**不建议单会话完成**。拆分为:
| 子任务 | 范围 | 风险 |
|-------|------|------|
| **P0-3.1** | 后端 release model + migration | 低(只是建表) |
| **P0-3.2** | 后端 /api/v1/releases APIdraft + publish | 中 |
| **P0-3.3** | MCP 工具 create_release_draft / publish_release | 中 |
| **P0-3.4** | `release` skill 创建 | 低 |
| **P0-3.5** | Gitea draft release 集成 | 中 |
| **P0-3.6** | build.yaml 改为 release.published 触发 | **高**(影响所有人的发版流程) |
| **P0-3.7** | 回滚能力 | 中 |
**推荐落地节奏:**
- 先做 P0-3.1 ~ P0-3.5(构建基础能力)
- **保留旧 push-to-main 流程作为 fallback**
- 跑 2 周观察
- 再做 P0-3.6(切换 CI/CD 触发源)
- 最后 P0-3.7(回滚能力)
---
## 6. 风险与应对
### R1: build.yaml 触发源切换影响所有开发者
**影响**:目前大家习惯 "merge main 即部署",切换后需等 publish
**应对**
- 上线前 1 周发邮件 + 企微通知
- 提供手动触发(workflow_dispatch)作为应急入口
- 文档化新流程
### R2: 回滚脚本自动生成不够可靠
**影响**:复杂 migration(如数据迁移)的回滚无法自动推导
**应对**
- AI 只生成简单反向 DDLCREATE→DROP 等)
- 复杂情况标记 `⚠️ 需手工补全`
- 回滚 SQL 由人工审查后纳入 draft release
### R3: draft release 被忘记 publish
**影响**:功能开发完但生产没部署
**应对**
- 企微机器人每天检查超 24h 未 publish 的 draft,发提醒
- 管理员 dashboard 显示 draft 列表
---
## 7. 验收标准
- [ ] `releases` 表创建,migration 落地
- [ ] `/api/v1/releases/drafts` 创建 draft(至少支持最小字段)
- [ ] MCP `create_release_draft` 工具可用
- [ ] 能从 Claude Code 一键创建 Gitea draft release
- [ ] build.yaml 支持 `release.published` 触发
- [ ] 至少跑通 1 次完整流程(draft → 人工 publish → 自动部署)
- [ ] 回滚脚本自动生成覆盖 >80% 的 DDL 场景
---
## 8. 参考
- devflow-claude: `plugins/req/commands/release.md`
- REQ-20260416-0017(母需求)
- 当前 build.yaml: `/Users/donglinlai/coding/qiudl/new-ai-proj/.gitea/workflows/build.yaml`
- Gitea Release API: https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateRelease
---
## 9. 下一步行动
- [ ] 本文档提交评审
- [ ] 评审通过后拆 7 个子任务(P0-3.1 ~ P0-3.7)并分别关联到 REQ-20260416-0017
- [ ] 从 P0-3.1(数据模型)开始实施
+7 -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']):
return "devops", ["devops", "deployment", "operations"]
elif any(x in plugin_name for x in ['ai-proj', 'req']):
return "productivity", ["project-management", "tasks", "requirements"]
@@ -116,15 +112,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
+100
View File
@@ -0,0 +1,100 @@
# Claude Code Hooks
本目录包含 ai-proj-helper 体系的 Claude Code 钩子脚本。
**REQ-20260416-0017 P0 批 — 源自 devflow-claude 借鉴**
## 脚本清单
| 脚本 | 事件 | 作用 |
|------|------|------|
| `session-context.sh` | SessionStart | 从分支名解析 REQ-ID,注入需求上下文到会话 |
| `pre-tool-confirm.sh` | PreToolUse (Bash) | 拦截生产发布、force push、docker 生产容器、reset --hard 等危险操作 |
## 安装(用户级,一次即可)
### 方式 1:编辑 `~/.claude/settings.json`
```jsonc
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
"timeout": 10
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/pre-tool-confirm.sh",
"timeout": 30
}
]
}
]
}
}
```
### 方式 2:一键安装脚本
```bash
bash /Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/install.sh
```
## 验证
### SessionStart hook
1. 切到一个带 REQ-ID 的分支:`git checkout feat/REQ-20260416-0017-xxx`
2. 打开新的 Claude Code 会话
3. 会话开头应看到 `# 需求上下文(SessionStart Hook`
### PreToolUse hook
让 AI 尝试执行这些命令中的任一条,应弹出原生确认对话框:
- `git push origin main`
- `git push --force`
- `tea pr merge --base main`
- `docker rm ai_postgres_prod`
- `git reset --hard`
## 依赖
- `bash` (macOS / Linux 默认)
- `jq` (PreToolUse hook 需要,`brew install jq`)
- `python3` (SessionStart hook 解析 JSON)
- `curl` (SessionStart hook 调 MCP API)
## 自定义
### 修改 MCP API 地址
在项目根目录创建 `.ai-proj-env`
```bash
export AI_PROJ_API_BASE="https://api.ai-proj.example.com"
export AI_PROJ_MCP_KEY="your-mcp-api-key"
```
或设置全局环境变量。
### 拦截更多命令
编辑 `pre-tool-confirm.sh`,在 `REASON` 赋值的 elif 链中增加规则。
## 设计原则
1. **快速失败退出**:不处理的命令立即 `exit 0`,不影响性能
2. **非侵入性**:网络/依赖缺失时静默退出,不阻塞正常工作流
3. **可复现**:hook 脚本跟随仓库分发,方便团队一致部署
+81
View File
@@ -0,0 +1,81 @@
#!/bin/bash
# install.sh
# 一键把 ai-proj-helper hooks 注册到 ~/.claude/settings.json
#
# 用法: bash hooks/install.sh
set -e
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SETTINGS_FILE="$HOME/.claude/settings.json"
if ! command -v python3 >/dev/null 2>&1; then
echo "❌ 需要 python3(用于合并 JSON"
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "⚠️ 未安装 jqPreToolUse hook 将无法正常工作"
echo " 请执行: brew install jq"
fi
mkdir -p "$HOME/.claude"
python3 << EOF
import json
import os
settings_file = "$SETTINGS_FILE"
hooks_dir = "$HOOKS_DIR"
# 读已有配置
if os.path.exists(settings_file):
with open(settings_file) as f:
data = json.load(f)
else:
data = {}
# 合并 hooks
hooks = data.setdefault("hooks", {})
# SessionStart
session_start = hooks.setdefault("SessionStart", [])
session_cmd = f"{hooks_dir}/session-context.sh"
already_registered = any(
any(h.get("command") == session_cmd for h in entry.get("hooks", []))
for entry in session_start
)
if not already_registered:
session_start.append({
"hooks": [{"type": "command", "command": session_cmd, "timeout": 10}]
})
print("✅ 注册 SessionStart hook")
else:
print("⏭️ SessionStart hook 已存在")
# PreToolUse (Bash)
pre_tool = hooks.setdefault("PreToolUse", [])
pre_cmd = f"{hooks_dir}/pre-tool-confirm.sh"
already_registered = any(
entry.get("matcher") == "Bash" and any(h.get("command") == pre_cmd for h in entry.get("hooks", []))
for entry in pre_tool
)
if not already_registered:
pre_tool.append({
"matcher": "Bash",
"hooks": [{"type": "command", "command": pre_cmd, "timeout": 30}]
})
print("✅ 注册 PreToolUse (Bash) hook")
else:
print("⏭️ PreToolUse hook 已存在")
# 写回
with open(settings_file, "w") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
print(f"\n📝 已更新: {settings_file}")
print("\n下一步:")
print(" 1. 重启 Claude Code 会话")
print(" 2. 切到含 REQ-ID 的分支测试 SessionStart hook")
print(" 3. 让 AI 尝试 'git push origin main' 测试 PreToolUse hook")
EOF
+94
View File
@@ -0,0 +1,94 @@
#!/bin/bash
# pre-tool-confirm.sh
# PreToolUse Hook: 拦截危险操作,强制原生确认对话框
#
# 输入格式(stdin JSON):
# { "tool_name": "Bash", "tool_input": { "command": "..." } }
#
# 输出格式(stdout JSON):
# { "hookSpecificOutput": { "hookEventName": "PreToolUse",
# "permissionDecision": "ask",
# "permissionDecisionReason": "..." } }
#
# 安装方式:在 ~/.claude/settings.json 配置
# hooks.PreToolUse:
# - matcher: "Bash"
# hooks:
# - type: command
# command: "<path>/hooks/pre-tool-confirm.sh"
# timeout: 30
#
# 参考:devflow-claude confirm-before-commit.sh + ai-proj memory 规则
# REQ-20260416-0017 P0-2
set -e
INPUT=$(cat)
if ! command -v jq >/dev/null 2>&1; then
# 没有 jq 就不处理
exit 0
fi
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if [ -z "$COMMAND" ]; then
exit 0
fi
REASON=""
# ============ 1. 生产分支推送 ============
if echo "$COMMAND" | grep -qE '\bgit\s+push\b.*\b(origin\s+)?(main|master)\b'; then
REASON="⚠️ 即将推送到 main/master 生产分支。确认已过 PR 评审?"
# ============ 2. 强制推送 ============
elif echo "$COMMAND" | grep -qE '\bgit\s+push\b.*(--force|--force-with-lease|-f\b)'; then
REASON="⛔ 危险:force push 会覆盖远程历史,可能丢失他人提交。确认继续?"
# ============ 3. tea pr merge --base main ============
elif echo "$COMMAND" | grep -qE '\btea\s+pr\s+merge\b.*--base\s+main\b'; then
REASON="⚠️ 即将合并 PR 到 main 分支,合并后将触发生产部署。确认 PR 已测试?"
# ============ 4. docker rm/stop 生产容器 ============
elif echo "$COMMAND" | grep -qE '\bdocker\s+(rm|stop|kill)\b.*\b(ai_postgres_prod|ai_backend_prod|ai_frontend_prod|ai_redis_prod)\b'; then
REASON="⛔ 危险:即将停止/删除生产容器!2026-04-05 曾因此宕机 15 分钟。确认是灾难恢复?"
# ============ 5. docker rm/stop 其他 prod 相关 ============
elif echo "$COMMAND" | grep -qE '\bdocker\s+(rm|stop|kill)\b.*_prod\b'; then
REASON="⚠️ 即将停止/删除含 _prod 的容器。确认是生产环境?"
# ============ 6. git reset --hard / clean -fd ============
elif echo "$COMMAND" | grep -qE '\bgit\s+reset\s+--hard\b'; then
REASON="⚠️ git reset --hard 会丢弃所有未提交改动。确认继续?"
elif echo "$COMMAND" | grep -qE '\bgit\s+clean\s+.*-f'; then
REASON="⚠️ git clean -f 会删除未跟踪文件。确认继续?"
# ============ 7. rm -rf / 系统路径 ============
elif echo "$COMMAND" | grep -qE '\brm\s+.*-[rf]+.*\s+(/|/\*|~|\$HOME)'; then
REASON="⛔ 危险:rm -rf 指向系统根或家目录。确认继续?"
# ============ 8. ssh 生产服务器 + 破坏性命令 ============
elif echo "$COMMAND" | grep -qE 'ssh\s+\S*prod\S*.*\b(rm|drop|truncate|delete)\b'; then
REASON="⛔ 危险:在生产服务器执行破坏性命令。确认继续?"
# ============ 9. psql/mysql 生产数据库 + DROP/TRUNCATE/DELETE ============
elif echo "$COMMAND" | grep -qiE '(psql|mysql).*prod.*\b(DROP|TRUNCATE|DELETE FROM)\b'; then
REASON="⛔ 危险:生产数据库 DROP/TRUNCATE/DELETE。确认已备份?"
# ============ 10. MCP advance_delivery_stage force=true ============
# 这种会走 MCP 而不是 Bash,本 hook 不好拦,留给另一个 matcher 处理
fi
if [ -n "$REASON" ]; then
jq -n --arg reason "$REASON" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: $reason
}
}'
else
exit 0
fi
+161
View File
@@ -0,0 +1,161 @@
#!/bin/bash
# release-draft.sh
# 最小可用版:从本地 git 仓库创建 Gitea draft release
#
# 用法:
# export GITEA_TOKEN=$(bw get password "Gitea - qiudl Token")
# bash release-draft.sh v1.2.0 [--from v1.1.9]
#
# 输出: 创建的 draft release URL(待人工 publish
# REQ-20260416-0017 P0-3 最小可用脚本
set -e
VERSION="$1"
if [ -z "$VERSION" ]; then
echo "❌ 用法: $0 <version> [--from <previous_tag>]"
exit 1
fi
FROM_TAG=""
if [ "$2" = "--from" ]; then
FROM_TAG="$3"
fi
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "❌ 不在 git 仓库内"
exit 1
fi
REPO_SLUG=$(git remote get-url origin 2>/dev/null | \
sed -E 's|.*[:/]([^/]+/[^/]+)\.git$|\1|' | \
sed -E 's|.*[:/]([^/]+/[^/]+)$|\1|')
if [ -z "$REPO_SLUG" ]; then
echo "❌ 无法从 git remote 推断 OWNER/REPO"
exit 1
fi
if [ -z "$GITEA_TOKEN" ]; then
echo "❌ 需要 GITEA_TOKEN 环境变量"
echo " export GITEA_TOKEN=\$(bw get password 'Gitea - qiudl Token')"
exit 1
fi
GITEA_URL="${GITEA_URL:-https://gitea.pipexerp.com}"
# 推断 from
if [ -z "$FROM_TAG" ]; then
FROM_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
fi
TO_REF="HEAD"
# 生成 changelog 内容
echo "📋 生成 changelog..."
CHANGELOG=""
if [ -n "$FROM_TAG" ]; then
COMMITS=$(git log --pretty=format:'- %s (%h)' "${FROM_TAG}..${TO_REF}" 2>/dev/null || echo "")
else
COMMITS=$(git log --pretty=format:'- %s (%h)' "${TO_REF}" 2>/dev/null || echo "")
fi
if [ -z "$COMMITS" ]; then
echo "⚠️ 无 commit,放弃"
exit 1
fi
# 按类型分组
FEATS=$(echo "$COMMITS" | grep -iE 'feat(\(|:)|新功能' || true)
FIXES=$(echo "$COMMITS" | grep -iE 'fix(\(|:)|修复' || true)
CHORES=$(echo "$COMMITS" | grep -iE 'chore(\(|:)' || true)
OTHERS=$(echo "$COMMITS" | grep -vE 'feat(\(|:)|fix(\(|:)|chore(\(|:)|新功能|修复' || true)
CHANGELOG="## 发布内容
**版本**: \`${VERSION}\`
**区间**: \`${FROM_TAG:-init}..${TO_REF}\`
"
if [ -n "$FEATS" ]; then
CHANGELOG="${CHANGELOG}### 新功能
${FEATS}
"
fi
if [ -n "$FIXES" ]; then
CHANGELOG="${CHANGELOG}### Bug 修复
${FIXES}
"
fi
if [ -n "$CHORES" ]; then
CHANGELOG="${CHANGELOG}### 杂项
${CHORES}
"
fi
if [ -n "$OTHERS" ]; then
CHANGELOG="${CHANGELOG}### 其他
${OTHERS}
"
fi
CHANGELOG="${CHANGELOG}
---
⚠️ **这是 draft release**,审查无误后点击 'Publish release' 按钮才会触发生产部署。
📋 审查要点:
- [ ] 所有改动已过 PR 评审
- [ ] SQL migration 已验证(如有)
- [ ] 回滚方案已确认(如有)
- [ ] 生产环境准备就绪"
# 创建 draft release
echo "🚀 创建 Gitea draft release..."
BODY_JSON=$(python3 -c "
import json
print(json.dumps({
'tag_name': '$VERSION',
'target_commitish': 'main',
'name': '$VERSION',
'body': '''$CHANGELOG''',
'draft': True,
'prerelease': False,
}))
")
RESP=$(curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$BODY_JSON" \
"${GITEA_URL}/api/v1/repos/${REPO_SLUG}/releases")
HTML_URL=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('html_url',''))" 2>/dev/null)
if [ -n "$HTML_URL" ]; then
echo "✅ Draft release 已创建"
echo "🔗 $HTML_URL"
echo ""
echo "⏭️ 下一步:"
echo " 1. 打开链接审查产物清单"
echo " 2. 确认无误后点 'Publish release' 按钮"
echo " 3. CI/CD 将自动触发生产部署"
else
echo "❌ 创建失败"
echo "$RESP" | head -20
exit 1
fi
+130
View File
@@ -0,0 +1,130 @@
#!/bin/bash
# session-context.sh
# SessionStart Hook: 会话启动时自动注入需求上下文
#
# 从当前 Git 分支名解析 REQ-ID,调用 ai-proj MCP API 查询需求详情,
# 把标题 / 状态 / delivery_stage / reviewer / 进行中需求数注入 system-reminder。
#
# 安装方式:
# 在 ~/.claude/settings.json 的 hooks.SessionStart 配置:
# {
# "command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
# "timeout": 10
# }
#
# 参考:devflow-claude 同名脚本 + ai-proj MCP 适配
# REQ-20260416-0017 P0-1
set -e
# 仅在 git 仓库内执行
if ! git rev-parse --git-dir >/dev/null 2>&1; then
exit 0
fi
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
if [ -z "$REPO_ROOT" ]; then
exit 0
fi
cd "$REPO_ROOT"
# ============ 1. 当前分支 → REQ ID ============
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
REQ_ID=""
if [ -n "$BRANCH" ]; then
# 匹配 feat/REQ-20260416-0017-xxx / fix/REQ-20260416-0017 / feature/req-20260416-0017
REQ_ID=$(echo "$BRANCH" | grep -oiE 'REQ-[0-9]{8}-[0-9]{4}' | head -1 | tr '[:lower:]' '[:upper:]')
fi
# ============ 2. 无 REQ 时仅输出分支信息(静默退出条件) ============
if [ -z "$REQ_ID" ]; then
# 只要不在 main/develop 上就提示一下
case "$BRANCH" in
main|master|develop|"") exit 0 ;;
esac
echo "# 会话上下文"
echo ""
echo "- 当前分支: \`${BRANCH}\`(未检测到 REQ ID"
exit 0
fi
# ============ 3. 查询 MCP API ============
# MCP API 通过 localhost:8080 直连(ai-proj 本地后端)或 ai-proj-prod
# 这里优先读项目根的 .ai-proj-env 决定环境
API_BASE="${AI_PROJ_API_BASE:-}"
API_TOKEN="${AI_PROJ_MCP_KEY:-}"
if [ -f "$REPO_ROOT/.ai-proj-env" ]; then
# shellcheck disable=SC1091
source "$REPO_ROOT/.ai-proj-env"
fi
if [ -z "$API_BASE" ]; then
# 默认走本地 dev
API_BASE="http://localhost:8080"
fi
# 查询需求
RESP=""
if command -v curl >/dev/null 2>&1; then
if [ -n "$API_TOKEN" ]; then
RESP=$(curl -s --max-time 3 -H "X-MCP-API-Key: $API_TOKEN" \
"${API_BASE}/api/v1/mcp/requirements/by-display-id/${REQ_ID}" 2>/dev/null || echo "")
else
RESP=$(curl -s --max-time 3 \
"${API_BASE}/api/v1/mcp/requirements/by-display-id/${REQ_ID}" 2>/dev/null || echo "")
fi
fi
# ============ 4. 解析并输出 ============
echo "# 需求上下文(SessionStart Hook"
echo ""
echo "- 分支: \`${BRANCH}\`"
echo "- 需求: **${REQ_ID}**"
if [ -n "$RESP" ] && command -v python3 >/dev/null 2>&1; then
# 尝试用 python 解析
PARSED=$(python3 -c "
import sys, json
try:
d = json.loads('''$RESP''')
data = d.get('data', {})
if not data:
sys.exit(0)
title = data.get('title', '?')
status = data.get('status', '?')
stage = data.get('delivery_stage', '?')
priority = data.get('priority', '?')
project = data.get('project_name', '?')
print(f'title={title}')
print(f'status={status}')
print(f'stage={stage}')
print(f'priority={priority}')
print(f'project={project}')
except Exception:
pass
" 2>/dev/null)
if [ -n "$PARSED" ]; then
TITLE=$(echo "$PARSED" | grep '^title=' | sed 's/^title=//')
STATUS=$(echo "$PARSED" | grep '^status=' | sed 's/^status=//')
STAGE=$(echo "$PARSED" | grep '^stage=' | sed 's/^stage=//')
PRIORITY=$(echo "$PARSED" | grep '^priority=' | sed 's/^priority=//')
PROJECT=$(echo "$PARSED" | grep '^project=' | sed 's/^project=//')
[ -n "$TITLE" ] && echo "- 标题: ${TITLE}"
[ -n "$PROJECT" ] && echo "- 项目: ${PROJECT}"
[ -n "$STATUS" ] && echo "- 状态: ${STATUS}"
[ -n "$STAGE" ] && echo "- 交付阶段: ${STAGE}"
[ -n "$PRIORITY" ] && echo "- 优先级: ${PRIORITY}"
else
echo "- 📡 MCP API 响应为空或未授权(API_BASE=${API_BASE}"
fi
else
echo "- ⚠️ 无法连接 MCP API${API_BASE}),仅显示分支信息"
fi
echo ""
echo "💡 相关命令:\`/req get ${REQ_ID}\` 查看详情 · \`/commit\` 智能提交"
+98
View File
@@ -0,0 +1,98 @@
#!/bin/bash
# validate-prd.sh
# PostToolUse Hook: PRD 文档写入/编辑后自动校验章节完整性
#
# 检查标准 PRD 模板的必需章节是否存在。
# 借鉴 devflow-claude validate-requirement.sh。
# REQ-20260416-0017 P1-14
#
# 安装方式(可选,加到 ~/.claude/settings.json:
# hooks.PostToolUse:
# - matcher: "Write|Edit"
# hooks:
# - type: command
# command: "<path>/hooks/validate-prd.sh"
# timeout: 5
#
# 也可由 skill 脚本在 PRD 写完后手动调用:
# bash validate-prd.sh /path/to/PRD.md
set -e
# ============ 1. 获取文件路径 ============
FILE_PATH="$1"
# 如果没有参数,从 stdin JSON 提取(PostToolUse hook 模式)
if [ -z "$FILE_PATH" ] && command -v jq >/dev/null 2>&1; then
INPUT=$(cat 2>/dev/null || echo "")
if [ -n "$INPUT" ]; then
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.content // empty' 2>/dev/null)
fi
fi
# 没有文件路径,静默退出
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# 不是 .md 文件,跳过
case "$FILE_PATH" in
*.md) ;;
*) exit 0 ;;
esac
# 文件不存在,跳过
if [ ! -f "$FILE_PATH" ]; then
exit 0
fi
# 不含 PRD / 需求 关键词的文件名,跳过
BASENAME=$(basename "$FILE_PATH")
case "$BASENAME" in
*PRD*|*prd*|*需求*|*requirement*|*REQ*) ;;
*) exit 0 ;;
esac
# ============ 2. 检查章节 ============
# 标准 PRD 必需章节(来自 req-prd SKILL.md 的模板)
REQUIRED_SECTIONS=(
"## 1. 概述"
"### 1.1 背景"
"### 1.2 目标"
"### 1.4 客户原始诉求"
"## 2. 用户分析"
"## 3. 功能需求"
"## 4. 交互设计"
"## 5. 技术要求"
"## 6. 上线计划"
"## 7. 风险评估"
)
MISSING=()
CONTENT=$(cat "$FILE_PATH")
for section in "${REQUIRED_SECTIONS[@]}"; do
# 模糊匹配:忽略空格差异和标点
PATTERN=$(echo "$section" | sed 's/[[:space:]]*//g')
CONTENT_CLEAN=$(echo "$CONTENT" | sed 's/[[:space:]]*//g')
if ! echo "$CONTENT_CLEAN" | grep -qi "$(echo "$PATTERN" | sed 's/#//g')"; then
MISSING+=("$section")
fi
done
# ============ 3. 输出 ============
if [ ${#MISSING[@]} -gt 0 ]; then
echo ""
echo "⚠️ PRD 章节检查:${BASENAME}"
echo ""
echo "缺少 ${#MISSING[@]} 个必需章节:"
for m in "${MISSING[@]}"; do
echo "$m"
done
echo ""
echo "💡 请参考 req-prd skill 的 PRD 模板补充缺失章节。"
echo " 章节结构不可变:不得新增、删除、合并或重命名模板中的章节。"
fi
exit 0
+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"
+456
View File
@@ -0,0 +1,456 @@
#!/usr/bin/env bash
# install-skills.sh — Cross-agent skill sync from ai-proj-helper
#
# Usage:
# ./install-skills.sh [options]
#
# Options:
# --agent <agent> Install target: codex (default) or claude
# --dry-run Preview changes without writing anything
# --category <cat> Only install plugins in dir_category=<cat>
# Valid values: biz, core, dev, integration, personal, req
# --exclude <name> Skip one install_name (repeatable)
# --force Overwrite even if local files were modified
# --cleanup Remove locally installed skills that are no longer in repo
# --list List all available plugins without installing
# --help Show this help
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILLS_DIR=""
COMMANDS_DIR=""
STATE_FILE=""
AGENT_TARGET="codex"
DRY_RUN=false
CATEGORY_FILTER=""
EXCLUDED_NAMES=()
FORCE=false
CLEANUP=false
LIST_ONLY=false
INSTALL_ACTION=false
# ── Colour helpers ─────────────────────────────────────────────────────────────
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
RESET='\033[0m'
info() { echo -e "${BLUE}[info]${RESET} $*"; }
ok() { echo -e "${GREEN}[ok]${RESET} $*"; }
warn() { echo -e "${YELLOW}[warn]${RESET} $*"; }
error() { echo -e "${RED}[error]${RESET} $*" >&2; }
dry() { echo -e "${YELLOW}[dry]${RESET} $*"; }
# ── Argument parsing ───────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--agent)
[[ $# -ge 2 ]] || { error "--agent requires codex or claude"; exit 1; }
AGENT_TARGET="$2"; shift ;;
--dry-run) DRY_RUN=true ;;
--force) FORCE=true ;;
--cleanup) CLEANUP=true ;;
--list) LIST_ONLY=true ;;
--category)
[[ $# -ge 2 ]] || { error "--category requires a value"; exit 1; }
CATEGORY_FILTER="$2"; shift ;;
--exclude)
[[ $# -ge 2 ]] || { error "--exclude requires an install_name"; exit 1; }
EXCLUDED_NAMES+=("$2"); shift ;;
--help|-h)
grep '^#' "$0" | grep -v '!/usr' | sed 's/^# \?//'
exit 0 ;;
*)
error "Unknown argument: $1"
exit 1 ;;
esac
shift
done
case "$AGENT_TARGET" in
codex)
# ~/.agents/skills is the current user-level Codex discovery location and
# is intentionally agent-neutral. Commands are installed as normal skills.
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.agents/skills}"
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.agents/.ai-proj-helper-installed-skills.json}"
;;
claude)
SKILLS_DIR="${AI_PROJ_HELPER_SKILLS_DIR:-${HOME}/.claude/skills}"
COMMANDS_DIR="${AI_PROJ_HELPER_COMMANDS_DIR:-${HOME}/.claude/commands}"
STATE_FILE="${AI_PROJ_HELPER_STATE_FILE:-${HOME}/.claude/.installed-skills.json}"
;;
*)
error "Unsupported agent: $AGENT_TARGET (expected codex or claude)"
exit 1
;;
esac
# ── State helpers (plain JSON via python3) ─────────────────────────────────────
state_get() {
# state_get <install_name> -> prints version or empty string
local name="$1"
if [[ -f "$STATE_FILE" ]]; then
python3 -c "
import json,sys
try:
d=json.load(open('$STATE_FILE'))
print(d.get('$name',{}).get('version',''))
except: pass
" 2>/dev/null || true
fi
}
state_digest() {
# state_digest <install_name> -> prints installed content digest or empty string
local name="$1"
if [[ -f "$STATE_FILE" ]]; then
python3 -c "
import json
try:
d=json.load(open('$STATE_FILE'))
print(d.get('$name',{}).get('content_digest',''))
except: pass
" 2>/dev/null || true
fi
}
state_set() {
# state_set <install_name> <version> <install_type> <content_digest>
local name="$1" ver="$2" itype="$3" digest="$4"
python3 -c "
import json,os
f='$STATE_FILE'
d=json.load(open(f)) if os.path.exists(f) else {}
d['$name']={'version':'$ver','install_type':'$itype','content_digest':'$digest','agent':'$AGENT_TARGET'}
json.dump(d,open(f,'w'),indent=2)
" 2>/dev/null
}
state_remove() {
local name="$1"
python3 -c "
import json,os
f='$STATE_FILE'
if not os.path.exists(f): exit()
d=json.load(open(f))
d.pop('$name',None)
json.dump(d,open(f,'w'),indent=2)
" 2>/dev/null
}
state_all_names() {
if [[ -f "$STATE_FILE" ]]; then
python3 -c "
import json
d=json.load(open('$STATE_FILE'))
for k in d: print(k)
" 2>/dev/null || true
fi
}
# ── Plugin discovery ───────────────────────────────────────────────────────────
find_plugins() {
find "$REPO_DIR" -path "*/skills-*/*-plugin/.claude-plugin/plugin.json" | sort
}
read_field() {
# read_field <json_file> <field>
python3 -c "import json,sys; d=json.load(open('$1')); print(d.get('$2',''))" 2>/dev/null || true
}
content_digest() {
# Stable digest for one command file or a complete skill directory.
python3 - "$1" <<'PY'
import hashlib
import os
import pathlib
import sys
target = pathlib.Path(sys.argv[1])
if not target.exists():
print("")
raise SystemExit
digest = hashlib.sha256()
files = [target] if target.is_file() else sorted(
path for path in target.rglob("*") if path.is_file() or path.is_symlink()
)
for path in files:
# A single-file command is renamed when installed for Claude. Hash its
# content under a stable logical name so source and target compare equally.
relative = "." if target.is_file() else path.relative_to(target).as_posix()
digest.update(relative.encode("utf-8"))
digest.update(b"\0")
if path.is_symlink():
digest.update(b"link\0")
digest.update(os.readlink(path).encode("utf-8"))
else:
digest.update(path.read_bytes())
digest.update(b"\0")
print(digest.hexdigest())
PY
}
is_compatible_subset() {
# True when every file in an existing legacy target also exists unchanged in
# the repository source. This safely upgrades old SKILL.md-only installs.
python3 - "$1" "$2" <<'PY'
import pathlib
import sys
source = pathlib.Path(sys.argv[1])
target = pathlib.Path(sys.argv[2])
if not source.is_dir() or not target.is_dir():
raise SystemExit(1)
for target_path in target.rglob("*"):
if target_path.is_dir():
continue
source_path = source / target_path.relative_to(target)
if not source_path.is_file() or target_path.is_symlink() != source_path.is_symlink():
raise SystemExit(1)
if target_path.is_symlink():
if target_path.readlink() != source_path.readlink():
raise SystemExit(1)
elif target_path.read_bytes() != source_path.read_bytes():
raise SystemExit(1)
raise SystemExit(0)
PY
}
# Resolve the actual source directory to rsync from.
# If skills/ has SKILL.md at the top level, use it directly.
# If skills/ has a single subdirectory (e.g. skills/dev-test/SKILL.md), use that subdirectory.
resolve_skills_src() {
local skills_dir="$1"
if [[ -f "$skills_dir/SKILL.md" ]]; then
echo "$skills_dir"
return
fi
# Find the first subdirectory that contains SKILL.md
local sub
sub="$(find "$skills_dir" -maxdepth 2 -name 'SKILL.md' | head -1)"
if [[ -n "$sub" ]]; then
echo "$(dirname "$sub")"
return
fi
echo "$skills_dir"
}
# ── Install a single plugin ────────────────────────────────────────────────────
install_plugin() {
INSTALL_ACTION=false
local json_path="$1"
local plugin_dir
plugin_dir="$(dirname "$(dirname "$json_path")")" # strip /.claude-plugin/plugin.json
local skills_dir="$plugin_dir/skills"
local install_name install_type dir_category version
install_name="$(read_field "$json_path" install_name)"
install_type="$(read_field "$json_path" install_type)"
dir_category="$(read_field "$json_path" dir_category)"
version="$(read_field "$json_path" version)"
local excluded
for excluded in ${EXCLUDED_NAMES[@]+"${EXCLUDED_NAMES[@]}"}; do
[[ "$install_name" == "$excluded" ]] && return
done
# Skip if no install metadata (legacy plugin without our new fields)
if [[ -z "$install_name" || -z "$install_type" ]]; then
warn "$(basename "$plugin_dir"): missing install_name/install_type, skipping"
return
fi
# Category filter
if [[ -n "$CATEGORY_FILTER" && "$dir_category" != "$CATEGORY_FILTER" ]]; then
return
fi
# Verify skills directory exists
if [[ ! -d "$skills_dir" ]]; then
warn "$install_name: no skills/ directory in plugin, skipping"
return
fi
local effective_install_type="$install_type"
if [[ "$AGENT_TARGET" == "codex" ]]; then
effective_install_type="skill"
fi
if [[ "$LIST_ONLY" == true ]]; then
echo " [$dir_category] $effective_install_type:$install_name v$version"
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
# A recorded content digest distinguishes repository updates from user edits.
# Legacy state is adopted automatically only when the target is missing or
# already identical to the repository source.
local current_version recorded_digest source_digest target_digest
current_version="$(state_get "$install_name")"
recorded_digest="$(state_digest "$install_name")"
source_digest="$(content_digest "$source_path")"
target_digest="$(content_digest "$target_path")"
if [[ "$FORCE" == false && -n "$target_digest" && "$target_digest" == "$source_digest" ]]; then
if [[ "$DRY_RUN" == false && ( "$current_version" != "$version" || "$recorded_digest" != "$source_digest" ) ]]; then
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
fi
return
fi
local legacy_subset=false
if [[ "$effective_install_type" == "skill" && -z "$recorded_digest" && -n "$target_digest" ]]; then
if is_compatible_subset "$source_path" "$target_path"; then
legacy_subset=true
fi
fi
if [[ "$FORCE" == false && -n "$target_digest" && "$legacy_subset" == false ]]; then
if [[ -z "$recorded_digest" || "$target_digest" != "$recorded_digest" ]]; then
warn "$install_name: local files were modified or have legacy unverified state — skipping (use --force once to adopt repository content)"
return
fi
fi
# Perform install
if [[ "$effective_install_type" == "command" ]]; then
# Single-file command → ~/.claude/commands/<name>.md
local src_md="$src_dir/SKILL.md"
if [[ ! -f "$src_md" ]]; then
warn "$install_name: SKILL.md not found, skipping"
return
fi
if [[ "$DRY_RUN" == true ]]; then
dry "$install_name$COMMANDS_DIR/${install_name}.md"
INSTALL_ACTION=true
else
mkdir -p "$COMMANDS_DIR"
cp "$src_md" "$COMMANDS_DIR/${install_name}.md"
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
ok "$install_name → command (v$version)"
INSTALL_ACTION=true
fi
else
# Standard skill directory → the selected agent's discovery root.
local dst_dir="$SKILLS_DIR/$install_name"
if [[ "$DRY_RUN" == true ]]; then
dry "$install_name$dst_dir/"
INSTALL_ACTION=true
else
mkdir -p "$dst_dir"
# rsync resolved source (handles nested skills/ structures)
rsync -a --checksum --delete "$src_dir/" "$dst_dir/"
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
ok "$install_name → skill (v$version)"
INSTALL_ACTION=true
fi
fi
}
# ── Cleanup removed plugins ────────────────────────────────────────────────────
cleanup_removed() {
if [[ "$CLEANUP" == false ]]; then
return
fi
info "Checking for removed plugins to clean up..."
# Collect all install_names still in repo
local repo_names=()
while IFS= read -r json_path; do
local name
name="$(read_field "$json_path" install_name)"
[[ -n "$name" ]] && repo_names+=("$name")
done < <(find_plugins)
# Check state file for installed plugins no longer in repo
while IFS= read -r installed_name; do
local found=false
for repo_name in "${repo_names[@]}"; do
[[ "$repo_name" == "$installed_name" ]] && found=true && break
done
if [[ "$found" == false ]]; then
local itype
itype="$(python3 -c "import json; d=json.load(open('$STATE_FILE')); print(d.get('$installed_name',{}).get('install_type',''))" 2>/dev/null || true)"
if [[ "$DRY_RUN" == true ]]; then
dry "Would remove: $installed_name ($itype)"
else
if [[ "$itype" == "command" ]]; then
rm -f "$COMMANDS_DIR/${installed_name}.md"
else
rm -rf "${SKILLS_DIR:?}/$installed_name"
fi
state_remove "$installed_name"
ok "Removed: $installed_name"
fi
fi
done < <(state_all_names)
}
# ── Main ───────────────────────────────────────────────────────────────────────
main() {
if [[ "$LIST_ONLY" == true ]]; then
info "Available plugins in $REPO_DIR:"
local count=0
while IFS= read -r json_path; do
install_plugin "$json_path"
((count++)) || true
done < <(find_plugins)
echo ""
info "Total: $count plugins"
return
fi
info "Installing skills for $AGENT_TARGET from: $REPO_DIR"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written"
[[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER"
local installed=0
while IFS= read -r json_path; do
install_plugin "$json_path"
if [[ "$INSTALL_ACTION" == true ]]; then
((installed++)) || true
fi
done < <(find_plugins)
cleanup_removed
echo ""
if [[ "$DRY_RUN" == true ]]; then
info "Dry run complete. $installed plugins would be installed/updated."
else
info "Done. $installed plugins installed/updated."
info "State saved to: $STATE_FILE"
fi
}
main "$@"
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "biz-contract",
"install_type": "skill",
"dir_category": "biz"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "biz-ops",
"install_type": "skill",
"dir_category": "biz"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "biz-plan",
"install_type": "skill",
"dir_category": "biz"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "finance",
"install_type": "skill",
"dir_category": "biz"
}
@@ -4,5 +4,8 @@
"version": "2.0.1",
"author": {
"name": "qiudl"
}
},
"install_name": "ai-proj",
"install_type": "skill",
"dir_category": "core"
}
@@ -0,0 +1,11 @@
{
"name": "pm-ask-plugin",
"description": "基于真实数据的项目问答 /ask。必须引用 MCP/git 真实数据,禁止编造",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "pm-ask",
"install_type": "skill",
"dir_category": "core"
}
+126
View File
@@ -0,0 +1,126 @@
---
name: pm-ask
description: 基于 ai-proj MCP + git log 真实数据的项目问答。禁止编造。当用户说"/ask"、"问一下"、"统计一下"、"最近多少"、"谁做了多少"、"本月进度"等项目相关问题时自动激活。
---
# pm-ask Skill — 基于真实数据的项目问答
借鉴自 devflow-claude `/pm:ask`。源自 REQ-20260416-0017 P1-9。
## 核心原则(铁律)
> **必须基于真实数据,禁止编造。**
- 所有数字必须来自 MCP API / git log / 文件系统的**真实查询**
- 必须引用具体 **REQ-XXX / commit-sha / 文件路径**
- 数据不足时**明确说明缺失**,不得"大概"、"估计"
- **区分事实和推测**:推测必须用 `⚠️ 推测:` 前缀
## 适用场景
| 用户问题示例 | 数据源 | 工具 |
|-------------|--------|------|
| "qiudl 最近一个月提交了多少代码?" | git log | `git log --author=qiudl --since='30 days ago'` |
| "本月完成了哪些需求?" | ai-proj MCP | `list_requirements --status=completed` |
| "当前进行中的需求有多少?分别是?" | MCP | `list_requirements --status=in_progress` |
| "REQ-xxx 拆了哪些任务?完成多少?" | MCP | `get_requirement_tasks` |
| "backend/services/user.go 最近谁在改?" | git log | `git log --follow -- file` |
| "和上月对比,产出是多了还是少了?" | git log + MCP | 组合查询 |
| "生成一封客户更新邮件" | MCP + git | 聚合后结构化输出 |
## 工作流
### 1. 意图分析(确定数据源)
关键词 → 数据源映射:
| 关键词 | 数据源 |
|--------|--------|
| 需求 / REQ / 需求状态 | ai-proj MCP `list_requirements` / `find_requirement` |
| 任务 / todo / 待办 | MCP `list_tasks` / `find_task` |
| 提交 / commit / 代码量 / 贡献 | `git log` |
| 文件修改 / 变更 | `git log --follow` / `git blame` |
| 进度 / 完成率 | MCP `get_project_stats` / `get_requirement_statistics` |
| 周报 / 月报 | 组合:MCP + git log + 时间范围过滤 |
| 风险 / 停滞 / 延期 | MCP + 时间戳分析 |
### 2. 执行查询
**查询前必须声明**
```
📊 正在查询数据源:
- ai-proj MCP: list_requirements (status=in_progress)
- git log: --author=qiudl --since='2026-04-01'
```
### 3. 生成答案(强约束)
**结构**
```
## 直接答案
<一句话结论,附数字>
## 数据来源
- <查询语句 1> → <结果摘要>
- <查询语句 2> → <结果摘要>
## 细节
<表格 / 列表,每条必有 REQ-XXX 或 commit-sha>
## ⚠️ 数据缺失(如有)
<说明哪些数据无法获取>
```
### 4. 禁止事项
**不允许的答案:**
- "大概 XX 个左右"(必须精确数字或说明"无法精确统计")
- "主要在做 XX"(必须列具体 REQ
- "应该是 XX"(推测必须 `⚠️ 推测:` 标记)
- "最近进度不错"(必须数据支持)
**违反时自我纠正**:如果生成答案时发现缺少真实数据引用,重新查询,不要猜测。
## 输出适配
根据问题类型自动切换输出格式:
| 问题类型 | 输出格式 |
|---------|---------|
| 数量统计 | 数字 + 列表 |
| 对比分析 | 表格 |
| 进度追踪 | 进度条 / 完成率 |
| 历史回顾 | 时间线 |
| 生成文档(邮件/报告) | 完整结构化文本 |
## 与其他 skill 的关系
| 相关 skill | 分工 |
|-----------|------|
| `ai-proj` | 执行 MCP 调用(本 skill 的底层) |
| `req-workflow` | 需求生命周期管理(本 skill 只读查询) |
| `dev-review` | 代码评审(本 skill 提供评审背景数据) |
## 受众适配(借鉴自 devflow report-generator
生成报告时按受众调整:
| 受众 | 侧重 | 禁用 |
|------|------|------|
| 高层 | 交付物、里程碑、风险 | 技术细节 |
| 客户 | 功能价值、上线时间 | 内部术语 |
| 内部团队 | 技术细节、Blocker | - |
| 新人 | 背景上下文、术语表 | 假设已知 |
## Memory 使用规则
本 skill **仅基于当前数据查询**生成答案。memory 可用于:
- 记住用户偏好的输出格式(如"喜欢表格不喜欢列表")
- 记住常用的过滤条件(如"默认看 qiudl 的提交"
**禁止**:用 memory 里的历史数据"缓存"事实类答案(这会导致过时数据)。每次问都要重新查。
## 参考
- devflow-claude: `plugins/pm/commands/ask.md`
- REQ-20260416-0017 P1-9
@@ -0,0 +1,11 @@
{
"name": "pm-risk-plugin",
"description": "三维度项目风险扫描:需求层/代码层/流程层。当用户说'/risk'、'风险扫描'、'有什么风险'时自动激活",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "pm-risk",
"install_type": "skill",
"dir_category": "core"
}
+154
View File
@@ -0,0 +1,154 @@
---
name: pm-risk
description: 三维度项目风险扫描(需求层/代码层/流程层)。当用户说"/risk"、"风险扫描"、"有什么风险"、"哪些需求停滞了"、"分支健康"时自动激活。
---
# pm-risk Skill — 三维度风险扫描
借鉴自 devflow-claude `/pm:risk`。源自 REQ-20260416-0017 P1-10。
## 命令
```
/risk [--save]
```
- 不加参数:扫描并展示报告
- `--save`:保存到思源笔记
## 扫描维度
### 维度 1: 需求层
通过 ai-proj MCP 查询需求状态:
| 检测项 | 数据源 | 严重 | 警告 | 提示 |
|--------|--------|------|------|------|
| 需求停滞 | `list_requirements` + `updated_at` | >14 天无更新 | >7 天无更新 | >3 天无更新 |
| 草稿滞留 | `list_requirements --status=draft` | >30 天 | >14 天 | >7 天 |
| 开发中无提交 | 需求关联分支 + `git log` | >7 天无 commit | >3 天 | - |
| 测试中超期 | `list_requirements --delivery_stage=testing` | >14 天 | >7 天 | - |
| 已批准未开发 | `list_requirements --status=approved` | >30 天 | >14 天 | - |
**查询方式**
```
mcp__ai-proj__list_requirements(status=in_progress)
→ 遍历每个需求的 updated_at,计算距今天数
```
### 维度 2: 代码层
通过 git 命令分析:
| 检测项 | 命令 | 严重 | 警告 |
|--------|------|------|------|
| 未合并分支过久 | `git branch -r --no-merged origin/develop` + `git log -1` | >14 天无 commit | >7 天 |
| 频繁修复文件 | `git log --diff-filter=M --name-only` 近 30 天 | 同文件 >5 次 fix commit | >3 次 |
| 提交频率下降 | 本周 vs 上周 commit 数 | 下降 >70% | 下降 >50% |
| 大文件提交 | `git log --diff-filter=A --name-only` | >10MB 文件 | >5MB |
**查询方式**
```bash
# 未合并分支
git for-each-ref --sort=-committerdate --format='%(refname:short) %(committerdate:short)' refs/remotes/origin/ | grep -v 'main\|develop\|HEAD'
# 频繁修复
git log --since='30 days ago' --grep='fix' --diff-filter=M --name-only --pretty=format:'' | sort | uniq -c | sort -rn | head -10
# 提交频率
THIS_WEEK=$(git log --since='7 days ago' --oneline | wc -l)
LAST_WEEK=$(git log --since='14 days ago' --until='7 days ago' --oneline | wc -l)
```
### 维度 3: 流程层
通过 MCP + git 交叉分析:
| 检测项 | 检测方法 | 级别 |
|--------|---------|------|
| 跳过评审直接开发 | 需求 status 从 draft 直接跳到 in_progress(没有 approved 记录) | 警告 |
| PR 无评审直接合并 | Gitea API 查 PR 无 review approve 就 merge | 警告 |
| 测试门禁跳过 | delivery_stage 从 dev 跳到 released(没有 testing | 严重 |
| 直接推 main | `git log --first-parent origin/main` 非 merge commit | 严重 |
## 风险级别定义
| 级别 | 图标 | 含义 | 响应 |
|------|------|------|------|
| 严重 | 🔴 | 立即关注 | 当天处理 |
| 警告 | 🟡 | 近期需处理 | 3 天内处理 |
| 提示 | 🔵 | 值得留意 | 下次迭代关注 |
## 输出模板
```markdown
# 🔍 风险扫描报告
**扫描时间**: YYYY-MM-DD HH:MM CST
**扫描范围**: 项目 ai-proj
## 概览
| 维度 | 🔴 严重 | 🟡 警告 | 🔵 提示 |
|------|---------|---------|---------|
| 需求层 | X | X | X |
| 代码层 | X | X | X |
| 流程层 | X | X | X |
## 🔴 严重风险
### [S1] REQ-20260410-0001 停滞 18 天
- **需求**: REQ-20260410-0001 用户积分管理
- **状态**: in_progress
- **最后更新**: 2026-04-01
- **建议**: 联系负责人确认是否阻塞,必要时降级或暂停
### [S2] ...
## 🟡 警告
### [W1] 分支 feat/xxx 14 天未合并
- **分支**: feat/old-feature
- **最后 commit**: 2026-04-05
- **建议**: 确认是否放弃,清理或合并
## 🔵 提示
...
## ✅ 一切正常的维度
- (无严重/警告时显示此段)
---
**下次扫描建议**: 每周一早会前执行 `/risk`
```
## 无风险时的输出
```
✅ 风险扫描完成 — 一切正常
扫描了 X 个需求、Y 个分支、Z 条提交,未发现风险项。
下次扫描建议:下周一
```
## 定期执行建议
- **手动**:每周一早会前 `/risk`
- **自动**:可配合 `/loop 7d /risk --save` 定期扫描并保存到思源笔记
## 与其他 skill 的关系
| skill | 协作 |
|-------|------|
| `pm-ask` | risk 发现问题后,用 `/ask` 深入分析 |
| `req-workflow` | risk 发现流程违规后,用 `/req` 修正 |
| `ai-proj` | 底层 MCP 数据查询 |
## 参考
- devflow-claude: `plugins/pm/commands/risk.md`
- REQ-20260416-0017 P1-10
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "publish",
"install_type": "skill",
"dir_category": "core"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "agent-browser",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,11 @@
{
"name": "agent-swarm-plugin",
"description": "Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "agent-swarm",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,406 @@
---
name: agent-swarm
description: Multi-agent orchestration using OpenAI Swarm patterns. Coordinate specialized agents for complex development workflows with handoffs and context sharing.
---
# Agent Swarm - Multi-Agent Orchestration
基于 OpenAI Swarm 设计模式的多智能体协作系统,用于复杂开发任务的智能分解与协调。
## 核心概念
### 1. Agent(智能体)
每个 Agent 是具有特定职责的专家:
- **Instructions**: Agent 的角色定义和行为准则
- **Functions**: Agent 可以调用的工具函数
- **Handoffs**: 何时移交给其他 Agent
### 2. Handoff(任务移交)
Agent 之间的控制权转移机制:
- 当前 Agent 完成自己的职责
- 识别需要其他专长
- 移交给最合适的 Agent
### 3. Context Variables(上下文变量)
跨 Agent 共享的状态:
- 项目目录
- 技术栈信息
- 当前进度
- 发现的问题
---
## 预定义 Agent
### 1. Architect Agent(架构师)
**职责**: 理解需求、技术选型、设计系统架构
**何时使用**:
- 用户描述新功能或系统
- 需要技术方案设计
- 需要架构评审
**工具**:
- Read codebase
- Grep patterns
- 设计文档生成
**Handoff to**:
- Coder Agent(开始编码)
- Reviewer Agent(评审设计)
---
### 2. Coder Agent(编码者)
**职责**: 实现功能、编写代码、修复 bug
**何时使用**:
- 架构师完成设计
- 用户提出 bug 修复
- 需要代码重构
**工具**:
- Edit files
- Write files
- Git operations
**Handoff to**:
- Tester Agent(代码完成后)
- Architect Agent(遇到设计问题)
---
### 3. Tester Agent(测试员)
**职责**: 编写测试、运行测试、验证功能
**何时使用**:
- 代码编写完成
- 需要测试覆盖
- 验证 bug 修复
**工具**:
- Run tests
- Write test cases
- Coverage reports
**Handoff to**:
- Deployer Agent(测试通过)
- Coder Agent(发现问题)
---
### 4. Deployer Agent(部署员)
**职责**: 构建镜像、部署服务、监控上线
**何时使用**:
- 测试全部通过
- 需要发布到环境
- 需要回滚版本
**工具**:
- Docker build
- SSH deployment
- Health checks
**Handoff to**:
- Monitor Agent(部署完成)
- Coder Agent(部署失败)
---
### 5. Reviewer Agent(评审员)
**职责**: 代码审查、文档审查、安全检查
**何时使用**:
- PR 创建后
- 重要功能完成
- 需要质量把关
**工具**:
- Diff analysis
- Security scan
- Best practices check
**Handoff to**:
- Coder Agent(需要修改)
- Deployer Agent(审查通过)
---
## 使用方法
### 基本调用
```bash
/swarm start "在 new-ai-proj 中实现任务批量删除功能"
```
**执行流程**:
1. **Architect** 分析需求 → 设计 API 和前端交互
2. **Coder** 实现后端 API → 实现前端 UI
3. **Tester** 编写单元测试 → 运行测试
4. **Reviewer** 代码审查 → 安全检查
5. **Deployer** 部署到 staging → 验证功能
---
### 指定起始 Agent
```bash
/swarm coder "修复 backend/handlers/task_handler.go 的空指针 bug"
```
直接从 Coder Agent 开始,跳过架构设计阶段。
---
### 传递上下文
```bash
/swarm start "优化数据库查询性能" \
--context project=/Users/coolbuy-dev/coding/new-ai-proj \
--context stack=Go,PostgreSQL,Redis \
--context module=backend/services
```
---
### 查看执行轨迹
```bash
/swarm trace
```
显示 Agent 调用链:
```
Architect → analyzed requirements (3 min)
↓ handoff: "Design complete, ready for implementation"
Coder → implemented 5 files (12 min)
↓ handoff: "Code complete, needs testing"
Tester → wrote 8 test cases, all passed (5 min)
↓ handoff: "Tests passed, ready for review"
Reviewer → approved with 2 suggestions (2 min)
↓ handoff: "Approved, ready for deployment"
Deployer → deployed to staging, health check OK (3 min)
```
---
## 配置文件
### swarm.yaml
在项目根目录创建 `swarm.yaml` 自定义 Agent 行为:
```yaml
agents:
architect:
instructions: |
你是系统架构师,专注于 Go + Vue.js 技术栈。
遵循 RESTful API 设计原则。
考虑性能、安全性、可维护性。
max_turns: 5
coder:
instructions: |
你是 Go 后端工程师和 Vue.js 前端工程师。
编写清晰、简洁、高性能的代码。
遵循项目现有代码风格。
tools:
- Edit
- Write
- Bash
max_turns: 10
tester:
instructions: |
你是测试工程师,编写全面的测试用例。
确保边界条件、错误处理、并发安全。
tools:
- Bash
- Write
test_command: "go test ./... -v"
max_turns: 5
context_variables:
project_root: /Users/coolbuy-dev/coding/new-ai-proj
backend_lang: Go 1.21
frontend_framework: Vue 3
database: PostgreSQL 15
deployment_target: staging.ai.pipexerp.com
```
---
## 高级功能
### 1. 自定义 Agent
```yaml
agents:
database-optimizer:
instructions: |
你是数据库性能优化专家。
分析慢查询、优化索引、设计缓存策略。
functions:
- explain_analyze
- create_index
- cache_design
handoff_to:
- coder # 实现优化方案
```
---
### 2. 条件 Handoff
```yaml
handoff_rules:
- from: tester
to: coder
condition: "test_pass_rate < 90%"
message: "测试失败率超过 10%,需要修复"
- from: tester
to: deployer
condition: "test_pass_rate == 100%"
message: "所有测试通过,可以部署"
```
---
### 3. 并行 Agent
对于独立任务,多个 Agent 可以并行工作:
```bash
/swarm parallel \
"coder: 实现后端 API" \
"coder: 实现前端 UI" \
"tester: 编写 API 测试"
```
---
## 与 Remote Coding 集成
在 OpenClaw 中调用本地 Claude Code 执行 Swarm 工作流:
```bash
# OpenClaw 调用 Melbourne Claude Code
ssh melbourne "cd /Users/coolbuy-dev/coding/new-ai-proj && \
/opt/homebrew/bin/claude --dangerously-skip-permissions \
-p '/swarm start 实现任务批量删除功能'"
```
---
## 实际案例
### 案例 1: 新功能开发
**任务**: "为 AI-Proj 实现需求批量导出功能"
**执行过程**:
1. **Architect**:
- 分析需求:导出格式(Excel/PDF)、筛选条件、数据脱敏
- 设计 API: `POST /api/v1/requirements/export`
- 设计前端:导出按钮、进度条、下载链接
2. **Coder**:
- 后端实现 export service
- 前端实现导出 UI 组件
- 集成 file download 功能
3. **Tester**:
- 测试大量数据导出(1000+ 需求)
- 测试并发导出
- 测试下载失败重试
4. **Reviewer**:
- 检查文件大小限制
- 检查内存泄漏风险
- 检查数据权限控制
5. **Deployer**:
- 部署到 staging
- 验证导出功能
- 监控资源使用
---
### 案例 2: Bug 修复
**任务**: "修复任务详情页加载缓慢问题"
**执行过程**:
1. **Architect**:
- 分析性能瓶颈:N+1 查询问题
- 设计优化方案:使用 JOIN 和预加载
2. **Coder**:
- 优化数据库查询
- 添加 Redis 缓存
- 更新前端数据获取逻辑
3. **Tester**:
- 性能测试:加载时间从 3s → 300ms
- 并发测试:100 用户同时访问
- 缓存一致性测试
4. **Deployer**:
- 灰度发布到 10% 用户
- 监控性能指标
- 全量发布
---
## 最佳实践
1. **明确任务范围**: 复杂任务交给 Swarm,简单任务直接执行
2. **合理设置 max_turns**: 避免 Agent 陷入死循环
3. **记录 Handoff 原因**: 便于追溯和调试
4. **定期审查轨迹**: 优化 Agent 协作流程
5. **利用 Context Variables**: 避免重复传递信息
---
## 故障排查
| 问题 | 原因 | 解决方案 |
|------|------|----------|
| Agent 一直循环 | max_turns 设置过大 | 降低 max_turns,添加明确的 handoff 条件 |
| Handoff 失败 | 目标 Agent 未定义 | 检查 swarm.yaml 配置 |
| 上下文丢失 | Context Variables 未传递 | 在 handoff 时显式传递 context |
| 执行太慢 | 串行执行可并行任务 | 使用 `/swarm parallel` |
---
## 与其他 Skills 集成
- **dev-coding**: Coder Agent 使用 dev-coding 的编码规范
- **dev-test**: Tester Agent 使用 dev-test 的测试策略
- **ops-tools**: Deployer Agent 使用 ops-tools 进行部署
- **ai-proj**: 所有 Agent 使用 ai-proj MCP 进行任务同步
---
## 命令速查
| 命令 | 功能 |
|------|------|
| `/swarm start <task>` | 启动 Swarm 工作流(从 Architect 开始) |
| `/swarm <agent> <task>` | 从指定 Agent 开始 |
| `/swarm parallel <tasks>` | 并行执行多个任务 |
| `/swarm trace` | 查看执行轨迹 |
| `/swarm config` | 显示当前配置 |
| `/swarm agents` | 列出所有可用 Agent |
| `/swarm stop` | 终止当前 Swarm 执行 |
---
## 参考资料
- [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)
@@ -0,0 +1,11 @@
{
"name": "ai-chat-plugin",
"description": "AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "ai-chat",
"install_type": "skill",
"dir_category": "dev"
}
+537
View File
@@ -0,0 +1,537 @@
---
name: ai-chat
description: AI Chat 测试与管理。发送消息测试 AI Chat 工具调用链路,管理工具开关和 Provider 配置,支持 local/staging 环境切换。
arguments: <subcommand> [args]
---
# AI Chat Skill
测试和管理 Coolbuy PaaS AI Chat 服务的 Claude Code skill。
## Quick Reference
| 命令 | 用途 |
|------|------|
| `/ai-chat send <message>` | 发送消息到 AI Chat,实时显示工具调用 + AI 回复 |
| `/ai-chat env [local\|staging]` | 切换/查看目标环境(默认 local) |
| `/ai-chat tools [category]` | 列出当前环境已注册的工具 |
| `/ai-chat config` | 查看 AI 配置(Provider、工具开关等) |
| `/ai-chat history` | 显示本次会话的历史消息 |
---
## Environment Config
两套环境,通过 `/ai-chat env` 切换:
| 环境 | Auth URL | AI URL | 登录账号 |
|------|----------|--------|----------|
| **local** (默认) | `http://localhost:7089` | `http://localhost:7092` | lining_admin / admin123 |
| **staging** | `http://39.105.150.219:7089` | `http://39.105.150.219:7092` | lining_admin / admin123 |
### 状态文件
环境状态保存在 `/tmp/ai-chat-state.json`,格式:
```json
{
"env": "local",
"token": "eyJ...",
"token_env": "local",
"history": []
}
```
---
## Commands
### /ai-chat env
**切换或查看当前环境。**
用法:
- `/ai-chat env` — 显示当前环境
- `/ai-chat env local` — 切换到本地环境
- `/ai-chat env staging` — 切换到 staging 环境
实现步骤:
1. 读取 `/tmp/ai-chat-state.json`(不存在则默认 `{"env":"local","history":[]}`
2. 如果提供了参数,更新 `env` 字段并清空 `token`(环境变了 token 失效)
3. 写回状态文件
4. 输出当前环境信息表格
---
### /ai-chat send
**发送消息到 AI Chat 并实时显示流式响应。**
用法:`/ai-chat send <message>`
实现步骤:
#### Step 1: 读取状态
```bash
# 读取状态文件
cat /tmp/ai-chat-state.json 2>/dev/null || echo '{"env":"local","history":[]}'
```
确定环境变量:
- **local**: `AUTH_URL=http://localhost:7089`, `AI_URL=http://localhost:7092`
- **staging**: `AUTH_URL=http://39.105.150.219:7089`, `AI_URL=http://39.105.150.219:7092`
#### Step 2: 获取 Token
如果状态文件中没有 token 或 `token_env` 与当前 `env` 不匹配,执行登录:
```bash
curl -s -X POST "$AUTH_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"lining_admin","password":"admin123"}'
```
从响应中提取 `access_token`
```bash
# 响应格式
# {"access_token":"eyJ...","refresh_token":"...","token_type":"Bearer","expires_in":7200,"user_info":{...}}
```
`python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])"` 提取 token。
将 token 和 token_env 保存到状态文件。
#### Step 3: 构造请求并发送 SSE 流
```bash
curl -s -N -X POST "$AI_URL/api/v1/ai/chat/stream" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"message\":\"$MSG\",\"history\":$HISTORY}" 2>&1
```
**重要**: `history` 字段传入之前的会话历史(从状态文件读取),实现多轮对话。
#### Step 4: 解析 SSE 事件
用 Python 脚本解析 SSE 流(比 bash while read 更可靠):
```python
#!/usr/bin/env python3
"""解析 AI Chat SSE 流并格式化输出"""
import sys, json
full_content = ""
tool_calls = []
for line in sys.stdin:
line = line.strip()
if not line.startswith("data:"):
continue
data_str = line[5:].strip()
if not data_str:
continue
try:
event = json.loads(data_str)
except json.JSONDecodeError:
continue
evt_type = event.get("type", "")
if evt_type == "content":
chunk = event.get("content", "")
full_content += chunk
# 实时输出内容片段
sys.stdout.write(chunk)
sys.stdout.flush()
elif evt_type == "tool_call":
tc = event.get("tool_call", {})
tool_name = tc.get("name", "unknown")
tool_args = tc.get("arguments", {})
tool_id = tc.get("id", "")
tool_calls.append({"id": tool_id, "name": tool_name})
# 输出工具调用标记
args_str = json.dumps(tool_args, ensure_ascii=False)
if len(args_str) > 200:
args_str = args_str[:200] + "..."
print(f"\n🔧 Tool Call: {tool_name}", file=sys.stderr)
print(f" Args: {args_str}", file=sys.stderr)
elif evt_type == "tool_result":
tr = event.get("tool_result", {})
tool_name = tr.get("name", "unknown")
content = tr.get("content", "")
is_error = tr.get("is_error", False)
# 截断长结果
if len(content) > 500:
content = content[:500] + f"... ({len(content)} chars total)"
status = "❌ Error" if is_error else "✅ Result"
print(f" {status} [{tool_name}]: {content}", file=sys.stderr)
elif evt_type == "done":
usage = event.get("usage") or {}
prompt_t = usage.get("prompt_tokens", 0)
completion_t = usage.get("completion_tokens", 0)
total_t = usage.get("total_tokens", 0)
print(f"\n\n--- Done ---", file=sys.stderr)
if total_t > 0:
print(f"Tokens: {prompt_t} prompt + {completion_t} completion = {total_t} total", file=sys.stderr)
if tool_calls:
print(f"Tool calls: {len(tool_calls)} ({', '.join(tc['name'] for tc in tool_calls)})", file=sys.stderr)
elif evt_type == "error":
err = event.get("error", "unknown error")
print(f"\n❌ Error: {err}", file=sys.stderr)
# 输出换行
print()
# 将 full_content 输出到 fd 3 用于状态更新(如果 fd 3 打开)
try:
with open("/tmp/ai-chat-response.txt", "w") as f:
f.write(full_content)
except:
pass
```
#### Step 5: 更新会话历史
发送完成后,将用户消息和 AI 回复追加到状态文件的 `history` 数组中:
```json
[
{"role": "user", "content": "<用户消息>"},
{"role": "assistant", "content": "<AI 完整回复>"}
]
```
#### 完整 bash 执行流程
```bash
#!/usr/bin/env bash
set -euo pipefail
MSG="$1"
STATE_FILE="/tmp/ai-chat-state.json"
# 1. 读取状态
if [ -f "$STATE_FILE" ]; then
STATE=$(cat "$STATE_FILE")
else
STATE='{"env":"local","history":[]}'
fi
ENV=$(echo "$STATE" | python3 -c "import json,sys; print(json.load(sys.stdin).get('env','local'))")
TOKEN=$(echo "$STATE" | python3 -c "import json,sys; print(json.load(sys.stdin).get('token',''))")
TOKEN_ENV=$(echo "$STATE" | python3 -c "import json,sys; print(json.load(sys.stdin).get('token_env',''))")
HISTORY=$(echo "$STATE" | python3 -c "import json,sys; print(json.dumps(json.load(sys.stdin).get('history',[])))")
# 2. 确定 URL
if [ "$ENV" = "staging" ]; then
AUTH_URL="http://39.105.150.219:7089"
AI_URL="http://39.105.150.219:7092"
else
AUTH_URL="http://localhost:7089"
AI_URL="http://localhost:7092"
fi
# 3. 获取 token(如果需要)
if [ -z "$TOKEN" ] || [ "$TOKEN_ENV" != "$ENV" ]; then
echo "🔐 Logging in to $ENV environment..."
LOGIN_RESP=$(curl -s -X POST "$AUTH_URL/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"lining_admin","password":"admin123"}')
TOKEN=$(echo "$LOGIN_RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('access_token',d.get('data',{}).get('access_token','')))")
if [ -z "$TOKEN" ]; then
echo "❌ Login failed: $LOGIN_RESP"
exit 1
fi
echo "✅ Login successful"
# 更新状态中的 token
STATE=$(echo "$STATE" | python3 -c "
import json,sys
s=json.load(sys.stdin)
s['token']='$TOKEN'
s['token_env']='$ENV'
print(json.dumps(s,ensure_ascii=False))
")
fi
# 4. 发送 SSE 请求并解析
echo ""
echo "📤 Sending to $ENV: $MSG"
echo "---"
# 转义消息中的特殊字符
MSG_JSON=$(python3 -c "import json; print(json.dumps('$MSG'))")
curl -s -N -X POST "$AI_URL/api/v1/ai/chat/stream" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"message\":$MSG_JSON,\"history\":$HISTORY}" 2>&1 | python3 -c "
import sys, json
full_content = ''
tool_calls = []
for line in sys.stdin:
line = line.strip()
if not line.startswith('data:'):
continue
data_str = line[5:].strip()
if not data_str:
continue
try:
event = json.loads(data_str)
except json.JSONDecodeError:
continue
evt_type = event.get('type', '')
if evt_type == 'content':
chunk = event.get('content', '')
full_content += chunk
sys.stdout.write(chunk)
sys.stdout.flush()
elif evt_type == 'tool_call':
tc = event.get('tool_call', {})
tool_name = tc.get('name', 'unknown')
tool_args = tc.get('arguments', {})
tool_calls.append({'name': tool_name})
args_str = json.dumps(tool_args, ensure_ascii=False)
if len(args_str) > 200:
args_str = args_str[:200] + '...'
print(f'\n🔧 Tool Call: {tool_name}', file=sys.stderr)
print(f' Args: {args_str}', file=sys.stderr)
elif evt_type == 'tool_result':
tr = event.get('tool_result', {})
tool_name = tr.get('name', 'unknown')
content = tr.get('content', '')
is_error = tr.get('is_error', False)
if len(content) > 500:
content = content[:500] + f'... ({len(content)} chars total)'
status = '❌' if is_error else '✅'
print(f' {status} [{tool_name}]: {content}', file=sys.stderr)
elif evt_type == 'done':
usage = event.get('usage') or {}
pt = usage.get('prompt_tokens', 0)
ct = usage.get('completion_tokens', 0)
tt = usage.get('total_tokens', 0)
print(f'\n\n--- Done ---', file=sys.stderr)
if tt > 0:
print(f'Tokens: {pt} prompt + {ct} completion = {tt} total', file=sys.stderr)
if tool_calls:
names = ', '.join(tc['name'] for tc in tool_calls)
print(f'Tool calls: {len(tool_calls)} ({names})', file=sys.stderr)
elif evt_type == 'error':
err = event.get('error', 'unknown error')
print(f'\n❌ Error: {err}', file=sys.stderr)
print()
with open('/tmp/ai-chat-response.txt', 'w') as f:
f.write(full_content)
"
# 5. 更新历史
RESPONSE=$(cat /tmp/ai-chat-response.txt 2>/dev/null || echo "")
python3 -c "
import json
state_file = '$STATE_FILE'
try:
with open(state_file) as f:
state = json.load(f)
except:
state = {'env': 'local', 'history': []}
msg = $MSG_JSON
resp = '''$RESPONSE'''
state['history'].append({'role': 'user', 'content': msg})
if resp:
state['history'].append({'role': 'assistant', 'content': resp})
state['token'] = '$TOKEN'
state['token_env'] = '$ENV'
with open(state_file, 'w') as f:
json.dump(state, f, ensure_ascii=False, indent=2)
"
echo ""
echo "💬 History: $(echo "$STATE" | python3 -c "import json,sys; h=json.load(sys.stdin).get('history',[]); print(len(h)//2 + 1)") messages in session"
```
**重要注意事项**
- 上面的脚本是逻辑参考,**不要**原样执行。Claude Code 应按步骤逐一执行 bash 命令。
- 消息中的引号和特殊字符需要用 python3 json.dumps 转义。
- 如果 token 过期(401 响应),自动重新登录。
- SSE 超时设置 `--max-time 120`
---
### /ai-chat tools
**列出当前环境已注册的工具。**
用法:
- `/ai-chat tools` — 列出所有工具
- `/ai-chat tools <category>` — 按分类过滤
实现步骤:
1. 读取状态获取环境和 token(必要时先登录)
2. 发送请求:
```bash
# 列出所有工具
curl -s "$AI_URL/api/v1/ai/tools" \
-H "Authorization: Bearer $TOKEN"
# 按分类过滤
curl -s "$AI_URL/api/v1/ai/tools?category=order" \
-H "Authorization: Bearer $TOKEN"
```
3. 响应格式:
```json
{
"tools": [
{
"name": "list_orders",
"description": "查询订单列表",
"category": "order",
"enabled": true,
"parameters": {...}
}
]
}
```
4. 按 category 分组,输出表格:
```
📋 AI Tools (142 total)
order (15 tools)
├── list_orders 查询订单列表
├── get_order_detail 获取订单详情
└── ...
product (12 tools)
├── list_products 查询商品列表
└── ...
```
已知工具分类:order, product, sku, inventory, task, brand, requirement, customer, dashboard, distribution, finance, discount, channel, approval, organization, feature_gap
---
### /ai-chat config
**查看 AI 服务配置。**
实现步骤:
1. 根据当前环境读取对应配置文件:
- **local**: 读取 `ai-service/api/etc/ai-api-local.yaml`
- **staging**: SSH 到 staging 读取 `/opt/coolbuy/configs/ai-api.yaml`
2. 显示关键配置项:
- AI Provider 和 Model
- 各工具分类的开关状态
- API 端口
3. 输出格式:
```
⚙️ AI Config (local)
Provider: deepseek
Model: deepseek-chat
Port: 7092
Tool Categories:
✅ order ✅ product ✅ sku
✅ inventory ✅ task ✅ brand
✅ requirement ✅ customer ✅ dashboard
✅ distribution ✅ finance ✅ discount
✅ channel ✅ approval ✅ organization
✅ feature_gap
```
---
### /ai-chat history
**显示当前会话的历史消息。**
实现步骤:
1. 读取 `/tmp/ai-chat-state.json``history` 数组
2. 按时间顺序显示:
```
💬 Chat History (3 messages)
[1] 👤 User: 你好
🤖 AI: 你好!我是 AI 助手...
[2] 👤 User: 搜索组织架构找到大客户部
🤖 AI: 我来帮你搜索... (used: search_organizations)
[3] 👤 User: 最近的订单
🤖 AI: 以下是最近的订单列表...
```
3. 如果需要清空历史:`/ai-chat history clear`
- 删除状态文件中的 history 数组,重置为空
---
## SSE Event Format Reference
AI Chat SSE 流使用 `event: message` + `data: {json}` 格式:
| type | 数据字段 | 说明 |
|------|---------|------|
| `content` | `content: "<text>"` | 增量文本内容 |
| `tool_call` | `tool_call: {id, name, arguments}` | AI 请求调用工具 |
| `tool_result` | `tool_result: {tool_call_id, name, content, is_error}` | 工具执行结果 |
| `done` | `usage: {prompt_tokens, completion_tokens, total_tokens}` (可能为 null), `finish_reason` | 流结束 |
| `error` | `error: "<message>"` | 错误 |
---
## Troubleshooting
### Token 过期 (401)
如果请求返回 401,删除缓存 token 重新登录:
```bash
# 清除 token 强制重新登录
python3 -c "
import json
with open('/tmp/ai-chat-state.json') as f: s=json.load(f)
s['token']=''
with open('/tmp/ai-chat-state.json','w') as f: json.dump(s,f)
"
```
### 连接失败
- **local**: 确认本地服务已启动 (`./scripts/start_dev.sh`)
- **staging**: 确认 staging 服务运行中 (`ssh coolbuy-staging "docker ps | grep ai-service"`)
### SSE 流中断
- 检查 AI 服务日志
- local: 查看终端输出
- staging: `ssh coolbuy-staging "docker logs coolbuy-ai-service --tail 50"`
### 消息中包含特殊字符
务必用 `python3 -c "import json; print(json.dumps(msg))"` 转义消息内容,避免 JSON 解析失败。
@@ -0,0 +1,11 @@
{
"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"
}
@@ -0,0 +1,73 @@
---
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.
---
# 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,11 @@
{
"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"
}
@@ -0,0 +1,43 @@
---
name: ai-proj-macos-release
description: Build, sign, notarize, publish, and verify the AI-Proj macOS Apple Silicon package through the repository's release chain. Use when asked to release, republish, update, or repair the downloadable macOS application, including Gatekeeper failures and download-manifest updates. Do not use for service deployments or unrelated applications.
---
# AI-Proj macOS release
Use this workflow only for an explicitly requested AI-Proj macOS package release. Repository scripts and current project instructions are authoritative; stop and report any contradiction.
## Resolve the release contract
Before building, read the nearest `AGENTS.md`, the desktop package configuration, and the repository's macOS build, publish, and verification scripts. Resolve from those files:
- application version and architecture;
- production API configuration;
- signing identity and notarization mechanism;
- object-storage bucket, endpoint, release prefix, and public manifest;
- required website or download-manifest fallback version.
Do not copy machine-specific credential paths or identifiers into source control. Use the operator's configured secure credential provider without printing secret values.
## Release flow
1. Confirm the requested release version is unused. Keep package metadata, native application metadata, artifact filename, public manifest, and website fallback aligned.
2. Use a clean checkout of the exact approved commit. Run the repository's production desktop build script with the production API mode.
3. Require a valid Developer ID signature. If the repository's default notarization profile is unavailable, use another already-authorized App Store Connect credential source only after confirming its key, key ID, and issuer belong together.
4. Submit the final package to Apple notarization, wait for acceptance, staple the ticket, and validate it.
5. Mount the package read-only and verify the nested application with `codesign`, `spctl`, and the repository's smoke checks. Require Gatekeeper to report a notarized Developer ID.
6. Publish through the repository script. Use its configured object-storage credentials and upload mode; never handcraft a mutable public path when the script provides immutable versioned objects.
7. Require remote read-back verification of size and SHA-256. Upload artifacts first and update the public manifest last.
8. Download the public artifact independently and repeat signature, notarization, Gatekeeper, size, and checksum verification.
9. Confirm the manifest's latest version and asset URL, then update the website entry if the request includes it.
## Failure boundaries
- Missing signing or storage credentials: stop and report the missing configured provider; do not search broadly through personal files.
- Notarization authentication failure: stop and correct the credential tuple; never publish an unnotarized package.
- Gatekeeper reports an unnotarized or invalid application: do not publish.
- Upload stalls or fails: use only an alternative mode supported by the repository script, then repeat remote checksum verification.
- A public version already exists: do not overwrite it unless the user explicitly authorizes replacement and the repository permits it.
- Never expose signing keys, API keys, keychain passwords, storage credentials, or token values in logs, commits, manifests, or bundles.
Record the exact commit, version, artifact checksum and size, Apple result, public URL, manifest result, and verification outcome. Do not mark the release complete until the independently downloaded artifact passes all checks.
@@ -2,5 +2,10 @@
"name": "db-migration-plugin",
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时激活。",
"version": "1.0.0",
"author": { "name": "qiudl" }
"author": {
"name": "qiudl"
},
"install_name": "db-migration",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,11 @@
{
"name": "defect-analysis-plugin",
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
"version": "1.1.0",
"author": {
"name": "qiudl"
},
"install_name": "defect-analysis",
"install_type": "command",
"dir_category": "dev"
}
@@ -0,0 +1,161 @@
---
name: defect-analysis
description: 系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。
---
# 设计缺陷分析 Skill(通用版)
你是资深架构审计师。对给定的需求方案或代码实现,执行系统性的多维度缺陷检查,**反复迭代直到收敛**(连续一轮无新发现即停止)。
## 检查维度(按严重度排序)
### 1. 致命级:架构不可行
- **异步/同步冲突**:异步操作被当作同步使用?长时间操作阻塞了请求?
- **框架限制**v-html 无法绑定事件、WebSocket/SSE 超时、API 轮数限制
- **数据格式不匹配**:前后端约定的 ID 格式/字段名/序列化方式不一致?
- **循环依赖**:模块 A 内部调 B,B 又依赖 A 的结果?嵌套调用超时?
### 2. 高级:运行时崩溃
- **资源生命周期**DB session/连接/文件句柄在回调中过期?
- **并发冲突**:多个异步操作同时修改共享状态?用户操作和自动流程冲突?
- **超时/死锁**:链式调用累计超时?轮询无限等待?重试风暴?
- **内存泄漏**:大数据未释放?事件监听器未清理?闭包持有旧引用?
### 3. 中级:数据错误
- **状态覆盖**:多次回调覆盖同一变量?最后一次覆盖前面的?
- **上下文丢失**:对话/会话截断导致关键信息丢失?
- **参数传递断裂**:A 组件的输出无法完整传递给 B 组件?
- **类型不安全**JSON.parse 可能失败?nullable 字段未处理?双重编码?
- **初始化缺失**:变量未赋初值?首次使用时为 undefined/NaN
### 4. 低级:体验/维护问题
- **重复触发**watcher/callback 多次触发同一操作?
- **维护成本**:硬编码路径/行号/ID 需要手动同步?
- **XSS/注入**:用户输入或外部输出被直接渲染为 HTML?
- **状态清理**:组件卸载/页面切换时未清理进行中的请求/定时器?
## 检查流程
1. **读取方案描述**(需求文档或代码)
2. **画数据流图**(从用户操作 → 前端 → API → 后端 → DB/外部服务 → 返回)
3. **沿数据流逐节点检查**:每个节点问 5 个问题:
- 输入从哪来?可能为 null/异常吗?
- 输出给谁?接收方能处理所有情况吗?
- 耗时多久?会超时吗?
- 资源(session/连接/监听器)何时释放?
- 并发执行 N 次会怎样?
4. **检查边界**
- 前后端交界(API 格式/认证/超时/序列化)
- 同步/异步交界(await/callback/轮询/SSE
- 组件生命周期交界(mount/unmount/路由切换)
- AI/LLM 输出交界(结构化 vs 自由文本,幻觉风险,token 限制)
5. **从用户旅程检查**
- 首次使用(服务未就绪?数据为空?)
- 正常使用(N 次重复操作后状态累积?)
- 异常使用(断网/超时/并发/快速切换)
- 边界数据(空列表/超大数据/特殊字符)
## 迭代收敛规则
- 每轮检查一个维度,输出发现的缺陷列表
- 如果某轮发现 0 个新缺陷 → **收敛,停止**
- 如果 5 轮后仍有新发现 → 继续;20 轮作为阶段复盘点,不得误报为已收敛
- 达到 20 轮仍有新发现时,汇总剩余风险面并请求用户确认是否继续;用户已明确要求持续审计时可继续下一阶段
- 只有出现一轮 0 个新缺陷时才标记收敛;达到授权范围、时间或预算边界时应报告“尚未收敛”,不得伪装完成
- 每个缺陷标注严重度和轮次
## 输出格式
对每个缺陷:
```
### 缺陷 #N: {标题}{致命/高/中/低}
**问题**{一句话描述}
**场景**{触发条件}
**后果**{不修复会怎样}
**解决**{具体方案}
**验收**:- [ ] {如何确认已修复}
```
最后输出汇总表:
```
| 轮次 | 维度 | 缺陷数 | 关键发现 |
|---|---|---|---|
| 1 | 架构 | N | ... |
| ... | ... | ... | ... |
| K | 收敛 | 0 | 无新发现 |
```
## 端到端验证方法论
设计方案发现的缺陷可能在实际运行时不存在,反之亦然。对关键功能执行以下分层验证:
### 层 1:单元验证(后端隔离测试)
直接调用目标函数,绕过 API/前端,确认核心逻辑可用:
```python
# 示例:验证 AI 工具是否正常返回数据
docker exec app python3 -c "
import asyncio, json
from app.services.ai_tools import ai_tool_registry
from app.models.base import async_session_factory
async def test():
async with async_session_factory() as db:
result = await ai_tool_registry.execute('tool_name', {args}, user_id=1, db=db)
print(json.loads(result))
asyncio.run(test())
"
```
**如果这层失败**:代码逻辑错误或依赖缺失。
### 层 2:AI 行为验证(LLM 是否正确调用工具)
直接调用 AI 非流式接口,验证 LLM 是否输出了预期的工具调用标签:
```python
# 示例:验证 DeepSeek 是否输出 [TOOL_CALL]
result = await ai_gateway.chat([
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": "分析回测 LB-xxx"}
])
print("[TOOL_CALL] found:", "[TOOL_CALL]" in result["content"])
```
**如果这层失败**SYSTEM_PROMPT 不够强,LLM 不遵循指令。加"必须"/"绝不能"等强制词。
### 层 3:SSE 流式验证(前后端数据管道)
用 curl 模拟前端 SSE 请求,检查事件流格式:
```bash
curl -N "http://localhost:8000/api/ai/chat" \
-H "Authorization: Bearer $TOKEN" \
-d '{"message":"测试","model":"v3"}' | head -20
```
检查是否有 `type: "tool_call"``type: "tool_result"` 事件。
**如果这层失败**:SSE 流解析/工具执行/事件格式问题。
### 层 4:前端渲染验证(浏览器实际效果)
打开 F12 → Network → 找到 SSE 请求 → EventStream 选项卡:
-`tool_call` 事件?→ 后端正常
-`tool_result` 事件?→ 工具执行正常
- 页面渲染了结果?→ 前端正常
**如果这层失败**:前端缓存(Cmd+Shift+R)、v-html 渲染、事件委托问题。
### 层 5:部署验证(CI/CD + 远端环境)
```bash
# 检查 CI 绿否
gh run list --limit 1 --branch main
# 检查远端容器是否加载了新代码
ssh server "docker exec app grep 'key_function' /app/path/to/file.py"
# 检查远端日志
ssh server "docker logs app 2>&1 | tail -20"
```
**如果这层失败**PR 未合并、CI 失败、Docker 缓存旧镜像、.env 缺配置。
### 常见的"设计没问题但实际不工作"的原因
| 症状 | 通常原因 | 排查方法 |
|---|---|---|
| AI 不调用工具 | SYSTEM_PROMPT 用"可以"而非"必须" | 层 2 验证 |
| 工具返回空 | DB 中无数据 / 权限隔离 user_id 不匹配 | 层 1 验证 |
| 前端无反应 | 浏览器缓存旧 JS / SSE 事件未解析 | 层 4 + Cmd+Shift+R |
| 远端不生效 | PR 未合并 / Docker 用了旧镜像 | 层 5 验证 |
| 数据格式错 | 双重 JSON 编码 / 字段名不一致 | 层 3 验证 |
| 按钮点不了 | v-html 无法绑 Vue 事件 | 层 4 + 事件委托 |
@@ -2,5 +2,10 @@
"name": "deploy-rollback-plugin",
"description": "回滚方案插件。部署后发现问题时的回滚策略、数据修复、灰度回退。挂载在 deploy 阶段。",
"version": "1.0.0",
"author": { "name": "qiudl" }
"author": {
"name": "qiudl"
},
"install_name": "deploy-rollback",
"install_type": "skill",
"dir_category": "dev"
}
@@ -1 +1,11 @@
{"name":"dev-android-plugin","description":"Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
{
"name": "dev-android-plugin",
"description": "Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。按需加载。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "dev-android",
"install_type": "skill",
"dir_category": "dev"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "dev-arch",
"install_type": "skill",
"dir_category": "dev"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "dev-cicd",
"install_type": "skill",
"dir_category": "dev"
}
@@ -4,5 +4,8 @@
"version": "2.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "dev-coding",
"install_type": "skill",
"dir_category": "dev"
}
@@ -73,6 +73,72 @@ ai-proj task append-doc --id <taskId> --content "实现说明"
---
## Step 0:验证优先(Karpathy: Goal-Driven Execution
**编写任何代码前,必须先写验证脚本。** 规则来源:Karpathy "Goal-Driven Execution" 原则。
> "Define success criteria. Loop until verified."
> "Fix the bug" → "Write a test that reproduces it, then make it pass"
### 执行流程
```
① 写验证脚本(按类型选择)
② 运行一遍,确认全部 FAIL(证明功能确实不存在 / bug 确实存在)
③ 编码实现
④ 再次运行验证脚本,全部 PASS → 完成
```
### 后端验证脚本模板
实现 API 前,先写好所有 curl 命令并标注期望结果:
```bash
# 验证脚本:REQ-XXXX [功能名]
BASE="http://localhost:8080"
TOKEN="<JWT>"
echo "=== T1: 正常创建 ==="
curl -s -X POST "$BASE/api/v1/xxx" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"test"}' | jq '.code'
# 期望: 0
echo "=== T2: 缺少必填字段 ==="
curl -s -X POST "$BASE/api/v1/xxx" \
-H "Authorization: Bearer $TOKEN" \
-d '{}' | jq '.code'
# 期望: 非 0(参数错误)
echo "=== T3: 跨租户访问 ==="
curl -s -X GET "$BASE/api/v1/xxx/999" \
-H "Authorization: Bearer $TOKEN_OTHER_TENANT" | jq '.code'
# 期望: 403
```
**先运行 → 全部 FAIL → 编码 → 再次运行 → 全部 PASS**
### 前端验证脚本模板
实现页面前,先列出所有 `data-testid` 和期望的 DOM 状态:
```
验证清单(编码前先确认这些状态不存在 / 行为不正确):
- data-testid="xxx-btn-submit" 点击 → 列表刷新,行数增加 1
- data-testid="xxx-table" 行数 === API 返回 total
- data-testid="xxx-input-name" 空值提交 → 显示「请输入名称」提示
```
### 与 VP 三件套的关系
| VP 协议 | 验证优先对应 |
|---------|------------|
| VP-Data | 先在环境建好测试数据(curl 确认成功) |
| VP-Steps | **即为本节验证脚本** — 编码前写好,编码后执行 |
| VP-Pass | 验证脚本每条命令的期望输出值 |
---
## Go 后端开发
### 分层架构
@@ -490,3 +556,55 @@ fi
| `dev-ios` | iOS 开发(插件,按需加载)|
| `dev-android` | Android 开发(插件,按需加载)|
| `dev-mcp` | MCP bridge 开发(插件,按需加载)|
---
## CLAUDE.md 架构检查机制(REQ-20260416-0017 P0-5
**原则:本 skill 不硬编码任何项目的架构细节,从项目 CLAUDE.md 读取**
### 为什么
同一套 skill 要支持多个技术栈(Go+Gin / React+AntD / Vue+Element / Python+FastAPI)。如果把分层、命名、目录结构写死在 SKILL.md 里,跨项目就会冲突。
devflow-claude 的做法(借鉴):skill 只管**流程和模板**,项目架构由 CLAUDE.md 的 "Architecture" / "项目架构" 章节定义。
### 执行前检查
开始编码任务前,skill 先检查项目根 `CLAUDE.md`
```bash
# 检查 CLAUDE.md 是否含架构关键词
if [ -f "CLAUDE.md" ]; then
if grep -qiE "(架构|分层|目录结构|tech stack|architecture|project structure)" CLAUDE.md; then
echo "✅ 检测到项目架构信息"
else
echo "⚠️ CLAUDE.md 缺少架构描述"
echo " dev-coding 需要架构信息来生成准确的文件路径和分层顺序"
echo ""
echo " 📋 建议操作:"
echo " - 查看预置架构片段: ai-proj-helper/skills-dev/dev-coding-plugin/templates/claude-md-snippets/"
echo " - 选择匹配技术栈的片段,补充到 CLAUDE.md 的 '## Architecture' 章节"
echo ""
echo " ⚠️ 继续执行,但生成的文件路径可能不够准确"
fi
else
echo "⚠️ 未找到项目 CLAUDE.md,建议创建"
fi
```
### 架构片段模板库
位于 `skills-dev/dev-coding-plugin/templates/claude-md-snippets/`
| 文件 | 适用场景 |
|------|---------|
| `go-gin-gorm.md` | Go + Gin + GORM 后端(ai-proj backend 风格) |
| `react-antd.md` | React + TypeScript + Ant Designai-proj frontend 风格) |
| `vue-element.md` | Vue 3 + Element Pluscoolbuy-paas 风格) |
| `mcp-typescript.md` | MCP bridge TypeScriptmcp-task-bridge 风格) |
| `generic.md` | 通用空白骨架 |
### 非阻断原则
架构信息缺失时**仅警告不阻止**。用户仍可继续,但会被告知"生成的建议可能不够准确"。
@@ -0,0 +1,47 @@
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节,按实际情况填写 -->
## Architecture
### 技术栈
- **语言**: _TODO_
- **框架**: _TODO_
- **数据库**: _TODO_
- **缓存**: _TODO_
- **部署**: _TODO_
### 目录结构
```
project-root/
├── ???/ # _TODO: 说明_
├── ???/ # _TODO_
└── ???/
```
### 分层 / 模块规则
1. _TODO: 依赖方向_
2. _TODO: 允许/禁止的跨层调用_
### 命名规范
| 类型 | 约定 | 示例 |
|------|------|------|
| _TODO_ | _TODO_ | _TODO_ |
### 错误处理
_TODO_
### 日志
_TODO_
### 测试
_TODO_
### 其他关键约定
- _TODO_
@@ -0,0 +1,56 @@
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
## Architecture
### 分层结构(Go + Gin + GORM
```
backend/
├── routes/ # HTTP 路由定义(按模块拆分)
├── handlers/ # 请求解析 + 响应组装(薄层,不含业务)
├── services/ # 业务逻辑(事务、组合、校验)
├── models/ # GORM 数据模型
├── database/ # Repository 层(SQL、查询)
├── middleware/ # 认证、CORS、日志、限流
├── migrations/ # SQL 迁移文件
└── utils/ # 通用工具(密码、签名等)
```
### 分层规则(强制)
1. **请求流向**Route → Handler → Service → Database → Models
2. **Handler 禁止直接访问 database**:必须走 Service 层
3. **Service 禁止调用 Handler 或 Route**:单向依赖
4. **Model 仅定义结构 + GORM tag**:不含业务方法
### 命名规范
| 类型 | 约定 | 示例 |
|------|------|------|
| 文件名 | snake_case | `user_service.go` |
| 包名 | lowercase | `services`, `handlers` |
| 导出函数/类型 | PascalCase | `CreateUser`, `UserRepository` |
| 内部函数 | camelCase | `validatePassword` |
| 常量 | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` |
### 错误处理
- 使用 `errors.New()` 或自定义 error type
- Handler 层统一返回 `{"code": X, "msg": "...", "data": ...}`
- Service 层返回原始 error,由 Handler 转换
### 日志
- 使用结构化 log`log.WithField("user_id", uid).Info("...")`
- 禁用 `fmt.Println` / `print`
### 测试
- 单元测试文件名:`xxx_test.go`
- 使用 `testify/assert`
- Mock 用 `testify/mock``gomock`
### 依赖检查
- **新 handler 禁止直接 `import database/`**:需走 Service 层
- `./scripts/check-architecture.sh check` 作为 CI 门禁
@@ -0,0 +1,75 @@
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
## Architecture
### 目录结构(MCP Bridge - TypeScript
```
mcp-task-bridge/
├── src/
│ ├── tools/ # MCP tool 定义(每个工具一个文件)
│ ├── resources/ # MCP resources(若有)
│ ├── prompts/ # MCP prompts(若有)
│ ├── client/ # 后端 REST API 客户端
│ ├── utils/ # 工具函数
│ └── index.ts # 入口
├── tests/
└── dist/ # 编译产物(不提交)
```
### 工具定义规范
每个 MCP tool 一个文件:
```typescript
// tools/create-task.ts
export const createTaskTool: Tool = {
name: 'create_task',
description: '...',
inputSchema: {
type: 'object',
properties: { ... },
required: [...]
}
};
export async function handleCreateTask(args) { ... }
```
### 后端 API 调用
- 所有 REST 请求通过 `src/client/api.ts` 统一封装
- 认证头由 client 自动附加(不在 tool 里处理)
- 错误统一转成 MCP error response
### 命名规范
| 类型 | 约定 | 示例 |
|------|------|------|
| MCP tool name | snake_case | `create_task`, `list_requirements` |
| 文件名 | kebab-case | `create-task.ts` |
| 函数名 | camelCase | `handleCreateTask` |
| Tool 变量 | camelCase + `Tool` | `createTaskTool` |
### 构建与部署
- `npm run build``dist/`
- **修改代码后必须重新 build**:`pkill -f mcp-task-bridge/dist/index.js` 重启 MCP server
- 不能直接运行 TypeScript 源码
### 环境配置
- `dev` 环境:`ai-proj-dev` MCP server
- `prod` 环境:`ai-proj-prod` MCP server
- 禁止跨环境传数据(dev 需求不能关联 prod 任务)
### 测试
- Jest + ts-jest
- 集成测试模拟真实 MCP 协议
### 常见错误
- **Rule 1**: MCP 端点必须 `/api/v1/mcp/` 前缀
- **Rule 2**: 修改后必须 rebuild + 重启
- **Rule 3**: 环境隔离(dev / prod
@@ -0,0 +1,78 @@
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
## Architecture
### 目录结构(React + TypeScript + Ant Design
```
frontend/src/
├── pages/ # 页面级组件(路由对应)
├── components/ # 可复用 UI 组件
├── services/ # API 客户端(Axios 封装)
├── hooks/ # 自定义 React Hooks
├── contexts/ # Context Providersauth, timer 等)
├── utils/ # 工具函数(auth, validation, date 等)
├── types/ # TypeScript 类型定义
└── config/ # Feature flags, 性能配置
```
### 状态管理
| 状态类型 | 方案 |
|---------|------|
| 服务器状态 | React Query (TanStack Query) |
| 全局状态 | Context API |
| 本地状态 | useState / useReducer |
| 表单状态 | Ant Design Form |
**禁止**Redux / MobX(本项目不使用)
### 路由
- React Router v6
- 路由定义集中在 `src/routes/`
- 懒加载:`const Page = lazy(() => import(...))`
### API 调用
- 使用 `services/` 下的封装函数,不要在组件里直接 `axios.get`
- 响应类型必须有 TypeScript interface
- 错误统一由 axios 拦截器处理
### 样式
- Ant Design 组件 + CSS Module
- 禁止内联 `style={{ ... }}` 用于复杂样式
- 全局变量走 CSS Variables
### Modal 安全规则(重要)
`Modal.success/info/warning/error` 是非阻塞调用,后续 UI 操作必须放在 `onOk` 回调中:
```tsx
// WRONG
Modal.success({ title: '成功' });
setNextModalOpen(true); // 立即执行,两个 modal 冲突
// CORRECT
Modal.success({
title: '成功',
onOk: () => setNextModalOpen(true),
});
```
### 命名规范
| 类型 | 约定 | 示例 |
|------|------|------|
| 组件文件 | PascalCase | `UserProfile.tsx` |
| Hook 文件 | camelCase | `useAuth.ts` |
| 工具文件 | kebab-case | `date-utils.ts` |
| 组件名 | PascalCase | `UserProfile` |
| Hook 名 | `use` 前缀 | `useAuth` |
### 测试
- 单测:Jest + React Testing Library
- E2EPlaywright
- 测试文件:`xxx.test.tsx` 与源文件同目录
@@ -0,0 +1,67 @@
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
## Architecture
### 目录结构(Vue 3 + TypeScript + Element Plus
```
src/
├── views/ # 页面级组件(路由对应)
├── components/ # 可复用组件
├── api/ # API 封装
├── stores/ # Pinia stores
├── composables/ # 组合式函数(use* hooks
├── utils/ # 工具函数
├── types/ # TypeScript 类型定义
└── router/ # Vue Router 配置
```
### 状态管理
- **Pinia**(官方推荐)
- 每个业务模块一个 store`stores/user.ts``stores/order.ts`
- 禁止直接在组件里写持久状态
### 路由
- Vue Router 4
- 路由守卫统一在 `router/guards.ts`
- 懒加载:`component: () => import('@/views/...')`
### Composition API
- **强制使用 `<script setup>`**,禁止 Options API
- Props 用 `defineProps<T>()`Emits 用 `defineEmits<T>()`
### API 调用
- `api/` 下按模块划分:`api/user.ts``api/order.ts`
- 每个函数返回类型明确
- 错误由 axios 拦截器统一处理
### 命名规范
| 类型 | 约定 | 示例 |
|------|------|------|
| 组件文件 | PascalCase | `UserProfile.vue` |
| Composable | camelCase + use 前缀 | `useAuth.ts` |
| Store | camelCase | `useUserStore` |
| API 文件 | kebab-case | `user-api.ts` |
| 工具函数 | camelCase | `formatDate` |
### 样式
- SCSS + Element Plus 主题
- scoped style(避免全局污染)
- 全局变量走 SCSS 变量或 CSS Variables
### 国际化
- 使用 vue-i18n
- 消息文件:`src/locales/zh.json` / `en.json`
- 禁止硬编码文本:用 `t('path.to.key')`
### 测试
- 单测:Vitest + Vue Test Utils
- E2EPlaywright / Cypress
@@ -0,0 +1,11 @@
{
"name": "dev-commit-plugin",
"description": "智能 /commit 命令:分支保护 + 自动建功能分支 + Conventional Commits 生成",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "dev-commit",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,186 @@
---
name: dev-commit
description: 智能 git commit — 分支保护 + 自动建功能分支 + Conventional Commits 生成。当用户说"提交代码"、"commit"、"/commit"、"保存修改"时自动激活。
---
# dev-commit Skill — 智能提交
借鉴自 devflow-claude `/req:commit`。源自 REQ-20260416-0017 P0-6。
## 核心功能
**用户说"/commit" 或 "提交代码" 时执行:**
1. **分支合规检查**
2. **自动建功能分支**(若在保护分支)
3. **Conventional Commits 生成**
4. **自动关联 REQ-XXX**
## 流程详解
### 1. 分支合规检查(强制)
```bash
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
# 保护分支名单
PROTECTED_BRANCHES="main master develop production"
for b in $PROTECTED_BRANCHES; do
if [ "$CURRENT_BRANCH" = "$b" ]; then
IS_PROTECTED=1
break
fi
done
```
**铁律****绝对禁止**在 main / develop / master / production 分支上直接 commit。
### 2. 保护分支上有改动 → 自动建功能分支
```
检测到 main/develop 有未提交改动:
推荐方案:
1. 自动建分支 feat/xxx 并切换过去(推荐)
2. 取消本次 commit,手动切分支
3. 临时 stash 后切分支
【默认选 1】
```
**分支命名自动推断:**
- 从对话上下文:如果刚在讨论 "REQ-20260416-0017" → `feat/REQ-20260416-0017-summary`
- 从文件变更:扫描 staged files 的路径 → 推断模块(如 `backend/services/user/``feat/user-xxx`
- 从 commit message 意图:如果意图是 fix → `fix/xxx`
**前缀规则:**
- `feat/` — 新功能
- `fix/` — bug 修复
- `chore/` — 杂项(依赖升级、CI 调整等)
- `refactor/` — 重构
- `docs/` — 文档
### 3. Conventional Commits 生成
**格式:**
```
<type>(<scope>): <description> [(REQ-XXX)] [closes #N]
```
**type**
- feat - 新功能
- fix - 修复
- chore - 杂项
- refactor - 重构
- docs - 文档
- test - 测试
- perf - 性能
- style - 格式
**scope**
- 从修改的文件路径推断:`backend/services/user/``user`
- `frontend/src/pages/login/``login`
**示例:**
```
feat(mcp): 新增 set_requirement_reviewers 工具 (REQ-20260415-0023)
fix(frontend): 403 权限重载死循环
chore(cicd): 精简 CI/CD 流程,移除 Staging 环境 (REQ-20260415-0004) closes #242
```
### 4. REQ-XXX 自动关联
**查找顺序:**
1. 当前分支名:`feat/REQ-20260416-0017-xxx` → 提取 `REQ-20260416-0017`
2. 最近对话中提到的 REQ ID
3. MCP 查询当前用户的"进行中"需求(如只有一个,直接用)
**分支名含 `-iN` 时追加 `closes #N`**
```
feat/REQ-xxx-i42 + commit → commit message 自动加 "closes #42"
```
### 5. 提交确认
提交前展示预览:
```
即将执行:
分支: feat/mcp-set-reviewers
Files: backend/mcp/tools/set_reviewers.go, mcp-task-bridge/src/tools/set-reviewers.ts
Message:
feat(mcp): 新增 set_requirement_reviewers 工具 (REQ-20260415-0023)
确认提交?(y/n)
```
## Issue 集成规范(REQ-20260416-0017 P1-13
借鉴 devflow-claude 的 issue 关联机制。
### 分支名 `-iN` 后缀
当任务/需求来自 Gitea/GitHub issue 时,分支名末尾追加 `-iN`
```
feat/REQ-20260416-0017-user-points-i12 ← issue #12
fix/login-token-expired-i5 ← issue #5
```
**规则**
- `-iN` 仅当 issue 关联存在时追加
- `N` 为纯数字(不带 `#`
- 位于分支名最末尾
### commit message 自动追加 `closes #N`
当分支名含 `-iN` 后缀时,commit message 末尾自动追加 `closes #N`
```
feat(user): 实现积分规则管理 (REQ-20260416-0017) closes #12
fix(auth): 修复 token 过期未刷新 closes #5
```
**效果**PR 合并后 Gitea/GitHub 自动关闭关联 issue。
### Issue 编号读取优先级
| 优先级 | 来源 | 说明 |
|-------|------|------|
| 1 | `--from-issue=#N` 参数 | 用户显式指定 |
| 2 | 分支名 `-iN` 后缀 | 自动解析 `(-i(\d+))$` |
| 3 | MCP 需求文档关联的 issue | (预留) |
### 不触发的情况
- 分支名不含 `-iN` → 不追加 `closes`
- 用户显式说"不关联 issue" → 跳过
## 与 ai-proj 集成
- **查询当前需求**:通过 MCP `mcp__ai-proj__find_requirement``list_requirements` 找 user 进行中的
- **commit 后可选**:调用 `mcp__ai-proj__update_task` 更新关联任务进度
## 排除项
本 skill **不做**
- 推送远程(留给 `/pr``git push`
- 创建 PR(留给 pull-request skill
- 代码评审(留给 dev-review skill
职责边界清晰,防止单命令膨胀。
## 风险控制
1. **保护分支改动不得 commit** — 强制拦截
2. **message 必须用 Conventional Commits** — 后续 changelog 依赖
3. **REQ-XXX 关联尽量自动推断** — 但推断不出不阻断
## 安装方式
本 skill 自动随 ai-proj-helper marketplace 加载。用户说"/commit" 即激活。
## 参考
- devflow-claude: `plugins/req/commands/commit.md`
- REQ-20260416-0017 P0-6
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "dev-deploy",
"install_type": "skill",
"dir_category": "dev"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "dev-integration",
"install_type": "skill",
"dir_category": "dev"
}
@@ -1 +1,11 @@
{"name":"dev-ios-plugin","description":"iOS 开发插件。Swift/SwiftUI + MVVM 架构、TestFlight 部署、Xcode 构建。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
{
"name": "dev-ios-plugin",
"description": "iOS 开发插件。Swift/SwiftUI + MVVM 架构、TestFlight 部署、Xcode 构建。按需加载。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "dev-ios",
"install_type": "skill",
"dir_category": "dev"
}
@@ -1 +1,11 @@
{"name":"dev-mcp-plugin","description":"MCP Bridge 开发插件。TypeScript MCP 服务开发、Token 管理、HTTP 客户端模式。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
{
"name": "dev-mcp-plugin",
"description": "MCP Bridge 开发插件。TypeScript MCP 服务开发、Token 管理、HTTP 客户端模式。按需加载。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "dev-mcp",
"install_type": "skill",
"dir_category": "dev"
}
@@ -1 +1,11 @@
{"name":"dev-pda-plugin","description":"PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先模式。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
{
"name": "dev-pda-plugin",
"description": "PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先模式。按需加载。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "dev-pda",
"install_type": "skill",
"dir_category": "dev"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "dev-review",
"install_type": "skill",
"dir_category": "dev"
}
+31 -6
View File
@@ -1,13 +1,13 @@
---
name: dev-review
description: 代码评审技能。视角对抗性扫描法,用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。
description: 代码评审技能。视角对抗性扫描法(含 Karpathy Scope 审计),用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。
---
# 代码评审 Skill (dev-review)
## 概述
独立的代码评审技能,核心方法论是**视角对抗性扫描法**。
独立的代码评审技能,核心方法论是**视角对抗性扫描法**(五个传统安全/健壮性视角 + Karpathy Scope 审计视角)
**适用场景**
- `/req cr [REQ-ID]` — 需求流程中的代码评审阶段
@@ -22,7 +22,7 @@ description: 代码评审技能。五视角对抗性扫描法,用于 PR 代码
| 上游 | 本技能输入 | 本技能输出 | 下游 |
|------|-----------|-----------|------|
| dev-coding | PR diff + 开发设计文档 | CR 报告(视角扫描 + 发现汇总 + 结论) | dev-test |
| dev-coding | PR diff + 开发设计文档 | CR 报告(视角扫描 + 发现汇总 + 结论) | dev-test |
---
@@ -67,7 +67,7 @@ description: 代码评审技能。五视角对抗性扫描法,用于 PR 代码
---
## 视角对抗性扫描法
## 视角对抗性扫描法
### 总览
@@ -146,6 +146,28 @@ file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
- [ ] Redis 不可用时是否有降级方案?(缓存穿透到数据库)
- [ ] token 过期/刷新逻辑是否正确?(access vs refresh 不同策略)
### 视角6Scope 审计者(Karpathy: Simplicity + Surgical
**思维模式**:每一行变更,需求有没有要求它?
> "Touch only what you must. Clean up only your own mess."
> "Every changed line should trace directly to the user's request."
扫描清单:
- [ ] diff 中变更的**每个文件**,是否都在 req-design 变更文件清单中?(超出清单 = 疑似顺手重构)
- [ ] 新增的函数/方法/结构体,每个都有对应 AC 需要它?
- [ ] 是否引入了"未来可能用到"的参数、配置项、可选字段、接口抽象?
- [ ] 是否修改了本次 AC 无关的注释、格式、变量名、import 顺序?
- [ ] 代码量是否合理?实现简单 AC 超过 200 行须说明必要性
"If you write 200 lines and it could be 50, rewrite it"
- [ ] 错误处理是否只覆盖真实会发生的场景?不为不可能的情况写防御代码
**典型发现示例**
```
backend/services/user_service.go:45 — 新增了 WithRetry 参数,但 AC 中无重试需求。
建议:移除该参数,AC 有需要时再添加。严重度:Low
```
---
## CR 报告模板
@@ -160,7 +182,7 @@ file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
### 变更概要
{1-3 句描述本次变更的目的和范围}
### 视角扫描结果
### 视角扫描结果
#### 1. 攻击者视角
{扫描发现,或 "未发现问题"}
@@ -177,6 +199,9 @@ file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
#### 5. 依赖者视角
{扫描发现,或 "未发现问题"}
#### 6. Scope 审计者视角(Karpathy
{扫描发现,或 "所有变更文件均在设计清单范围内,无过度实现"}
### 审查发现汇总
| # | 严重度 | 文件:行号 | 角 | 描述 | 建议 |
@@ -221,7 +246,7 @@ file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
| 文档存在 | CR 任务有附加文档 |
| 字数 | ≥ 500 字 |
| 代码引用 | 含 `file:line` 格式的引用 |
| 视角扫描 | 含全部 5 个视角章节 |
| 视角扫描 | 含全部 6 个视角章节(含 Scope 审计者) |
| 结论章节 | 含明确的通过/不通过结论 |
---
@@ -2,5 +2,10 @@
"name": "dev-scaffold-plugin",
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架(Model/Repository/Service/Handler/Route)。挂载在 dev 阶段。",
"version": "1.0.0",
"author": { "name": "qiudl" }
"author": {
"name": "qiudl"
},
"install_name": "dev-scaffold",
"install_type": "skill",
"dir_category": "dev"
}
@@ -4,5 +4,8 @@
"version": "2.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "dev-test",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,11 @@
{
"name": "executing-plans-plugin",
"description": "Use when you have a written implementation plan to execute in a separate session with review checkpoints.",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "executing-plans",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,96 @@
---
name: executing-plans
description: Use when you have a written implementation plan to execute in a separate session with review checkpoints
---
# Executing Plans
## Overview
Load plan, review critically, create branch, execute tasks in batches, report for review between batches.
**Core principle:** Batch execution with checkpoints for architect review.
**Announce at start:** "I'm using the executing-plans skill to implement this plan."
## The Process
### Step 1: Load and Review Plan
1. Read plan file
2. Review critically - identify any questions or concerns about the plan
3. If concerns: Raise them with your human partner before starting
4. If no concerns: Proceed to branch setup
### Step 2: Setup Branch
**Before any implementation, ensure proper branch isolation.**
1. Check if already on a feature branch for this task
2. If not, use `/pr start` to create one:
```bash
/pr start <type> <REQ-id> <name>
# Example: /pr start feature REQ-123 user-login
```
3. If no REQ-id available, ask user or create branch manually:
```bash
git fetch origin
git checkout -b <type>/<descriptive-name> origin/main
```
4. Confirm branch is ready before proceeding
**Branch types:** `feature`, `fix`, `refactor`
### Step 3: Create Tasks and Execute Batch
**Default: First 3 tasks**
1. Create TodoWrite tasks from plan
2. For each task in batch:
- Mark as in_progress
- Follow each step exactly (plan has bite-sized steps)
- Run verifications as specified
- Mark as completed
### Step 4: Report
When batch complete:
- Show what was implemented
- Show verification output
- Say: "Ready for feedback."
### Step 5: Continue
Based on feedback:
- Apply changes if needed
- Execute next batch
- Repeat until complete
### Step 6: Complete Development
After all tasks complete and verified:
- Announce: "I'm using the finishing-a-development-branch skill to complete this work."
- **REQUIRED SUB-SKILL:** Use superpowers:finishing-a-development-branch
- Follow that skill to verify tests, present options, execute choice
## When to Stop and Ask for Help
**STOP executing immediately when:**
- Hit a blocker mid-batch (missing dependency, test fails, instruction unclear)
- Plan has critical gaps preventing starting
- You don't understand an instruction
- Verification fails repeatedly
**Ask for clarification rather than guessing.**
## When to Revisit Earlier Steps
**Return to Review (Step 1) when:**
- Partner updates the plan based on your feedback
- Fundamental approach needs rethinking
**Don't force through blockers** - stop and ask.
## Remember
- Review plan critically first
- **Create feature branch before implementation**
- Follow plan steps exactly
- Don't skip verifications
- Reference skills when plan says to
- Between batches: just report and wait
- Stop when blocked, don't guess
@@ -0,0 +1,11 @@
{
"name": "finishing-branch-plugin",
"description": "Use when implementation is complete and all tests pass - verifies and creates PR.",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "finishing-a-development-branch",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,104 @@
---
name: finishing-a-development-branch
description: Use when implementation is complete and all tests pass - verifies and creates PR
---
# Finishing a Development Branch
## Overview
Verify tests pass, then push and create PR.
**Core principle:** Verify tests → Create PR → Done.
**Announce at start:** "I'm using the finishing-a-development-branch skill to complete this work."
## The Process
### Step 1: Verify Tests
**Before creating PR, verify tests pass:**
```bash
# Run project's test suite
npm test / cargo test / pytest / go test ./... / mvn test
```
**If tests fail:**
```
Tests failing (<N> failures). Must fix before completing:
[Show failures]
Cannot proceed with PR until tests pass.
```
Stop. Fix tests first.
**If tests pass:** Continue to Step 2.
### Step 2: Push and Create PR
Use the `/pr create` command which will:
1. **Check for existing PR first** - avoids duplicates
2. If PR exists: Report existing PR URL and skip
3. If no PR: Analyze commits, generate title/description, push, create PR
```bash
/pr create
```
**Duplicate prevention:** The `/pr create` command checks for existing open PRs on the current branch before creating a new one.
Report the PR URL when complete (whether existing or newly created).
### Step 3: Cleanup Worktree (if applicable)
Check if working in a worktree:
```bash
git worktree list | grep $(git branch --show-current)
```
If yes, ask user:
```
Worktree at <path>. Remove it now? (y/n)
```
If confirmed:
```bash
git worktree remove <worktree-path>
```
## Quick Reference
```
Tests Pass?
↓ yes
/pr create
PR URL returned
Cleanup worktree (optional)
Done
```
## Red Flags
**Never:**
- Create PR with failing tests
- Skip test verification
- Force-push without explicit request
**Always:**
- Verify tests before creating PR
- Use `/pr create` for consistent PR format
- Report the PR URL
## Integration
**Called by:**
- **executing-plans** (Step 6) - After all batches complete
**Uses:**
- **/pr create** - For pushing and PR creation
@@ -0,0 +1,11 @@
{
"name": "frontend-design-plugin",
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
"version": "1.0.1",
"author": {
"name": "qiudl"
},
"install_name": "frontend-design",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,695 @@
---
name: frontend-design
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
arguments: "[component|page|storybook] <description>"
---
# Frontend Design 前端设计技能
创建高质量、有设计感的前端界面和组件,支持 Storybook 组件开发。
---
## 命令格式
| 命令 | 功能 | 示例 |
|------|------|------|
| `/frontend-design component <描述>` | 创建 React/Vue 组件 | `/frontend-design component 产品卡片` |
| `/frontend-design page <描述>` | 创建完整页面 | `/frontend-design page 登录页` |
| `/frontend-design storybook <描述>` | 创建带 Storybook 的组件 | `/frontend-design storybook 按钮组件` |
---
## 设计原则
### 1. 设计思维先行
在编码前,明确以下问题:
- **目的**:这个界面解决什么问题?谁在使用?
- **调性**:选择一个明确的美学方向
- **差异化**:什么让这个设计令人难忘?
### 2. 美学方向选择
| 风格 | 特点 | 适用场景 |
|------|------|----------|
| 极简主义 | 大量留白、精炼元素 | 工具类、专业平台 |
| 现代商务 | 清晰层次、专业配色 | 企业官网、B2B |
| 活力年轻 | 鲜艳色彩、动感动画 | 消费品、社交 |
| 奢华精致 | 深色调、金属质感 | 高端品牌、金融 |
| 自然有机 | 柔和曲线、自然色系 | 健康、环保 |
| 复古怀旧 | 经典字体、做旧质感 | 文化、艺术 |
| 未来科技 | 渐变、玻璃拟态 | 科技、创新 |
### 3. 避免的设计陷阱
**禁止使用**
- 过度使用的字体:Inter、Roboto、Arial
- 陈词滥调的配色:紫色渐变白底
- 千篇一律的布局
- 缺乏个性的通用组件
**应该追求**
- 独特的字体组合
- 有意图的配色方案
- 打破常规的布局
- 有记忆点的细节
---
## Storybook 组件开发
### 项目结构
```
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.stories.tsx
│ │ ├── Button.module.css
│ │ └── index.ts
│ ├── Card/
│ │ ├── Card.tsx
│ │ ├── Card.stories.tsx
│ │ ├── Card.module.css
│ │ └── index.ts
│ └── index.ts
├── styles/
│ ├── variables.css
│ ├── typography.css
│ └── animations.css
└── .storybook/
├── main.ts
└── preview.ts
```
### 组件模板
#### 1. 组件文件 (Component.tsx)
```tsx
import React from 'react';
import styles from './Component.module.css';
export interface ComponentProps {
/** 组件变体 */
variant?: 'primary' | 'secondary' | 'outline';
/** 尺寸 */
size?: 'sm' | 'md' | 'lg';
/** 是否禁用 */
disabled?: boolean;
/** 子元素 */
children: React.ReactNode;
/** 点击事件 */
onClick?: () => void;
}
export const Component: React.FC<ComponentProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
children,
onClick,
}) => {
return (
<div
className={`${styles.component} ${styles[variant]} ${styles[size]}`}
data-disabled={disabled}
onClick={disabled ? undefined : onClick}
>
{children}
</div>
);
};
```
#### 2. Storybook Stories (Component.stories.tsx)
```tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Component } from './Component';
const meta: Meta<typeof Component> = {
title: 'Components/Component',
component: Component,
tags: ['autodocs'],
parameters: {
layout: 'centered',
docs: {
description: {
component: '组件描述文档',
},
},
},
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'outline'],
description: '组件变体样式',
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
description: '组件尺寸',
},
disabled: {
control: 'boolean',
description: '是否禁用',
},
},
};
export default meta;
type Story = StoryObj<typeof Component>;
/** 默认状态 */
export const Default: Story = {
args: {
children: '默认组件',
},
};
/** 主要变体 */
export const Primary: Story = {
args: {
variant: 'primary',
children: '主要按钮',
},
};
/** 次要变体 */
export const Secondary: Story = {
args: {
variant: 'secondary',
children: '次要按钮',
},
};
/** 不同尺寸 */
export const Sizes: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Component size="sm"></Component>
<Component size="md"></Component>
<Component size="lg"></Component>
</div>
),
};
/** 禁用状态 */
export const Disabled: Story = {
args: {
disabled: true,
children: '禁用状态',
},
};
```
#### 3. 样式文件 (Component.module.css)
```css
.component {
/* 基础样式 */
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-md);
font-family: var(--font-sans);
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
/* 变体 */
.primary {
background: var(--color-primary);
color: white;
}
.primary:hover {
background: var(--color-primary-dark);
transform: translateY(-1px);
box-shadow: 0 4px 12px var(--color-primary-shadow);
}
.secondary {
background: var(--color-secondary);
color: var(--color-text);
}
.outline {
background: transparent;
border: 2px solid var(--color-border);
color: var(--color-text);
}
/* 尺寸 */
.sm {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
.md {
padding: 0.75rem 1.5rem;
font-size: 1rem;
}
.lg {
padding: 1rem 2rem;
font-size: 1.125rem;
}
/* 状态 */
[data-disabled="true"] {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
```
---
## 设计系统变量
### CSS 变量模板
```css
:root {
/* 颜色 */
--color-primary: #0066ff;
--color-primary-dark: #0052cc;
--color-primary-light: #4d94ff;
--color-primary-shadow: rgba(0, 102, 255, 0.25);
--color-secondary: #f0f4f8;
--color-accent: #ff6b35;
--color-text: #1a1a2e;
--color-text-muted: #64748b;
--color-text-inverse: #ffffff;
--color-background: #ffffff;
--color-surface: #f8fafc;
--color-border: #e2e8f0;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-error: #ef4444;
/* 字体 */
--font-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-display: 'Clash Display', var(--font-sans);
--font-mono: 'JetBrains Mono', monospace;
/* 字号 */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 2rem;
--text-4xl: 2.5rem;
/* 间距 */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-12: 3rem;
--space-16: 4rem;
/* 圆角 */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
--radius-xl: 1.5rem;
--radius-full: 9999px;
/* 阴影 */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
/* 动画 */
--duration-fast: 150ms;
--duration-normal: 300ms;
--duration-slow: 500ms;
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* 暗色主题 */
[data-theme="dark"] {
--color-text: #f1f5f9;
--color-text-muted: #94a3b8;
--color-background: #0f172a;
--color-surface: #1e293b;
--color-border: #334155;
}
```
---
## 常用组件示例
### 1. 产品卡片 (ProductCard)
```tsx
// ProductCard.tsx
import React from 'react';
import styles from './ProductCard.module.css';
export interface ProductCardProps {
image: string;
title: string;
location: string;
rating: number;
reviewCount: number;
price: number;
originalPrice?: number;
tags?: string[];
onAddToCart?: () => void;
}
export const ProductCard: React.FC<ProductCardProps> = ({
image,
title,
location,
rating,
reviewCount,
price,
originalPrice,
tags = [],
onAddToCart,
}) => {
return (
<article className={styles.card}>
<div className={styles.imageWrapper}>
<img src={image} alt={title} className={styles.image} />
{tags.length > 0 && (
<div className={styles.tags}>
{tags.map((tag) => (
<span key={tag} className={styles.tag} data-tag={tag}>
{tag}
</span>
))}
</div>
)}
</div>
<div className={styles.content}>
<h3 className={styles.title}>{title}</h3>
<p className={styles.location}>📍 {location}</p>
<div className={styles.rating}>
<span className={styles.stars}> {rating.toFixed(1)}</span>
<span className={styles.reviewCount}>({reviewCount})</span>
</div>
<div className={styles.priceRow}>
<div className={styles.price}>
<span className={styles.currency}>¥</span>
<span className={styles.amount}>{price}</span>
<span className={styles.suffix}></span>
</div>
{originalPrice && (
<span className={styles.originalPrice}>¥{originalPrice}</span>
)}
</div>
<button className={styles.addButton} onClick={onAddToCart}>
</button>
</div>
</article>
);
};
```
```tsx
// ProductCard.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { ProductCard } from './ProductCard';
const meta: Meta<typeof ProductCard> = {
title: 'Components/ProductCard',
component: ProductCard,
tags: ['autodocs'],
parameters: {
layout: 'centered',
backgrounds: {
default: 'light',
},
},
};
export default meta;
type Story = StoryObj<typeof ProductCard>;
export const Default: Story = {
args: {
image: 'https://images.unsplash.com/photo-1494947665470-20322015e3a8',
title: '袋鼠岛一日游',
location: '阿德莱德出发',
rating: 4.8,
reviewCount: 126,
price: 389,
tags: ['热卖', '含午餐'],
},
};
export const WithDiscount: Story = {
args: {
...Default.args,
originalPrice: 499,
tags: ['特惠', '限时'],
},
};
export const Grid: Story = {
render: () => (
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 300px)',
gap: '1.5rem'
}}>
<ProductCard
image="https://images.unsplash.com/photo-1494947665470-20322015e3a8"
title="袋鼠岛一日游"
location="阿德莱德出发"
rating={4.8}
reviewCount={126}
price={389}
tags={['热卖']}
/>
<ProductCard
image="https://images.unsplash.com/photo-1506905925346-21bda4d32df4"
title="巴罗莎谷酒庄之旅"
location="阿德莱德出发"
rating={4.9}
reviewCount={89}
price={299}
originalPrice={399}
tags={['特惠', '含品酒']}
/>
<ProductCard
image="https://images.unsplash.com/photo-1540202403-b7abd6747a18"
title="海豚巡航体验"
location="格雷尔海滩"
rating={4.7}
reviewCount={234}
price={159}
tags={['亲子']}
/>
</div>
),
};
```
### 2. 按钮组件 (Button)
```tsx
// Button.tsx
import React from 'react';
import styles from './Button.module.css';
export interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
fullWidth?: boolean;
loading?: boolean;
disabled?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
children: React.ReactNode;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
fullWidth = false,
loading = false,
disabled = false,
leftIcon,
rightIcon,
children,
onClick,
}) => {
return (
<button
className={`
${styles.button}
${styles[variant]}
${styles[size]}
${fullWidth ? styles.fullWidth : ''}
`}
disabled={disabled || loading}
onClick={onClick}
>
{loading ? (
<span className={styles.spinner} />
) : (
<>
{leftIcon && <span className={styles.icon}>{leftIcon}</span>}
<span>{children}</span>
{rightIcon && <span className={styles.icon}>{rightIcon}</span>}
</>
)}
</button>
);
};
```
---
## Storybook 配置
### .storybook/main.ts
```ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
};
export default config;
```
### .storybook/preview.ts
```ts
import type { Preview } from '@storybook/react';
import '../src/styles/variables.css';
import '../src/styles/typography.css';
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#ffffff' },
{ name: 'gray', value: '#f8fafc' },
{ name: 'dark', value: '#0f172a' },
],
},
},
globalTypes: {
theme: {
description: 'Global theme for components',
defaultValue: 'light',
toolbar: {
title: 'Theme',
icon: 'circlehollow',
items: ['light', 'dark'],
dynamicTitle: true,
},
},
},
};
export default preview;
```
---
## 快速启动命令
### 创建新组件
```bash
# 创建组件目录
mkdir -p src/components/ComponentName
# 创建文件
touch src/components/ComponentName/{ComponentName.tsx,ComponentName.stories.tsx,ComponentName.module.css,index.ts}
```
### 安装 Storybook
```bash
# 初始化 Storybook
npx storybook@latest init
# 安装额外插件
npm install -D @storybook/addon-a11y @storybook/addon-interactions
# 启动 Storybook
npm run storybook
```
---
## 设计检查清单
### 组件质量检查
- [ ] Props 接口定义完整,带 JSDoc 注释
- [ ] 支持必要的变体(variant)和尺寸(size
- [ ] 处理禁用和加载状态
- [ ] 支持自定义 className
- [ ] 键盘可访问性
- [ ] 屏幕阅读器友好
### Storybook 质量检查
- [ ] 所有变体都有对应 Story
- [ ] argTypes 配置完整
- [ ] 包含组件文档描述
- [ ] 交互状态可测试
- [ ] 响应式展示
### 视觉质量检查
- [ ] 字体选择有特色
- [ ] 配色方案协调
- [ ] 动画流畅自然
- [ ] 间距一致
- [ ] 暗色主题支持
@@ -0,0 +1,11 @@
{
"name": "karpathy-guidelines",
"description": "Karpathy 四原则编码行为守则(Think Before Coding / Simplicity First / Surgical Changes / Goal-Driven Execution)。已深度融合到 req 技能工作流各阶段,可独立激活用于任意编码场景。",
"version": "1.0.1",
"author": {
"name": "qiudl"
},
"install_name": "karpathy-guidelines",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,97 @@
---
name: karpathy-guidelines
description: Karpathy 四原则编码行为守则。减少 LLM 常见编码错误:过度实现、静默假设、顺手重构、无验证标准。在任意编码场景激活。
---
# Karpathy Guidelines Skill
> 来源:[andrej-karpathy-skills](https://github.com/forrestchang/andrej-karpathy-skills)
> 在本项目中已深度融合到 req 技能工作流各阶段。
## 四原则
### 1. Think Before Coding(编前推理)
> "Don't assume. Don't hide confusion. Surface tradeoffs."
**在写第一行代码前:**
- 显式列出本次实现的假设(数据格式、边界条件、依赖接口)
- 如存在多种解读,列出所有方案(附估算),不要静默选择
- 如有更简单的实现方式,说出来
- 遇到不清晰的地方,停下来,指出混乱点,提问
**在 req 工作流中:** → 已嵌入 `req-prd` 的「Phase 0 假设倾倒协议」
---
### 2. Simplicity First(简单优先)
> "Minimum code that solves the problem. Nothing speculative."
**禁止:**
- 添加未被需求要求的功能
- 为单次使用的代码添加抽象
- 添加未被请求的"灵活性"或"可配置性"
- 为不可能发生的场景写错误处理
- 写了 200 行但 50 行就够的代码 → 重写
**自检:** "一个高级工程师看这段代码会觉得过度设计吗?" 如果是 → 简化
**在 req 工作流中:** → 已嵌入 `req-design` 过度设计检查 + `dev-review` 第六视角
---
### 3. Surgical Changes(手术式修改)
> "Touch only what you must. Clean up only your own mess."
**修改现有代码时:**
- 不要"顺手改进"相邻代码、注释或格式
- 不要重构没有损坏的代码
- 匹配现有代码风格,即使你会做不同的选择
- 发现不相关的死代码 → 提及但不删除
**你的变更造成的孤儿:**
- 删除你的变更导致的无用 import/变量/函数
- 不要删除已存在的死代码(除非被要求)
**铁律:** diff 中每一行修改都应该可以追溯到用户的需求
**在 req 工作流中:** → 已嵌入 `dev-review` 第六视角 + `check-surgical.sh` Harness 脚本
---
### 4. Goal-Driven Execution(目标驱动执行)
> "Define success criteria. Loop until verified."
**将请求转化为可验证目标:**
- "加验证" → "为无效输入写测试,然后让它通过"
- "修 bug" → "写一个复现 bug 的测试,然后让它通过"
- "重构 X" → "确保测试在重构前后都通过"
**多步任务需要说明计划:**
```
1. [步骤] → 验证: [检查项]
2. [步骤] → 验证: [检查项]
3. [步骤] → 验证: [检查项]
```
**在 req 工作流中:** → 已嵌入 `dev-coding` 的「Step 0 验证优先」+ VP 三件套协议
---
## 与 req 工作流的映射
| 原则 | 生效阶段 | 落地机制 |
|------|---------|---------|
| Think Before Coding | req-prd 启动前 | Phase 0 假设倾倒协议 |
| Simplicity First | req-design + dev-review | 过度设计检查 + 第六视角 |
| Surgical Changes | dev-review + CI | 第六视角 + check-surgical.sh |
| Goal-Driven Execution | dev-coding | Step 0 验证优先 + VP 三件套 |
## 反模式速查
| 场景 | ❌ LLM 常犯 | ✅ 正确做法 |
|------|-----------|-----------|
| "做个导出功能" | 静默假设格式/字段,直接实现 | 列出3种解读,等用户确认 |
| "让搜索更快" | 同时加缓存+索引+async | 列出3种"更快"含义,确认再做 |
| "加折扣计算" | Strategy+Abstract+Enum50行 | 一个函数,3行 |
| "修空邮件bug" | 顺手加用户名校验+类型注解 | 只改空邮件的那2行 |
| "修认证bug" | 直接修改,无验证标准 | 先写复现测试,修复后验证通过 |
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "pull-request",
"install_type": "skill",
"dir_category": "dev"
}
@@ -1,8 +1,11 @@
{
"name": "review-checklist-plugin",
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
"version": "1.0.0",
"version": "1.1.0",
"author": {
"name": "qiudl"
}
},
"install_name": "review-checklist",
"install_type": "skill",
"dir_category": "dev"
}
@@ -1,23 +0,0 @@
# 通用代码评审检查清单
适用于所有项目,补充五视角扫描法。
## API 设计
- [ ] RESTful 命名是否规范?(复数名词、无动词)
- [ ] 分页参数是否有默认值和上限?
- [ ] 响应格式是否统一?(code/message/data
## 错误处理
- [ ] 错误是否被正确传播?(不要吞掉错误)
- [ ] 用户可见的错误消息是否友好?(不暴露技术细节)
- [ ] 是否有 panic recover 兜底?
## 性能
- [ ] 列表查询是否有分页?(不允许无限制查询)
- [ ] N+1 查询问题?(循环内查数据库)
- [ ] 是否有不必要的全表扫描?(缺少索引)
## 可维护性
- [ ] 魔法数字是否提取为常量?
- [ ] 复杂业务逻辑是否有注释说明?
- [ ] 函数是否过长?(超过 100 行考虑拆分)
@@ -13,16 +13,17 @@ description: 项目级代码评审检查清单。按项目积累特定检查项
## 使用方式
1. `dev-review` 执行五视角扫描时,自动加载当前项目的检查清单
2. 扫描完成后,逐条检查清单项
3. 检查结果附加到 CR 报告的「项目检查清单」章节
1. `dev-review` 执行五视角扫描时,先读取 `references/general.md`
2. 如果仓库是 AI-Proj,读取 `references/ai-proj.md`;如果是 Coolbuy PaaS,读取 `references/coolbuy-paas.md`
3. 扫描完成后,逐条检查适用的清单项
4. 检查结果附加到 CR 报告的「项目检查清单」章节
## 检查清单文件
```
review-checklist-plugin/
├── skills/SKILL.md # 本文件
└── checklists/
review-checklist/
├── SKILL.md # 本文件
└── references/
├── ai-proj.md # AI-Proj 项目清单
├── coolbuy-paas.md # 酷采3.0 项目清单
└── general.md # 通用清单(所有项目适用)
@@ -32,7 +33,7 @@ review-checklist-plugin/
当 CR 中发现了一个**项目特有**的问题模式,且未来可能复发时:
1. 打开对应项目的检查清单文件
1. 打开 `references/`对应项目的检查清单文件
2. 添加条目,格式:`- [ ] {检查项} — 教训:{来源}`
3. 标注严重度和适用范围
@@ -18,6 +18,14 @@
- [ ] JWT token 类型是否区分 access/refresh?— 教训:token 混用导致安全漏洞
- [ ] bcrypt cost 是否使用 12?— 教训:默认 cost 10 导致登录失败
### 租户隔离(多企业安全,源自 REQ-20260711-0004
- [ ] 隔离/权限类修复是否枚举了威胁模型的**所有读取面**?— list 枚举 + 单条直读 + 按 ID/pattern 查 + count + 关联子查询。教训:P1 只修 list 面漏了 find_task/get-by-id 直读面,直读即绕过枚举防护,audit 才逮到高危残留
- [ ] 同一威胁在**镜像面**是否一并处理?— 一个对象类型(task)漏,同类(project/document/manual/history)大概率同漏
- [ ] MCP 裸 SQL(不走仓储层)是否应用 `ResolveTenantScope` / scope 片段?— SSE 面 list_tasks/list_projects 曾裸 SQL 无企业过滤
- [ ] context 注入的是**类型化 key**`EnterpriseIDContextKey{}`)而非字符串 key?— 字符串 key 与 `ResolveTenantScope` 读的类型化 key 不通,静默失效
- [ ] scope 解析不出 / 依赖为 nil 时是否 **fail-closed**(空哨兵拒绝)而非跳过(退化全量)?
- [ ] MCP endpoint 参数是否 snake_case + camelCase 双绑?— CLI 发 snake、bridge 发 camelgin 静默忽略不匹配参数(REQ-20260711-0003
### Redis
- [ ] Redis key 是否有 TTL?— 缺少 TTL 导致内存泄露
- [ ] Redis 不可用时是否降级到数据库?
@@ -37,6 +45,6 @@
## 通用
- [ ] `.env` 凭据文件是否被意外加入 git
- [ ] `.env` 凭据文件是否被意外加入 git
- [ ] 是否有硬编码的 URL/IP/端口?— 应使用配置
- [ ] 错误日志是否包含足够的上下文信息?(user_id, tenant_id, request_id
@@ -8,7 +8,7 @@
### 数据迁移
- [ ] 从酷采2.0迁移的字段映射是否正确?(varchar ID → bigint ID
- [ ] 迁移脚本是否处理了酷采2.0软删除标记(is_delete → deleted_at)?
- [ ] 迁移脚本是否处理了酷采2.0软删除标记(is_delete → deleted_at)?
## 前端(Vue 3 + Ant Design Vue
@@ -0,0 +1,46 @@
# 通用代码评审检查清单
每次代码审查都应加载本清单。
适用于所有项目,补充六视角扫描法(五传统视角 + Karpathy Scope 视角)。
## Karpathy 反模式速查(Scope 审计者视角辅助)
基于 [andrej-karpathy-skills](https://github.com/forrestchang/andrej-karpathy-skills) EXAMPLES.md 提炼。
### ❌ 反模式 → ✅ 正确做法
| 场景 | 反模式(LLM 常犯) | 正确做法 |
|------|-----------------|---------|
| "做个导出功能" | 静默假设文件格式/字段/分页,直接实现 | 列出3种解读(API/文件/任务队列),问用户选哪种 |
| "让搜索更快" | 同时加缓存+索引+async200行 | 列出3种"更快"含义+估算,等确认再做 |
| "加个折扣计算" | Strategy+Abstract+Enum+DataClass50行 | `def calc_discount(amount, pct): return amount * pct / 100` |
| "修保存偏好的bug" | 顺手加 merge/validate/notify/cache | 只改最小范围,加注释"其他特性按需再加" |
| "修空邮件校验bug" | 顺手加用户名校验+类型注解+docstring | 只改空邮件的那2行 |
| "加日志到上传函数" | 改引号风格+加类型注解+重构return逻辑 | 只加日志,保持原有代码风格 |
| "修认证bug" | "我会检查代码并做改进"(无标准) | 先写测试复现bug,再实现修复,再跑测试 |
| "加限流" | 一次提交Redis+多策略+配置系统+监控 | 分4步,每步独立可验证可部署 |
### 触发关键词(出现时加强 Scope 审计)
`export/导出` `faster/更快` `manage/管理` `notify/通知` `fix/修复` `improve/改进` `refactor/重构` `add/添加`
## API 设计
- [ ] RESTful 命名是否规范?(复数名词、无动词)
- [ ] 分页参数是否有默认值和上限?
- [ ] 响应格式是否统一?(code/message/data
## 错误处理
- [ ] 错误是否被正确传播?(不要吞掉错误)
- [ ] 用户可见的错误消息是否友好?(不暴露技术细节)
- [ ] 是否有 panic recover 兜底?
## 性能
- [ ] 列表查询是否有分页?(不允许无限制查询)
- [ ] N+1 查询问题?(循环内查数据库)
- [ ] 是否有不必要的全表扫描?(缺少索引)
## 可维护性
- [ ] 魔法数字是否提取为常量?
- [ ] 复杂业务逻辑是否有注释说明?
- [ ] 函数是否过长?(超过 100 行考虑拆分)
@@ -0,0 +1,11 @@
{
"name": "slark-cicd-plugin",
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "slark-cicd",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,201 @@
---
name: slark-cicd
description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/CD 可执行 runbook。覆盖本地预推快检、PR 与内网 ci/internal-gate 门禁、合并 origin/main、staging 真机预验、审批式生产发布,以及在 m5max 构建签名、公证并上传 macOS/Windows Desktop 包到 OSS。当用户要在 slarkqiudl/qiu-slark)里发布/上线、部署到生产或 staging、发布 Desktop 安装包、盯 CI/合并 PR、复现或验证运行时行为、或排查发布链/推送路由问题时使用。
---
# Slark CI/CD runbook
驱动 Slark 从改代码到上线的端到端流程。权威流程与完整细节见仓库文档
`docs/cicd-staging-production.md`(本技能命令与之一致);本文件是可直接照做的分阶段清单。
仓库:`qiudl/qiu-slark`(本机克隆通常在 `/Users/donglinlai/coding/slark`)。所有命令在**仓库根**执行。
`ai-proj` 相关命令加 `GODEBUG=netdns=go` 前缀。
## 铁律(先记死)
- GitHub `origin/main` 是唯一代码权威、合并入口与生产发布来源;`internal` 只用于 CI/镜像,永不发布。
- 生产发布只接受**最新 `origin/main` 的精确 40 位 SHA**`HEAD != origin/main` 会被 `deploy.sh` 拒绝。
- 生产发布必须在**普通 clone** 中执行,且 `.git` 必须是目录;`release-prod.sh` / `deploy.sh` 会拒绝
`.git` 为指针文件的 git worktree。不要污染现有 checkout:主目录脏或被占用时,新建干净的临时 clone。
- 审批式部署,永不自动:亲手敲的 `sudo`/`rm -rf`/`dd`/`curl|sh`/包安装是 `destructive`NEVER_AUTO),
**每次都要人审**;命中审批闸就**等人批**,绝不绕过、绝不把这些 key 加进 auto-approve 白名单。
- staging 与生产硬隔离:staging 脚本带生产 IP 拒运行护栏、不 push origin、不反向 rsync。
- 当前 Slark staging 权威目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e`
Name=`slark-staging`2026-08-18 核验公网 IP `15.135.112.181`)。公网 IP 自动分配,发布前必须按
实例 ID 重查并确认 Name/状态;不得凭历史 IP 判断目标。
- `82.157.141.202` 是 ai-proj 预生产宿主机。其 `/opt/slark-staging` cohost 是隔离的 Slark 兼容验收栈,
但不是 Slark 独立预生产;只有任务明确指定 cohost 时才能使用,不得作为普通 Slark staging 的默认目标。
- staging 发布若影响 `packages/daemon``packages/runtime`、Server/daemon 公共协议或 Desktop 公共运行链,
必须把 `server-staging``daemon-staging` 更新到同一精确 SHA;两者版本不一致不得宣告 staging 成功。
- 只修 PR 范围内的 CI 失败;不为过检去改 CI/workflow 或做无关改动。
## 选择链路
- 「发布/上线/部署生产」→ 生产发布链:阶段 1→2→4→5。
- 「Desktop/桌面版打包、发布、上传 OSS」→ 阶段 1→2 后走 `docs/desktop-oss-release.md` 的 m5max 双平台链路。
- 「盯 CI/合并 PR」→ 阶段 1→2(用 babysit 心态循环到 green + mergeable + 评论收口)。
- 「复现/验证运行时行为、别碰生产」→ staging 预验链(阶段 3,可独立随时跑)。
## 阶段 1PR 与内网 gate`ci/internal-gate`
真实门禁是内网 CIGitHub Actions 已停用)。检查与自查:
```bash
gh pr checks <PR> # 应见 ci/internal-gate
gh pr view <PR> --json state,mergeable,mergeStateStatus,isDraft,reviewDecision
```
- 基础门(全 PR):release-contract / member-gate / authz-matrix / controlled-content-exits /
onconflict-predicate / migration-idempotency / wsl-path-translation / typecheck / test(全量)。
- 条件外部门(按改动路径):改 server DB/迁移/schema → `postgresql-security`;改 web → `web-e2e`
android / daemon+desktop 各有门。
- 本地复跑基础门可跑部分:`pnpm -r typecheck && pnpm test`
红了:只修本 PR 范围内的失败。疑似无关的合并阻塞→先 merge 最新 `main`(可能别的 PR 已修)。
`BLOCKED: <label> requires <VAR>` = runner 环境未配外部门命令,属运维问题,不是 PR 代码问题。
## 阶段 2:合并到 `origin/main`
合并前确认:`mergeable=MERGEABLE``mergeStateStatus=CLEAN`、非 draft、`ci/internal-gate` 绿、评论收口。
```bash
gh pr merge <PR> --merge # 仓库风格:merge commit
```
若本机 `main` 被其他 worktree 占用导致 `--delete-branch` 本地 checkout 失败(属本地副作用):
```bash
gh pr view <PR> --json state,mergedAt,mergeCommit # 确认 GitHub 侧 MERGED
git push origin --delete <feature-branch> # 删远端
git branch -D <feature-branch> # 删本地(先切走)
```
## 阶段 3:staging 真机预验(可选,随时)
### 3.1 当前权威路径:Slark 独立 staging
稳定目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e`Name=`slark-staging`)。它使用自动分配
公网 IP,因此每次部署前先查询当前 IP;2026-08-18 核验值为 `15.135.112.181`
```bash
aws ec2 describe-instances --region ap-southeast-2 \
--instance-ids i-013e5ca3fdfddc13e \
--query 'Reservations[0].Instances[0].{Name:Tags[?Key==`Name`]|[0].Value,State:State.Name,PublicIp:PublicIpAddress}' \
--output table
export STAGING_SSH_KEY=~/.ssh/cloud-server-syd.pem
export STAGING_REMOTE=ubuntu@15.135.112.181
export STAGING_REMOTE_IP=15.135.112.181
export STAGING_BASE=http://15.135.112.181
scripts/deploy-staging.sh --migrate-dry-run
scripts/deploy-staging.sh
STAGING_TOKEN=$(cat /tmp/.staging_token) scripts/staging-seed.sh
scripts/staging-verify-isolation.sh
```
不得把查询到的 Name 非 `slark-staging` 的机器当作目标。四件套细节与环境隔离约束见
`docs/req-20260811-0059-staging-environment-design.md`
### 3.2 非权威路径:ai-proj cohost(仅明确指定时)
ai-proj cohost 位于 `ubuntu@82.157.141.202:/opt/slark-staging`。全程使用隔离数据、数据库、密钥和状态目录,
但宿主机身份仍是 ai-proj staging;普通“Slark 预生产”不得走这里。仅在任务明确要求 cohost 兼容验收时,
先在**干净的独立 worktree**检出待验精确 SHA,再执行:
```bash
STAGING_SSH_KEY=~/.ssh/ai_proj_stg.pem \
STAGING_KNOWN_HOSTS_FILE=~/.ssh/known_hosts \
STAGING_REMOTE=ubuntu@82.157.141.202 \
STAGING_REMOTE_IP=82.157.141.202 \
scripts/deploy-staging-cohost.sh
```
如果改动影响 daemon/runtime/公共协议,还需用 Compose `daemon` profile 更新 `daemon-staging`,并核对
Server/daemon revision 同为待验 SHA。
### 3.3 环境选择检查
普通 Slark staging 默认走 3.1;只有用户或需求明确说“ai-proj cohost”才走 3.2。任何验收都必须记录:
环境稳定身份、当次 IP、待验 40 位 SHA、Server/daemon revision/status、Server health 和目标功能结果。
routed-remote 要 `network=enabled` 必须同时满足:① agent `workspace_access=write` +
`repository_access=write`;② per-agent 工作区是可写 git worktree。细节见
`docs/req-20260811-0059-staging-environment-design.md`。staging 刻意不碰 `deploy.sh` 的生产闸门。
## 阶段 4:生产发布(审批式)
**唯一姿势**:从干净的普通 clone 发布最新 `origin/main``HEAD == origin/main` 精确 SHA)。staging 可用
独立 worktree,但生产脚本为保证同步与发布边界,会拒绝 worktree;不要在用户已有脏 checkout 中清理或发布。
```bash
# 若当前仓库不是普通 clone,先在安全父目录创建一次性干净 clone:
git clone https://github.com/qiudl/qiu-slark.git <clean-production-clone>
cd <clean-production-clone>
git fetch origin main
test -d .git # 必须通过;worktree 的 .git 是文件
git status --porcelain # 必须干净
git checkout --detach "$(git rev-parse origin/main)" # 在产品分支上时先 detach 到该 SHA
pnpm release:prod # = release-prod.sh → deploy.sh --backend --no-install --no-mirror
# 备选:scripts/deploy.sh --pull --backend
```
- `deploy.sh``check-production-release-sha.sh``HEAD == origin/main` SHA,不符即拒。
- `--backend` 才跑迁移:owner 连接(DDL 权)与运行时 `slark_app`(DML-only)分离;幂等、失败即非零。
- 命中审批闸(发布/sudo)→ **等人批**;不要绕。脚本内部 SSH 载荷子进程的 sudo 不触发闸。
- 普通工作 clone 发布完切回原分支:`git checkout <branch>`;一次性发布 clone 可保留作审计,清理时用可恢复方式。
## 阶段 5:发布后核查
```bash
scripts/verify-deploy.sh # 健康(前端/api/health=200)、bundle、server/daemon SHA、迁移集、federation、凭据
```
预期 `✓ deploy verification passed: server=<sha> daemon=<sha>``<sha> == origin/main`,舰队 `online ≥ 发布前`
镜像落后告警(默认 `--no-mirror`)非阻断,需补推按告警的幂等命令(需 Bitwarden session);镜像仅单向 fast-forward。
## Desktop m5max 双平台发布
Desktop 安装包不走 Server 生产部署脚本。权威流程见仓库 `docs/desktop-oss-release.md`,已验证基线为
REQ-20260819-0005 / Desktop 0.3.1
- 版本 PR 通过 `ci/internal-gate` 并合并后,m5max 从最新 `origin/main` 精确 SHA 的干净普通 clone 构建。
- m5max 原生构建、Developer ID 签名并公证/staple macOS DMG/ZIP;交叉构建 Windows x64 NSIS EXE。
- Darwin 不能执行 Windows 原生 `smoke:windows`。只有发布负责人明确接手 Windows 原生验收时才能继续交叉打包,交付证据必须标注该边界。
- 资产不回传开发机;由 m5max 直接上传 `xiaoqu-public-file/slark/desktop/releases/v<version>/`manifest 最后更新。
- `xiaoqu-public-file` 的 canonical 凭证源是发布控制机上的 `/Users/donglinlai/coding/param.rxt`(必须为 `0600`);AK/SK 分别位于 XML 标签 `ossAccessKeyId` / `ossAccessKeySecret`。不得改猜 Bitwarden、Keychain、通用 `credentials.env` 或 m5max 本地配置;只有该文件缺失、权限不符或标签解析为空时才停止并报告。
- m5max 不持久化 OSS 凭证。发布控制机从 `param.rxt` 提取 AK/SK 后,只用 `printf '%s\n%s\n' ... | ssh m5max ...` 经 SSH stdin 传两行;远端用 `read -r` 接收并仅为当次 shell 导出 `OSS_ACCESS_KEY_ID` / `OSS_ACCESS_KEY_SECRET`,随后运行上传脚本。禁止 `scp` 凭证文件、写远端临时文件或把值打印到日志。
- m5max 非交互 SSH 必须显式补 `PATH=/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$PATH`;已验证 `ossutil` 位于 `/Users/johnq/.local/bin/ossutil`。执行前检查 Node、三个安装包、精确 main SHA 和干净工作树,避免因交互 shell 环境差异重新猜路径。
- m5max 默认出口若走 `utun`,只为当次 `ossutil` 使用 `--bind-address "$(ipconfig getifaddr en0)"`,不得修改整机 Tailscale/默认路由。
- 完成前核对三份资产 SHA256/大小、macOS 签名与公证、公开 URL/manifest,并回写 ai-proj 证据。
## 推送路由自查(换机器/新克隆必做)
```bash
git remote -v # origin 收发都应是 https://github.com/qiudl/qiu-slark.git
git config --get-regexp '^url\.' # 有输出 = pushInsteadOf 改写,push 可能被静默重定向到内网
scripts/git-release-governance.sh check-local
scripts/git-release-governance.sh status
```
## 故障分流
| 现象 | 处置 |
| --- | --- |
| `release-prod` 拒绝 HEAD≠origin/main | `git fetch origin main` 后 detach 到 origin/main SHA,工作树须干净 |
| `release-prod` 拒绝 git worktree / `.git` 不是目录 | 改用干净的普通 clone,锁定最新 `origin/main`;不要在现有脏 checkout 中强行切换 |
| `ci/internal-gate` red | 看 gate 分组日志找 FAIL 项;`pnpm -r typecheck && pnpm test` 本地复现修复 |
| `BLOCKED: <label> requires <VAR>` | runner 未配外部门命令,报运维配 `SLARK_CI_*_CMD`,非 PR 问题 |
| 合并阻塞但与 PR 无关 | merge 最新 `main` 再看 |
| push 成功但 GitHub 没有 | 查 `git config --get-regexp '^url\.'`,移除 pushInsteadOf 或显式指定 GitHub URL |
| staging routed-remote 恒断网 | 查两道放网杠杆(workspace_access=write + 可写 git worktree |
| cohost Server 已更新但 daemon 仍是旧 SHA | 用 `--profile daemon up -d daemon-staging` 更新 daemon;核对两容器 revision 后才算完成 |
| cohost daemon 启动即退出/fatal | 看 `docker logs slark_daemon_staging`;修复凭据/环境或版本契约,禁止用旧 daemon 冒充验收通过 |
| verify 失败 SHA 不符 | 部署到了旧码,重发 |
## ai-proj 流程(强制)
任何改产品/代码行为的工作,动手前先在 ai-proj 立项 + 建 Task + 关联,实现/测试/交付回写;
commit/PR 引用 `REQ-...`。纯文档/工具/只读排查除外。详见仓库 `AGENTS.md` / `CLAUDE.md`
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "data-excel",
"install_type": "skill",
"dir_category": "integration"
}
@@ -1,14 +1,11 @@
{
"name": "doubao-voice-plugin",
"description": "Doubao (豆包) Voice API integration for TTS and ASR",
"version": "1.0.0",
"version": "1.0.1",
"author": {
"name": "qiudl"
},
"skills": [
{
"name": "doubao-voice",
"path": "./skills/SKILL.md"
}
]
"install_name": "doubao-voice",
"install_type": "skill",
"dir_category": "integration"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "feishu-bitable",
"install_type": "skill",
"dir_category": "integration"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "feishu-docx",
"install_type": "skill",
"dir_category": "integration"
}
@@ -4,5 +4,8 @@
"version": "1.1.0",
"author": {
"name": "qiudl"
}
},
"install_name": "feishu",
"install_type": "skill",
"dir_category": "integration"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "siyuan",
"install_type": "skill",
"dir_category": "integration"
}
+60 -150
View File
@@ -5,128 +5,35 @@ description: 思源笔记 API 集成。通过自然语言创建、编辑、搜
# 思源笔记 API 集成 Skill
## 服务配置
## 环境变量配置(必须)
### 阿里云生产环境 (推荐)
本 skill 通过环境变量读取思源笔记连接信息,不同用户/组织配置不同的值。
| 配置项 | 值 |
|--------|-----|
| 服务地址 | `https://siyuan.pipexerp.com` (HTTPS) |
| HTTP访问 | `http://47.93.23.182` (重定向到HTTPS) |
| SSH 别名 | `siyuan` |
| 访问码 | `SiYuan@2026` |
| 版本 | **3.1.11** (2026-02-16 升级) |
| 部署位置 | 阿里云 Ubuntu 24.04 (Docker) |
| 容器名称 | `siyuan` |
| 镜像 | `b3log/siyuan:v3.1.11` |
| 数据目录 | `/opt/siyuan/workspace` |
| 反向代理 | Caddy (HTTPS + SSL证书) |
| SSL证书 | Let's Encrypt (自动续期) |
**在 `~/.claude/settings.json` 中配置:**
### Tailscale 内网环境 (备用)
```json
{
"env": {
"SIYUAN_URL": "${SIYUAN_URL}",
"SIYUAN_TOKEN": "your-api-token-here"
}
}
```
| 配置项 | 值 |
|--------|-----|
| 服务地址 | `http://47.93.23.182` (Tailscale) |
| 局域网地址 | `http://192.168.1.50:6806` |
| API Token | `nfnycjb1g8vbexb2` |
| 版本 | 3.1.5 |
| 部署位置 | 飞牛OS (Docker) |
或在项目级 `.claude/settings.json` 中配置(优先级更高)。
**检查配置是否就绪:** 执行任何思源操作前,先确认环境变量已设置:
```bash
echo "URL: ${SIYUAN_URL:-未配置}"
echo "TOKEN: ${SIYUAN_TOKEN:+已配置}"
```
如果未配置,提示用户在 `~/.claude/settings.json``env` 中添加 `SIYUAN_URL``SIYUAN_TOKEN`
---
## 服务器管理 (阿里云)
### SSH 连接
```bash
# 快捷连接
ssh siyuan
# 完整连接命令
ssh -i /Users/donglinlai/Downloads/siyuan.pem root@47.93.23.182
```
### Docker 管理
```bash
# 查看容器状态
ssh siyuan "docker ps | grep siyuan"
# 查看日志
ssh siyuan "docker logs -f siyuan --tail 100"
# 重启服务
ssh siyuan "docker restart siyuan"
# 停止服务
ssh siyuan "docker stop siyuan"
# 启动服务
ssh siyuan "docker start siyuan"
# 升级到特定版本(推荐)
ssh siyuan "docker stop siyuan && docker rm siyuan"
ssh siyuan "docker pull b3log/siyuan:v3.1.11"
ssh siyuan "docker run -d --name siyuan --restart=always \
-p 127.0.0.1:6806:6806 \
-v /opt/siyuan/workspace:/siyuan/workspace \
-e LANG=en_US.UTF-8 \
b3log/siyuan:v3.1.11 \
--workspace=/siyuan/workspace \
--accessAuthCode=SiYuan@2026"
```
### 数据备份与恢复
```bash
# 查看数据目录大小
ssh siyuan "du -sh /opt/siyuan/workspace"
# 备份数据到服务器
ssh siyuan "tar -czf /root/siyuan-backup-\$(date +%Y%m%d).tar.gz /opt/siyuan/workspace"
# 下载备份到本地
scp siyuan:/root/siyuan-backup-*.tar.gz ~/Downloads/
# 恢复数据(先停止容器)
ssh siyuan "docker stop siyuan && \
tar -xzf /root/siyuan-backup-YYYYMMDD.tar.gz -C / && \
docker start siyuan"
```
### 访问测试
```bash
# 测试 HTTPS 访问
curl -I https://siyuan.pipexerp.com
# 测试认证 API
curl -s -X POST https://siyuan.pipexerp.com/api/system/version | jq .
# 浏览器访问
open https://siyuan.pipexerp.com
```
### Caddy 反向代理管理
```bash
# 查看 Caddy 状态
ssh siyuan "systemctl status caddy"
# 重启 Caddy
ssh siyuan "systemctl restart caddy"
# 查看 Caddy 配置
ssh siyuan "cat /etc/caddy/Caddyfile"
# 查看 SSL 证书
ssh siyuan "journalctl -u caddy | grep certificate"
```
---
## 🔐 保存门禁:敏感信息脱敏
## 🔐 保存禁:敏感息脱敏
**保存任何内容到思源笔记前,必须先完成脱敏处理。**
@@ -201,8 +108,8 @@ api.upsert_doc(notebook_id, "/文档路径", content)
所有 API 请求使用 POST 方法,需携带 Token
```bash
curl -X POST https://siyuan.pipexerp.com/api/xxx \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/xxx \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"param": "value"}'
```
@@ -239,8 +146,8 @@ curl -X POST https://siyuan.pipexerp.com/api/xxx \
#### 列出所有笔记本
```bash
curl -X POST https://siyuan.pipexerp.com/api/notebook/lsNotebooks \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/notebook/lsNotebooks \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{}'
```
@@ -248,8 +155,8 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/lsNotebooks \
#### 创建笔记本
```bash
curl -X POST https://siyuan.pipexerp.com/api/notebook/createNotebook \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/notebook/createNotebook \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name": "笔记本名称"}'
```
@@ -257,8 +164,8 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/createNotebook \
#### 删除笔记本
```bash
curl -X POST https://siyuan.pipexerp.com/api/notebook/removeNotebook \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/notebook/removeNotebook \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"notebook": "笔记本ID"}'
```
@@ -276,22 +183,22 @@ curl -X POST https://siyuan.pipexerp.com/api/notebook/removeNotebook \
```bash
# 1. 先查询是否存在(按路径精确匹配)
DOC_PATH="/网络管理/家庭Tailscale网络"
EXISTING=$(curl -s -X POST https://siyuan.pipexerp.com/api/query/sql \
-H "Authorization: Token nfnycjb1g8vbexb2" \
EXISTING=$(curl -s -X POST ${SIYUAN_URL}/api/query/sql \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"stmt\": \"SELECT id FROM blocks WHERE type='d' AND hpath='${DOC_PATH}' LIMIT 1\"}" \
| jq -r '.data[0].id // empty')
if [ -n "$EXISTING" ]; then
# 2a. 存在则更新
curl -s -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -s -X POST ${SIYUAN_URL}/api/block/updateBlock \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"id\": \"${EXISTING}\", \"dataType\": \"markdown\", \"data\": \"# 更新的内容\"}"
else
# 2b. 不存在则创建
curl -s -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -s -X POST ${SIYUAN_URL}/api/filetree/createDocWithMd \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"notebook": "笔记本ID", "path": "/网络管理/家庭Tailscale网络", "markdown": "# 新内容"}'
fi
@@ -300,8 +207,8 @@ fi
#### 创建文档 (仅新建时使用)
```bash
curl -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/filetree/createDocWithMd \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"notebook": "笔记本ID",
@@ -313,8 +220,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/createDocWithMd \
#### 获取文档内容
```bash
curl -X POST https://siyuan.pipexerp.com/api/filetree/getDoc \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/filetree/getDoc \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"id": "文档ID"}'
```
@@ -322,8 +229,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/getDoc \
#### 删除文档
```bash
curl -X POST https://siyuan.pipexerp.com/api/filetree/removeDoc \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/filetree/removeDoc \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"notebook": "笔记本ID", "path": "/文档路径"}'
```
@@ -331,8 +238,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/removeDoc \
#### 重命名文档
```bash
curl -X POST https://siyuan.pipexerp.com/api/filetree/renameDoc \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/filetree/renameDoc \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"notebook": "笔记本ID", "path": "/旧路径", "title": "新标题"}'
```
@@ -344,8 +251,8 @@ curl -X POST https://siyuan.pipexerp.com/api/filetree/renameDoc \
#### 插入块
```bash
curl -X POST https://siyuan.pipexerp.com/api/block/insertBlock \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/block/insertBlock \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"dataType": "markdown",
@@ -357,8 +264,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/insertBlock \
#### 更新块
```bash
curl -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/block/updateBlock \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"dataType": "markdown",
@@ -370,8 +277,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/updateBlock \
#### 删除块
```bash
curl -X POST https://siyuan.pipexerp.com/api/block/deleteBlock \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/block/deleteBlock \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"id": "块ID"}'
```
@@ -383,8 +290,8 @@ curl -X POST https://siyuan.pipexerp.com/api/block/deleteBlock \
#### 全文搜索
```bash
curl -X POST https://siyuan.pipexerp.com/api/search/fullTextSearchBlock \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/search/fullTextSearchBlock \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"query": "搜索关键词",
@@ -395,8 +302,8 @@ curl -X POST https://siyuan.pipexerp.com/api/search/fullTextSearchBlock \
#### SQL 查询
```bash
curl -X POST https://siyuan.pipexerp.com/api/query/sql \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/query/sql \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"stmt": "SELECT * FROM blocks WHERE content LIKE '\''%关键词%'\'' LIMIT 10"
@@ -410,8 +317,8 @@ curl -X POST https://siyuan.pipexerp.com/api/query/sql \
#### 导出 Markdown
```bash
curl -X POST https://siyuan.pipexerp.com/api/export/exportMdContent \
-H "Authorization: Token nfnycjb1g8vbexb2" \
curl -X POST ${SIYUAN_URL}/api/export/exportMdContent \
-H "Authorization: Token ${SIYUAN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"id": "文档ID"}'
```
@@ -421,13 +328,16 @@ curl -X POST https://siyuan.pipexerp.com/api/export/exportMdContent \
## Python 封装
```python
import os
import requests
from typing import Optional, Dict, Any
class SiYuanAPI:
"""思源笔记 API 封装"""
def __init__(self, base_url: str = "https://siyuan.pipexerp.com", token: str = "nfnycjb1g8vbexb2"):
def __init__(self, base_url: str = None, token: str = None):
base_url = base_url or os.environ.get("SIYUAN_URL", "")
token = token or os.environ.get("SIYUAN_TOKEN", "")
self.base_url = base_url
self.headers = {
"Authorization": f"Token {token}",
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "siyuan-to-feishu",
"install_type": "skill",
"dir_category": "integration"
}
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "wecom",
"install_type": "skill",
"dir_category": "integration"
}
@@ -0,0 +1,11 @@
{
"name": "req-audit-plugin",
"description": "部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "req-audit",
"install_type": "command",
"dir_category": "req"
}
+105
View File
@@ -0,0 +1,105 @@
---
name: req-audit
description: 部署后审计。运行时日志检查 + 静态缺陷分析 + 设计偏移检测。可独立调用或由 /req done 自动触发。
---
# 部署后审计 (audit)
对本次部署执行三维度审计:运行时行为、代码缺陷、设计偏移。
## 执行流程
### 2a. 运行时检查
检查部署后是否有新增错误。
**优先 SSH**
```bash
ssh -o ConnectTimeout=3 ${EC2_USER}@${EC2_HOST} \
"docker logs ${APP_CONTAINER} --since 10m 2>&1 | grep -i 'error\|panic\|fatal\|traceback'"
```
**降级 1CI 日志**
```bash
RUN_ID=$(gh run list --repo ${OWNER}/${REPO} --limit 1 --json databaseId -q '.[0].databaseId')
gh run view ${RUN_ID} --repo ${OWNER}/${REPO} --log 2>&1 | grep -i 'error\|panic\|fatal'
```
**降级 2N/A + 警告**
```
⚠️ 无法获取运行时日志(SSH 不可达 + CI 日志无异常信息),2a 标记为 N/A。
运行时问题可能未被发现,建议手动检查服务器日志。
```
### 2b. 静态分析
调用现有 `/defect-analysis` command,传入变更文件:
```
对以下变更文件执行缺陷分析:
{变更文件列表}
重点关注:运行时行为(不是合并前 CR 的重复,而是部署后复查)
```
输出:缺陷清单(按致命/高/中/低分级)
### 2c. 设计偏移检测
1. 读取需求的 PRD 文档(linkRole=prd 的任务文档)
2. 读取本次变更的源码
3. AI 对比分析:
```
请对比以下 PRD 功能点和实际代码实现:
PRD 功能点:
{从 PRD 提取的功能清单}
实际代码变更:
{变更文件的关键逻辑}
检查:
- 遗漏的功能(PRD 有但代码没实现)
- 多做的功能(代码有但 PRD 没提)
- 实现方式与 PRD 描述不一致
```
输出:偏移项列表
### 合并报告
```markdown
## 部署后审计报告
### 2a 运行时检查
| 检查项 | 结果 | 详情 |
| 新增错误 | ✅ 无 / ❌ 有 N 条 | ... |
### 2b 静态分析
| 缺陷 | 严重度 | 描述 |
(来自 defect-analysis 输出)
### 2c 设计偏移
| 偏移项 | 类型 | 说明 |
### 结论
- 致命/高级缺陷: N 个 → {阻断/通过}
- 中/低级缺陷: N 个 → 已创建 backlog
- 设计偏移: N 项 → {建议处理方式}
```
### 缺陷分级处理
| 级别 | 处理 |
|------|------|
| 致命 | 阻断归档 + 回滚建议 + `ai-proj task create` 创建修复任务并关联需求 |
| 高级 | 阻断归档 + 回滚建议 + 创建修复任务 |
| 中级 | 警告不阻断 + 创建 backlog 任务 |
| 低级 | 记录到报告,不创建任务 |
## 任务关联
- linkRole: `code_review`
- 任务标题: `【审计】部署后审计: {需求标题}`
- 报告附加到任务文档
@@ -4,5 +4,8 @@
"version": "1.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "req-compare",
"install_type": "skill",
"dir_category": "req"
}
@@ -4,5 +4,8 @@
"version": "2.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "req-design",
"install_type": "skill",
"dir_category": "req"
}
@@ -447,9 +447,30 @@ PRD 说「做什么」(用户故事、业务规则、验收标准),设计
---
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
**规则:本 skill 产出的设计文档禁止受 auto-memory 影响结构和字段定义。**
### 禁止行为
1. 不得用 memory 里的历史 API 契约填充当前设计(避免张冠李戴)
2. 不得根据 memory 偏好省略"变更文件清单"/"数据模型变更"等章节
3. 不得读取 `~/.claude/projects/*/memory/` 生成 API 字段或 SQL
### 允许行为
- memory 可影响交互风格、命令推荐
- memory 可记住"用户偏好 RESTful 而非 RPC" 这类**偏好**,但字段定义必须基于当前需求
### Why
设计文档是开发契约,字段/接口/表结构错一个字就是 bug。memory 污染会让 AI 脑补出"看起来像但不对"的字段。必须严格基于当前 PRD + 代码现状生成。
**参考**devflow-claude `plugins/req/commands/_common.md` 同名规则。
---
## 变更记录
| 版本 | 日期 | 变更内容 |
|------|------|----------|
| V1.0 | 2026-01-26 | 初始版本(原名 req-dev |
| V2.0 | 2026-04-06 | 重构为 req-design:移除架构指南和编码规范,聚焦 API 契约 + 任务拆分 |
| V2.1 | 2026-04-16 | 新增 Memory 隔离规则(REQ-20260416-0017 |
@@ -5,5 +5,8 @@
"deprecated": true,
"author": {
"name": "qiudl"
}
},
"install_name": "req-dev",
"install_type": "skill",
"dir_category": "req"
}
+54
View File
@@ -322,6 +322,59 @@ npm run lint
---
## ⚠️ 既有契约与读模型迁移门禁(必须)
> **惨痛教训**:新写链路把真实数据写入新表,旧 API 仍读原表,前端虽然没改却从此看不到新数据。“后端已成功执行”不等于“用户能看到执行结果”。
开发计划涉及新表、新账本、新写路径、替换存储模型或接口版本时,必须先完成以下门禁。未通过时,不得把“新建平行表/新建平行 API”写成默认方案。
### 1. 先证明旧模型不能复用
- [ ] 已检查现有表、字段、索引、状态机和扩展字段(如 metadata/JSON)。
- [ ] 已说明为什么增加字段、子表或现有类型无法满足需求。
- [ ] 新表承载的是真正独立的实体或生命周期,而不是为同一业务实体建立第二个彼此脱节的世界。
- [ ] 已区分“内部不可变证据/事件账本”和“产品对外读模型”;新建内部账本不代表必须更换原有产品接口。
### 2. 真实调用方查证
必须从对外接口向所有消费方反向追踪,不能只审查新后端写路径:
- [ ] 列出现有 API 路径、请求参数、响应结构和身份/分页语义。
- [ ] 使用 `rg` 查找 Web、iOS、Android、定时任务、报表和第三方调用方。
- [ ] 若仓库存在 `.codex-fe-ref/`,必须在其中查证配套前端的真实调用。
- [ ] 开发计划的变更文件清单必须覆盖“写入 → 投影/查询 → API → 页面”完整链路。
### 3. 稳定契约默认不变
- 既有用户界面已消费的 API,默认保留路径和响应契约;除非用户明确批准破坏性迁移,不得通过新建版本接口把兼容成本转嫁给前端。
- 同一业务概念只保留一个产品级读模型。内部账本可以拆分,但必须投影回既有读模型,或由原 API 无感聚合。
- 日志、告警、信号或推测数据不得在查询降级时冒充业务事实(例如把 warning 当成交易记录)。
### 4. 不可避免新写模型时的强制迁移计划
若确有独立生命周期、不可变审计或 1:N/N:N 证据需求,可以增加新表,但开发计划必须同时包含:
1. **写入兼容**:双写,或从新账本向既有读模型做事务性/幂等投影。
2. **历史回填**:有界、可恢复、可重跑,且不猜测无法证明的归属。
3. **去重身份**:定义跨旧/新数据源的稳定业务键,并用数据库唯一约束或等价强保证防重。
4. **读取切换**:定义何时以新证据为准、旧数据如何兼容,以及结果数量/金额/状态对账门槛。
5. **回滚策略**:回滚新写入时仍能读取已产生的真实业务事实,不删除或隐藏已成功数据。
6. **可观测性**:监控新写入成功但旧读模型缺失、投影延迟、去重冲突和回填差异。
### 5. 用户可见性是必须验收项
测试不得只证明“新写路径成功”。至少要有一个真实业务记录贯穿测试,同时证明:
- [ ] 新写路径产生了正确事实。
- [ ] 原有对外 API 可以立即或在约定 SLA 内返回该记录。
- [ ] 每个真实消费页面/客户端都能正确解码并展示。
- [ ] 时间、方向、状态、数量、金额与唯一标识在数据库、API 和 UI 之间一致。
- [ ] 部分失败、重试、重复投递、迟到数据和回滚后仍不丢数、不重复、不冒充。
开发计划必须把上述内容落成明确的变更文件、任务、测试用例和发布门禁,不得只在“风险”章节留一句提醒。
---
## 开发计划文档模板
```markdown
@@ -1695,3 +1748,4 @@ migrate -path migrations -database "postgres://..." up
|------|------|----------|--------|
| V1.0 | 2026-01-26 | 初始版本,创建 req-dev 技能 | Claude + qiudl |
| V1.1 | 2026-01-29 | 添加 iOS 端开发规范(SwiftUI + MVVM 架构) | Claude Opus 4.5 |
| V1.2 | 2026-08-26 | 新增既有契约与读模型迁移强制门禁,防止新写链路与原 API/前端脱节 | Codex |
@@ -0,0 +1,11 @@
{
"name": "req-lookback-plugin",
"description": "回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "req-lookback",
"install_type": "command",
"dir_category": "req"
}
@@ -0,0 +1,87 @@
---
name: req-lookback
description: 回归测试。部署后自动验证变更涉及的功能是否正常。可独立调用或由 /req done 自动触发。
---
# 回归测试 (lookback)
对本次部署变更的功能执行自动化验证。
## 执行流程
### 1. 获取变更文件列表
```bash
# 优先从最近合并的 PR 获取(覆盖 merge 多 commit 场景)
gh pr list --state merged --base main --limit 1 --json number,files
# 回退:git diff
git diff HEAD~1..HEAD --name-only
```
### 2. 自动发现验证项(动态推断)
读取变更文件,按类型推断需要验证什么:
| 文件类型 | 推断方式 | 验证命令 |
|----------|---------|---------|
| `*.vue` / `*.tsx` | grep router 配置,提取对应路由路径 | `curl -sf -o /dev/null -w "%{http_code}" ${SERVER}${ROUTE}` |
| `app/api/*.py` | grep 路由装饰器 `@router.get/post`,提取 API 路径 | `curl -sf -o /dev/null -w "%{http_code}" ${SERVER}/api${PATH}` |
| `app/services/*.py` | 找到引用该 service 的 api 文件,提取关联 API | 同上 |
| `nginx.conf` | 提取 location 块 | `curl` 各路径检查状态码 |
| `docker-compose.yml` | 提取服务列表 | `docker ps` 检查容器状态 |
| `alembic/*.py` | 提取表名 | `psql -c "\d table_name"` |
| `*.md` / `*.txt` / `SKILL.md` | 纯文档 | **N/A(自动通过)** |
| `*.css` / `*.scss` | 纯样式 | 轻量验证:页面可达即可 |
### 3. 执行验证
**远端模式**SSH 可达时):
```bash
# 3 秒超时检测
ssh -o ConnectTimeout=3 ${EC2_USER}@${EC2_HOST} "echo ok" 2>/dev/null
# 成功 → 执行完整验证(docker ps + curl + docker logs
```
**降级模式**SSH 不可达时):
```bash
# 检查最近 CI run 是否成功
gh run list --repo ${OWNER}/${REPO} --limit 1 --json conclusion
# conclusion=success → PASS
```
### 4. 输出报告
```markdown
## 回归测试报告
### 变更范围
| 文件 | 类型 | 推断的验证项 |
### 测试结果
| 测试项 | 方式 | 预期 | 实际 | 状态 |
### 结论: ✅ PASS / ❌ FAIL / N/A
```
### 5. 失败处理
```
⚠️ 回归测试失败:{失败项描述}
建议操作:
1. [回滚] git revert HEAD~1 + /req deploy(推荐,影响最小)
2. [修复] 创建 hotfix 分支修复后重新部署
3. [忽略] 标记为已知问题,继续归档
输入 1/2/3:
```
- 选 1 → 执行 `git revert`,提示用户运行 `/req deploy`
- 选 2 → 创建 hotfix 任务(ai-proj create_task),阻断归档
- 选 3 → 记录到报告,继续
## 任务关联
- linkRole: `test`
- 任务标题: `【回归】回归测试: {需求标题}`
- 报告附加到任务文档
@@ -4,5 +4,8 @@
"version": "2.0.0",
"author": {
"name": "qiudl"
}
},
"install_name": "req",
"install_type": "skill",
"dir_category": "req"
}
+12 -1
View File
@@ -41,7 +41,8 @@ analysis → design → dev → review → testing → [待部署池] → releas
- PRD 文档是提交评审的前置条件;代码评审是测试的前置条件
- **force=true 禁止自动使用** — 门禁未通过时必须 AskUserQuestion 确认 + 记录跳过原因
- **评审必须用户确认** — 禁止 AI 自审批
- **归档前门禁检查** — `/req done` 按需求类型(code/skill/ops)动态检查
- **归档前 AC 逐条验收(Gate 0,强制)** — `/req done` 必须先提取需求描述中的验收标准逐条确认,无 AC 或有未确认项均阻断,用户必须提供证据或说明跳过原因
- **归档前类型化门禁检查** — Gate 0 通过后,`/req done` 再按需求类型(code/skill/ops)动态检查
- **部署是项目级动作**,由 `/req deploy` 统一触发
- **需求完成后必须 git 提交并 push** — commit 格式:`feat(skill): REQ-XXXX 需求标题`
- **操作前先确认实际 ID** — 从 URL 提取 ID(如 `/requirements/897` → ID=897
@@ -393,6 +394,16 @@ Deploy Gate 3: 证据完整 ── 验收任务有文档,含检查项表格 +
9. `ai-proj req advance --id <id> --to released` 批量推进(仅 Gate 1-3 全部通过的需求)
**`/req done [REQ-ID]`** — 类型化归档门禁 + git commit + push + `ai-proj req archive --id <id>`
**Gate 0(强制):AC 验收逐条确认** — 在所有类型检查之前执行:
1. `ai-proj req get --id <id>` 获取需求描述
2. 提取 `## 验收标准` 章节下的所有 `- [ ]` / `- [x]` 条目
3. **无验收标准**AskUserQuestion 警告并要求用户确认为何无 AC,必须说明跳过原因才能继续
4. **有未勾选项(`- [ ]`**:逐条列出,AskUserQuestion 请用户对每条提供验证证据(截图/curl 输出/日志)或明确标记为 N/A + 说明原因
5. **全部已勾选(`- [x]`**:展示 AC 摘要,直接继续
6. AC 确认通过后,将每条 AC 的验证结果追加到需求描述(`ai-proj req update`),格式:`- [x] {原AC内容}(验证于 YYYY-MM-DD`
**类型检查(Gate 0 通过后)**
- **推断类型**:有 implementation → code;无 implementation 有 prd/test → skill;仅 deploy → ops
- **code 检查**delivery_stage=released + deploy 任务完成 + verification 任务完成(PDV 通过) + 部署文档 + 所有任务完成
- **skill 检查**delivery_stage≥testing + 所有任务完成
@@ -1,8 +1,11 @@
{
"name": "req-prd-plugin",
"description": "产品需求设计技能。PRD 文档编写、需求分析、用户故事、对比式分析。纯产品视角,不含技术实现。",
"version": "2.0.0",
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
"version": "2.2.0",
"author": {
"name": "qiudl"
}
},
"install_name": "req-prd",
"install_type": "skill",
"dir_category": "req"
}
+309 -11
View File
@@ -1,6 +1,6 @@
---
name: req-prd
description: 产品设计与需求管理。用于 PRD 文档编写、需求分析、用户故事创建、功能设计和原型规划。当用户提到产品设计、PRD、需求文档、功能规划、用户故事相关任务时自动激活
description: 产品设计与需求管理。用于 PRD、需求分析、用户故事、功能设计和原型规划,并将正式需求文档双写到本地仓库与 ai-proj Task Document
---
# 产品需求设计 Skill (req-prd)
@@ -16,7 +16,80 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
**插件扩展**
- `req-compare` — 对比式 PRD 编写(系统平移/竞品借鉴时激活)
- `req-prototype` — UI 原型生成
- `req-prototype` — UI 模块在 PRD/缺陷收敛后生成、上传并关联 HTML 原型
- `defect-analysis` — 设计访谈确认后,对最新版 PRD 反复审计和修订直至收敛
## 产品需求文档双写门禁
本技能创建或修改的所有正式需求文档都必须同时保存到:
1. 当前产品仓库的本地 Markdown 文件;
2. Requirement 对应角色任务的 ai-proj Task Document。
适用文档至少包括原始诉求/讨论记录、PRD、缺陷优化记录、原型版本与评审记录。Requirement description 只保存已确认的讨论摘要,不能替代 Task Document。
### 路径与任务映射
优先遵循仓库已有文档目录和命名约定;没有约定时使用:
| 文档 | 本地默认路径 | ai-proj 任务角色 |
|------|--------------|------------------|
| 需求讨论记录 | `docs/product/{REQ-ID}-{slug}-discussion.md` | `documentation` |
| PRD(含缺陷修订和原型回填) | `docs/product/{REQ-ID}-{slug}-prd.md` | `prd` |
同一 Requirement、同一角色只维护一个当前任务文档。不得把本地文件路径当成 ai-proj 持久化,也不得只把远程 Task Document 导出一次后继续单边修改。
### 每次写入协议
1. 修改前同时读取本地文件与 ai-proj Task Document;任一不存在则基于另一份初始化,二者都不存在才新建空模板;
2. 若两份内容不一致,比较文档 ID、版本、更新时间和内容摘要,保留双方未知内容并显式合并;无法安全合并时停止并请用户选择,禁止静默覆盖;
3. 先用安全文件编辑方式写入本地 Markdown,再创建或更新对应 Task Document
4. 写后重新读取两端,计算或比较内容摘要,确认正文一致,并记录本地路径、task_id、document_id、version/updated_at
5. 任一端写入或复读失败都属于未完成的部分写入:保留已成功一端用于恢复,立即报告并停止后续阶段,不得宣称“已保存”“已收敛”或提交评审。
问答每一轮、每次 PRD 修订、每轮 defect 处置和每次原型回填都执行该协议;不能等到流程结束再一次性补传。
## 模块设计访谈模式
设计模块、系统、跨域流程,或目标/边界/业务规则尚未确定时,必须先执行问答式设计访谈,不得直接补全假设后生成 PRD。用户明确要求“你问我答”时也进入此模式。范围小、规则已完整确认的需求可以直接编写 PRD。
进入此模式后,**完整读取并执行** [references/design-interview-and-defect-loop.md](references/design-interview-and-defect-loop.md)。该协议定义:
- 每轮只问一个会改变方案的关键问题;
- 将问题、AI 建议、用户原话、决策和未决项逐轮写入 ai-proj 需求的讨论文档;
- 讨论结论经用户确认后,才能创建或更新 PRD;
- 使用 `defect-analysis` 对最新版 PRD 执行“审计 → 修订 → 全量重审”循环;
- UI 模块在 PRD 收敛后使用 `req-prototype` 生成独立 HTML 原型、上传关联 Requirement、回填 PRD 并完成可访问性与关键状态校验;
- 原型评审改变产品行为时,回到问答、PRD 修订和 `defect-analysis` 全量重审,再生成新原型版本;
- 讨论文档缺失、写入失败、用户未确认、原型未验证,或仍有未处置的致命/高严重度缺陷时,不得宣称设计完成或提交评审。
## HTML 原型完成闸门
模块包含用户界面、用户操作流程或可视状态时,HTML 原型是产品设计交付物,不是评审后的可选补充。默认执行 `/req prototype upload [REQ-ID]`,具体生成、上传、iframe 回填和验证规则由 `req-prototype` 定义。
必须满足:
1. 原型基于最新版、已完成缺陷收敛的 PRD,并记录 PRD 文档标识、版本或内容摘要;
2. 覆盖核心入口、主流程以及 PRD 明确要求的空态、失败态、无权限态和确认/撤销反馈;
3. 将本地 HTML 源文件通过 ai-proj 上传到 OSS;上传后重新读取 Requirement,确认 OSS URL/版本已关联,并验证 URL 可访问、iframe 可展示、核心交互可操作;
4. 将 iframe、原型版本、版本说明和验证结果双写到本地 PRD 与 prd 角色 Task Document,并把生成、反馈、修订和确认双写到本地讨论记录与 documentation 任务文档;
5. 用户明确确认最终 PRD 与原型表达的是同一方案。
纯后端、批处理、基础设施等确实没有用户界面的模块可以跳过,但必须在讨论文档和 PRD `4.2` 中记录“无 UI,原型不适用”的理由及用户确认,不得静默省略。
## 客户原话原则(REQ-20260416-0017 P1-8
**编写 PRD 时必须包含「客户原始诉求」章节(模板 1.4),保留客户/业务方原话,不做 AI 加工。**
**为什么**
- 产品经理转述会失真(借鉴自 devflow-claude 的"客户场景"设计)
- 后续争议追溯时有据可查
- 团队成员看到原话能建立同理心
**填写规范**
- 原话用 Markdown `> 引用块` 包裹,区分于 AI 加工内容
- 标注提出人、时间、出处(会议/邮件/聊天)
- 特殊约束(时间/合规/预算)必须保留
---
@@ -50,6 +123,20 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|------|--------|--------|----------|
| ... | ... | ... | ... |
### 1.4 客户原始诉求 ⭐ 强制保留
> **重要**:记录客户/业务方提出需求时的**原始描述**,**不做 AI 加工、不总结、不转述**。
> 保留原话是为了后续溯源"我们当初为什么做这个"有据可查,避免产品经理转述失真。
- **场景1**(提出人:xxx / 时间:yyyy-mm-dd):
> "原始描述引用..."
- **场景2**
> "原始描述引用..."
**补充信息**(可选):
- 会议/邮件/聊天记录链接
- 客户特殊约束(如"必须在 Q2 前上线"
## 2. 用户分析
### 2.1 目标用户
[用户画像描述]
@@ -80,10 +167,21 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
### 4.2 界面原型
> 使用 `/req prototype [REQ-ID]` 基于 PRD 自动生成 Stitch 原型。
> 生成后截图将自动回填到此章节
> UI 模块使用 `/req prototype upload [REQ-ID]` 基于最新版 PRD 生成并上传 HTML 原型。
> 原型必须用 iframe 展示;Stitch 可作为视觉探索的可选输入,不能代替最终 HTML 原型闭环
[执行 `/req prototype` 后自动填充]
**原型基线**
- PRD 文档/版本:...
- 原型版本与说明:...
- Requirement 关联状态:已验证 | 未验证
- 可访问性/关键交互验证:...
<iframe src="[prototype_url]"
width="100%" height="600" frameborder="0"
style="border-radius:8px;border:1px solid #e5e7eb;">
</iframe>
[无 UI 模块填写:原型不适用的理由、讨论记录位置和用户确认原话]
## 5. 技术要求
### 5.1 性能要求
@@ -108,12 +206,49 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
### 6.2 灰度策略
[灰度发布计划]
## 7. 风险评估
## 7. 验收标准 ⭐ 强制包含 VP 三件套
> **规则(源自 REQ-20260421-0002**:每条 AC 必须附带 VP-Data / VP-Steps / VP-Pass,缺一项评审不通过。
### AC1: [验收条件标题]
**目标**[一句话描述期望结果]
**VP-Data(前置测试数据)**
- 环境:localhost / production(二选一,明确注明)
- 数据:[字段、值、状态,例如:需求状态=approved,关联任务 3 个,task_project_id 非空]
- 建数据方式:[curl localhost:8080/... 或 MCP 工具,禁止混用]
**VP-Steps(验证步骤)**
1. [工具 + 操作,例如:agent-browser open http://localhost:3000/xxx]
2. [检查指标,例如:eval `document.querySelector('.xxx').textContent`]
3. [确认值,例如:返回值包含"期望字符串"]
**VP-Pass(通过判定)**
- ✅ [具体期望值,例如:eval 返回数组长度 = 3]
- ✅ [第二个判定条件]
- ❌ [明确的不通过条件,例如:仅靠代码分析得出结论 = 不通过]
---
### AC2: [第二条验收条件]
**目标**...
**VP-Data**...
**VP-Steps**...
**VP-Pass**
- ✅ ...
- ❌ ...
## 8. 风险评估
| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|----------|
| ... | 高/中/低 | 高/中/低 | ... |
## 8. 附录
## 9. 附录
- 相关文档链接
- 参考资料
```
@@ -144,6 +279,81 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
---
## 需求粒度判断(REQ-20260416-0017 P1-12
**创建需求前,AI 必须先对标题做粒度预判。** 借鉴 devflow-claude `/req:split`
### 核心问题
> **"这个需求完成后,用户能感知到一个完整的功能变化吗?"**
> - 能 → 粒度合适
> - 不能(太大或太小)→ 需调整
### 粒度参考表
| 标题示例 | 粒度 | 建议 |
|---------|------|------|
| "用户积分系统"(含规则+查询+兑换+排行) | 太大 | 拆为 4 个需求 |
| "用户积分-积分规则管理"(含 CRUD+校验) | 合适 | 直接创建 |
| "用户积分-新增积分接口"(仅一个 API) | 太小 | 合并到功能级需求,或用任务(task) |
| "用户积分-新增 model 层"(按技术层拆) | 错误 | 按功能拆,不按技术层拆 |
### AI 自动检测规则
**标题过宽信号**(建议拆分):
- 含"系统"/"模块"/"平台"/"管理"等宏观词
- 描述中功能点 > 5 个
- 预估涉及文件 > 15 个
**标题过窄信号**(建议合并或改 task):
- 含"新增XX接口"/"修改XX字段"/"加一个按钮"
- 单个 CRUD 操作
- 预估涉及文件 ≤ 2 个
**错误拆分信号**(按技术层拆了):
- 标题含"model 层"/"service 层"/"handler 层"/"前端样式"
- 同一业务被拆为"后端接口"和"前端页面"两个独立需求
### 执行时机
1. **创建需求时**`/req new``create_requirement`):检查标题,给出建议
2. **编辑需求时**:功能清单超 8 项时提醒"是否应拆分"
3. **独立评估**:用 `/req split <标题>` 预判粒度
### 三种输出
1. **粒度合适** → 正常创建
2. **建议拆分** → 列出子功能建议,用户确认后批量创建
3. **建议改 task/QUICK** → 提示"这个用任务更合适"
### 已有需求扩展功能的决策
> **"去掉这个新功能点,原需求还能独立交付吗?"**
> - 能 → 新建需求
> - 不能 → 修改原需求(`/req edit`
| 场景 | 建议 |
|------|------|
| 新功能是原需求的自然延伸 | 修改原需求 |
| 新功能可独立上线 | 新建需求 |
| 原需求已完成/归档 | 必须新建 |
| 原需求开发中,新增会影响已有代码 | 新建(防范围蔓延) |
### 前后端拆分规则
```
✅ 正确:
REQ-001 用户积分规则管理-后端(含 CRUD 全部接口)
REQ-002 用户积分规则管理-前端(含 CRUD 全部页面)
❌ 错误:
REQ-001 用户积分规则-新增接口
REQ-002 用户积分规则-查询接口
REQ-003 用户积分规则-修改接口
```
---
## 用户故事编写
### 标准格式
@@ -221,6 +431,8 @@ mcp__ai-proj__link_tasks_to_requirement
### 文档管理
以下 MCP 操作只完成 ai-proj 侧写入;每次调用前后都必须按“产品需求文档双写门禁”同步并校验本地 Markdown。
```bash
# 创建 PRD 文档并关联任务
mcp__ai-proj__create-and-attach
@@ -238,6 +450,8 @@ mcp__ai-proj__export_task_document_to_file
- taskId: 任务ID
```
导出命令不能代替双写校验:导出后仍需确认目标路径符合仓库约定、正文与 Task Document 当前版本一致,且没有覆盖本地新增内容。
---
## 功能设计流程
@@ -255,6 +469,8 @@ mcp__ai-proj__export_task_document_to_file
- 需求池(ai-proj 需求列表)
```
若命中“模块设计访谈模式”,本阶段改为执行访谈协议并持续写入 ai-proj 讨论文档;访谈未确认前不进入 PRD 定稿。
### 2. 需求分析
```
@@ -279,10 +495,44 @@ mcp__ai-proj__export_task_document_to_file
输出:
- PRD 文档
- 原型设计
- 可生成原型的界面状态与交互规格
```
### 4. 评审验证
### 4. 缺陷收敛
```
输入:
- 已确认讨论结论
- 最新版完整 PRD
执行:
- defect-analysis 全维度审计
- 接受项修订 PRD
- 对修订后的完整 PRD 重新审计,直至一轮 0 个新缺陷
输出:
- 已收敛 PRD
- 缺陷处置记录
```
### 5. HTML 原型与反馈闭环
```
适用:
- 所有包含界面、用户操作或可视状态的模块
执行:
- 调用 req-prototype 的 upload 模式生成独立 HTML
- 上传并关联 Requirement
- iframe 回填 PRD,验证访问和关键交互
- 请用户评审;行为性反馈回到问答 → PRD → defect-analysis → 新原型版本
输出:
- 已验证、已关联的 HTML 原型
- PRD 与讨论文档中的版本/反馈/确认记录
```
### 6. 评审验证
```
评审维度:
@@ -296,6 +546,10 @@ mcp__ai-proj__export_task_document_to_file
- 修改意见
```
模块设计访谈模式下,本阶段必须调用 `defect-analysis`,并按访谈协议将每轮发现、处置、PRD 修订和收敛结论回写到同一讨论文档。
UI 模块还必须核对最终 HTML 原型与最新版 PRD 一致,并取得用户对二者的联合确认;无 UI 模块则核对已记录的不适用理由和用户确认。
---
## 竞品分析
@@ -340,10 +594,20 @@ mcp__ai-proj__export_task_document_to_file
### PRD 完整性检查
- [ ] 模块/系统设计已完成单轮单问访谈,且全过程已写入 ai-proj 讨论文档
- [ ] 讨论记录与 PRD 均已保存到仓库本地 Markdown 和对应 ai-proj Task Document
- [ ] 两端复读正文一致,交付说明包含本地路径、task/document 标识和远程版本
- [ ] 讨论结论已由用户明确确认
- [ ] `defect-analysis` 已基于最新版 PRD 收敛到一轮 0 个新缺陷
- [ ] 无未处置的致命/高严重度缺陷
- [ ] UI 模块 HTML 原型已生成、上传并关联 Requirement;无 UI 模块已记录不适用理由和用户确认
- [ ] 原型基线指向最新版 PRDPRD `4.2` 已回填 iframe、版本说明和验证结果
- [ ] 原型反馈导致的行为变更已回到问答、PRD 和 defect 全量重审,并生成新原型版本
- [ ] 用户已联合确认最终 PRD 与 HTML 原型
- [ ] 背景与目标明确
- [ ] 用户群体定义清晰
- [ ] 功能需求完整
- [ ] 验收标准可测试
- [ ] 验收标准可测试(每条 AC 附带 VP-Data / VP-Steps / VP-Pass
- [ ] 异常情况已考虑
- [ ] 性能要求已定义
- [ ] 上线计划合理
@@ -352,6 +616,8 @@ mcp__ai-proj__export_task_document_to_file
### 交互设计检查
- [ ] 用户流程完整
- [ ] HTML 原型覆盖核心入口、主流程及 PRD 指定的关键状态
- [ ] 原型 URL 可访问,Requirement 关联可读取,iframe 可展示,核心交互可操作
- [ ] 边界情况处理
- [ ] 错误提示友好
- [ ] 反馈及时
@@ -372,7 +638,8 @@ mcp__ai-proj__export_task_document_to_file
## 常用工具
### 原型设计
- **Stitch** (Google AI) — 集成在 `/req prototype`,自动从 PRD 生成原型
- **HTML upload(默认交付)** — `/req prototype upload` 生成可交互独立 HTML,上传后以 iframe 关联 Requirement 和 PRD
- **Stitch** (Google AI) — `/req prototype` 视觉探索与多屏草图,可作为 HTML 原型输入但不替代最终闭环
- Figma — 手动精细设计
- Sketch
- Axure
@@ -411,3 +678,34 @@ mcp__ai-proj__export_task_document_to_file
- 数据安全分级
- 敏感操作审计
- 权限最小化原则
---
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。**
### 禁止行为
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
### 允许行为
- memory 可影响**交互风格**(提问详略、确认节奏、语气)
- memory 可指导**命令选择**(如根据用户习惯推荐先走 req-compare 还是 req-prd
- memory 可影响**非产出文本**(如对话中的说明)
### Why
auto-memory 设计用于跨会话建立用户画像。但 PRD/需求文档是正式产出物,必须由**模板结构 + 当前输入**决定,不能因 memory 中的偏好自作主张调整结构,否则会导致:
- 模板章节漂移(用户不知道为什么这次少了一章)
- 历史项目内容污染(张冠李戴)
- 产出不可复现
### How to apply
执行 `/req prd` / PRD 编写 / 需求描述生成等命令时:
- 仅读取:模板文件、用户当前输入、引用的已有需求文档
- 不读取:memory 目录下的任何文件
- 产出前自检:章节数量和顺序与模板完全一致
**参考**devflow-claude 的 `plugins/req/commands/_common.md` 同名规则。
@@ -0,0 +1,256 @@
# 模块设计访谈、缺陷收敛与 HTML 原型闭环协议
本协议用于模块、系统、跨域流程等需要先澄清关键产品决策的设计任务。目标是让设计依据可追溯,让 PRD 在提交评审前经过可验证的缺陷收敛,并让 UI 模块通过可访问的 HTML 原型完成交互验证。
## 1. 进入与退出条件
满足任一条件时进入访谈模式:
- 用户明确要求“你问我答”、逐项讨论或产品访谈;
- 设计对象是模块、系统、跨域流程或涉及多个角色/组织;
- 目标、范围、数据归属、权限、状态流转、冲突优先级、异常策略中存在关键未决项。
需求范围小、上述决策均已明确时,可以直接编写 PRD。不要为了流程而重复询问用户已经回答的问题。
访谈模式只有同时满足以下条件才可结束:
1. 未决问题已清零,或明确列为非目标/后续项;
2. AI 已给出结构化讨论结论;
3. 用户明确确认讨论结论;
4. PRD 已按结论创建或更新;
5. `defect-analysis` 已对最新版 PRD 收敛;
6. UI 模块的 HTML 原型已生成、上传、关联、回填和验证;无 UI 模块已记录不适用理由并获得用户确认;
7. 用户联合确认收敛后的最终 PRD 与原型(或无 UI 的跳过结论)。
## 2. 讨论文档是跨轮次事实源
当当前工作已有 ai-proj Requirement 时,在提第一个问题前查找其已关联的讨论文档;没有时创建一个 documentation 角色的关联任务,并为任务创建文档:
- 任务标题:`【讨论】需求讨论: {需求标题}`
- 文档标题:`{REQ-ID} 需求讨论记录`
- 本地文件:优先使用仓库约定;默认 `docs/product/{REQ-ID}-{slug}-discussion.md`
- 一个 Requirement 只维护一个当前讨论文档,不因会话中断重复创建。
查找时同时核对 Requirement 关联关系、任务角色和标题,不能只按相似标题猜测。发现多个候选讨论文档时,列出标识和最近更新时间,请用户指定或授权合并;在此之前不得静默选择其中一个继续写入。
初始化文档时,先把触发本次设计的用户原始消息按时间和消息边界逐条写入“原始诉求”,再记录 `Q001`。不得只留下 AI 总结而丢失原始上下文。
“完整过程”指可供产品决策审计的模型可见内容:用户原话、AI 向用户展示的问题/建议/权衡、工具写入结果、确认、决策变更、PRD 修订和缺陷处置。不得记录或声称记录隐藏推理、系统提示、访问凭据及其他不可披露的内部信息。
若尚未指定 Requirement,先请用户提供已有 Requirement,或明确授权创建。取得 Requirement 和讨论文档前不得开始声称“已留痕”的正式访谈;不得仅为执行本技能本身擅自创建 Requirement。用户明确要求“创建需求并设计”才构成创建授权。
每次恢复会话时,先读取 Requirement、现有 PRD,以及讨论记录/PRD 的本地文件和 ai-proj Task Document,从最后一个未决问题继续。两端持久化内容必须一致;模型记忆不能覆盖文档中的用户原话和已确认决策。
### 本地与 ai-proj 双写
- 讨论记录和 PRD 的每次新增或修订,都必须同步到仓库本地 Markdown 与对应 ai-proj Task Document;本地文件用于代码库评审和版本控制,Task Document 用于需求关联、跨会话恢复和团队查看。
- 修改前读取两端。内容不一致时按版本、更新时间和摘要显式合并,保留双方未知内容;不能判断时停止并请用户选择,禁止以任一旧副本覆盖另一端。
- 每轮问答按顺序完成:更新本地讨论文件 → 更新 documentation 任务文档 → 复读两端并核对正文/摘要 → 再向用户提出下一问。
- 每次 PRD/defect/原型回填按同样协议更新本地 PRD 文件和 prd 任务文档。默认本地路径为 `docs/product/{REQ-ID}-{slug}-prd.md`,已有项目约定优先。
- 任一端失败时记录“部分写入”及成功端的路径/标识,停止后续流程。恢复时从成功端与失败前最后版本合并,禁止重复追加同一 Q/Round/Prototype 编号。
### 写入纪律
- 提问时先写入问题原文、问题意图和 AI 建议,再向用户提问。
- 收到回答后,先把用户原话和由此形成的决策写入,再提出下一问。
- 每轮使用稳定编号 `Q001``Q002`……;重试写入时复用编号,禁止重复追加同一轮。
- AI 提供推荐方案或选项时,为待确认内容写出明确编号或完整原文。用户仅回复“确认”“是”“前者”等短答案时,最终决策必须引用对应编号和被确认的完整内容,不能只记录孤立短词。
- 用户原话逐字保留在引用块中;AI 的解释、推论和建议必须分栏,不能伪装成用户决定。访问令牌、密码、私钥及依法需要保护的个人敏感信息不得落库,用 `[敏感信息已脱敏]` 替代并注明脱敏原因。
- 文档以追加式记录为主。状态为“待回答”的问题块可以在收到回答后原位补全一次;变为“已确认”后不得静默改写。纠正已确认结论时追加“决策变更”,并引用被替代的编号。
- 更新整篇文档前重新读取最新版并保留未知内容;若读取后文档又发生变化,基于最新版合并后重试,不能用旧副本覆盖其他会话的记录。
- 写入后读取本地文件和 ai-proj Task Document,确认本轮编号、正文和内容摘要一致。任一写入或校验失败时立即报告,停止进入下一轮,且不得声称“已记录”。
- Requirement 描述只同步用户确认后的“讨论结论”摘要;完整过程保留在讨论文档中。
## 3. 单轮单问访谈
每轮只问一个会实质改变产品方案的问题。优先按依赖关系覆盖以下决策面,而不是机械地逐项提问:
1. 用户问题、目标与成功指标;
2. 角色、主体和数据归属;
3. 范围、非目标及版本边界;
4. 实体关系与基数,例如一对一、一对多、多对多;
5. 权限来源、授权人和信任边界;
6. 创建、加入、变更、退出、撤销等状态与生命周期;
7. 多来源冲突时的优先级和人工覆盖规则;
8. 失败、超时、失联、重复请求和恢复策略;
9. 兼容、迁移、审计、数据隔离和验收方式。
问题应让用户做产品决策,不要求用户代替 AI 设计实现细节。用户让 AI 建议时,先给出一个推荐方案和主要权衡,再请用户确认或修正。若回答引入新的实体、状态或例外规则,沿其影响继续追问;若答案已能从用户原话或现有文档确定,则直接记录,不重复确认。
每轮记录以下内容:
```markdown
### Q001 · {决策主题}
- 状态:待回答 | 已确认 | 已替代
- AI 问题(原文):...
- 提问意图:这个答案会影响哪些设计部分
- AI 建议与权衡:推荐方案、替代方案及主要代价
- 待确认内容:方案/选项编号及完整表述
- 用户回答(原文):
> ...
- 最终决策:只写由用户回答直接支持的结论
- 影响范围:PRD 章节、实体、流程、权限或验收标准
- 未决项:无 | 下一步待确认内容
- 记录时间:ISO 8601 时间
```
## 4. 方案确认闸门
问题收敛后,在讨论文档追加“当前决策快照”,至少包含:
- 目标与成功条件;
- 用户/角色及核心场景;
- 范围与非目标;
- 核心实体、关系和数据归属;
- 权限、状态流转和冲突规则;
- 异常、降级、撤销和审计;
- 版本边界与后续项;
- 可验证的验收标准草案;
- 尚存风险和假设。
然后向用户展示同一份摘要并询问是否确认。只有用户明确表示确认,才能:
1. 将摘要同步到 Requirement 描述的 `## 讨论结论`
2. 创建或更新 PRD
3. 进入缺陷收敛循环。
每一步完成后重新读取目标对象确认写入成功。讨论结论未同步成功时不得开始写 PRD;PRD 未写入或读取到的内容与本次版本不一致时不得开始缺陷审计。
若用户修改任何结论,记录为新的问答或“决策变更”,更新快照后重新确认。
## 5. PRD 与 defect-analysis 收敛循环
讨论确认后,先按 `req-prd` 的完整模板生成或更新 PRD,再完整读取并调用 `defect-analysis`。每次审计都以**当前最新版完整 PRD**、已确认讨论记录和必要的真实代码/数据契约为输入,不能只审查上轮改动片段。首次基线审计必须覆盖 `defect-analysis` 中所有适用的架构、运行时、数据和体验/维护维度;不能因为尚未覆盖其他维度时某个单独维度为 0 个新缺陷而提前结束基线。
循环执行:
1. `defect-analysis` 检查最新版 PRD,并按其规则输出带严重度和轮次的缺陷;
2. 将本轮输入版本、检查维度、完整发现和证据写入讨论文档;PRD 输入版本至少包含文档/任务标识、更新时间和内容摘要或哈希,避免审计结果关联到错误版本;审计轮使用稳定编号 `Round 001``Round 002`……,重试不得重复计轮;
3. 对每个发现标记处置:接受、误报、延后;
4. 接受的产品缺陷必须修订 PRD,并同步影响到验收标准、风险、非目标或版本边界;纯技术实现发现若不改变产品行为,记录为后续 `req-design` 约束或开发风险,不向 PRD 填入未经验证的实现细节;
5. 误报必须记录反证;延后必须记录原因、风险、负责人或后续需求,不得静默忽略;
6. 记录 PRD 修改摘要和仍未解决的问题,重新读取 PRD 确认修订已经持久化;
7. 对修改后的完整 PRD 重新调用 `defect-analysis`
若某个修复会改变已确认的目标、范围、实体关系、权限、用户流程、冲突规则或验收口径,不能由 AI 静默应用。将它追加为新的问答或“决策变更”,说明缺陷证据、推荐方案和代价,获得用户确认并更新决策快照后,再修订 PRD;随后重新开始最新版 PRD 的全量审计。
完成全维度基线后,只有 `defect-analysis` 对最新版完整 PRD 出现一轮“0 个新缺陷”时才能标记 PRD 收敛。达到 20 轮仍有新发现只是阶段复盘点:汇总剩余风险并请求用户决定是否继续;不得把“达到轮数”写成“已收敛”。用户已明确要求持续审计时,按该技能规则继续下一阶段。
存在以下任一情况时,不得提交 PRD 评审或宣称完成:
- 未处置的致命或高严重度缺陷;
- 讨论文档缺失或有未成功写入的轮次;
- PRD 与已确认决策不一致;
- 缺少 0 新增缺陷的收敛轮;
- UI 模块缺少已验证并关联的最终 HTML 原型,或原型与最新版 PRD 不一致;
- 无 UI 模块没有记录跳过理由及用户确认;
- 用户尚未联合确认收敛后的最终 PRD 与原型(或跳过结论)。
## 6. HTML 原型闭环
### 6.1 适用性判断
PRD 包含页面、表单、列表、可视状态、用户操作或跨页面流程时,必须执行 `req-prototype` 的 HTML upload 模式。Stitch 截图或其他静态图片可以辅助探索,但不能替代可交互 HTML、Requirement 关联和 iframe 回填。
纯后端、批处理、基础设施等无用户界面的模块可以跳过。跳过前必须把理由、影响范围和待确认内容写入讨论文档,取得用户明确确认,并在 PRD `4.2 界面原型` 留下“不适用”记录。
### 6.2 生成基线与覆盖范围
1. 重新读取最新版 PRD,记录任务/文档标识、更新时间、版本和内容摘要或哈希;
2. 从功能需求、交互设计和验收标准提取页面清单、角色入口、主流程与关键状态;
3. 调用 `req-prototype` 生成独立 HTML。至少覆盖核心入口、主流程,以及 PRD 明确要求的空态、加载态、失败态、无权限态、确认和撤销反馈;
4. 原型不得引入 PRD 未确认的新权限、状态、自动化规则或默认值。为了连贯展示所作的推断必须显式标注为待确认,不能伪装成既定需求。
### 6.3 上传、关联、回填与验证
1. 将 HTML 源文件保存到仓库约定目录(默认 `docs/prototypes/`),再通过 `upload_prototype` 上传到 ai-proj 配置的 OSS,并记录 Requirement 数字 ID、OSS URL、版本说明和上传时间;
2. 重新读取 Requirement,确认返回的 OSS URL/版本确实已关联。仅有本地文件或上传成功响应不足以通过;
3. 将 iframe、PRD 基线、原型版本、版本说明和验证结果同时回填本地 PRD 与 prd 角色 Task Document
4. 用浏览器或等价方式验证 URL 可访问、iframe 可展示、核心导航和交互可操作、关键状态可识别。使用临时浏览器时按环境规则关闭;
5. 将生成输入、上传结果、验证证据和待确认差异写入讨论文档。任何写入或验证失败都必须停止,不得声称原型已完成。
### 6.4 用户评审与回流
向用户展示最终关联的原型,并请其同时检查信息结构、流程、状态、权限提示和关键文案:
- 仅视觉样式、间距、颜色等不改变产品行为的反馈,可以直接生成新原型版本,并记录修改摘要;
- 反馈改变目标、范围、实体关系、权限、状态、流程、异常策略、默认值或验收口径时,追加新的问答/决策变更,更新决策快照和 PRD,重新执行完整 `defect-analysis`,收敛后再生成新 HTML 原型版本;
- 每个新版本都必须重新执行关联、PRD 回填和可访问性/交互验证,不得覆盖或伪造历史版本;
- 只有用户明确确认“最终 PRD 与当前原型一致”后,模块产品设计才可结束。
## 7. 讨论文档结构
```markdown
# {REQ-ID} 需求讨论记录
## 元数据
- Requirement...
- 状态:访谈中 | 待方案确认 | PRD 优化中 | 原型制作中 | 待最终确认 | 已收敛
- 最新 PRD:任务/文档标识
- 更新时间:...
## 原始诉求
> 用户原话,按时间追加
## 问答记录
### Q001 · ...
...
## 决策变更
### D001 · 替代 Qxxx 的结论
...
## 当前决策快照
...
## 未决问题
- ...
## 方案确认
- 用户确认原话:...
- 确认时间:...
## PRD / 缺陷优化记录
### Round 1 · {检查维度}
- PRD 版本/摘要:...
- 新缺陷:...
- 处置与证据:...
- PRD 修订:...
- 剩余风险:...
## HTML 原型记录
### Prototype v1 · {版本说明}
- PRD 基线:任务/文档标识、版本、更新时间、摘要或哈希
- 是否适用:是 | 否(理由与用户确认)
- 原型 URL...
- Requirement 关联校验:...
- iframe / 可访问性 / 关键交互验证:...
- 用户反馈:...
- 行为性变更回流:无 | 对应 Q/D、PRD 版本和 defect 轮次
- 状态:待验证 | 待用户确认 | 已替代 | 已确认
## 收敛结论
- 收敛轮次:...
- 0 新增缺陷证据:...
- 最终原型版本/URL:... | 无 UI,不适用(确认记录:...)
- PRD 与原型一致性确认:...
- 未解决的中/低风险及接受理由:...
- 用户最终确认原话:...
```
## 8. 最终交付说明
最终回复必须同时给出:
- ai-proj Requirement 标识;
- 讨论任务/文档标识;
- 讨论记录本地路径及双写一致性状态;
- PRD 任务/文档标识;
- PRD 本地路径及双写一致性状态;
- 问答轮数、缺陷审计轮数和收敛轮;
- HTML 原型版本、URL、Requirement 关联与验证状态;无 UI 时给出跳过理由和用户确认;
- 仍被接受的中/低风险;
- 用户两次确认:讨论方案确认、最终 PRD 与原型(或无 UI 结论)的联合确认。
任何标识或写入状态无法验证时,用“未验证/未写入”如实标注。
@@ -1,8 +1,11 @@
{
"name": "req-prototype-plugin",
"description": "Stitch 原型生成与迭代。基于 PRD 文档自动生成 UI 原型。",
"version": "1.0.0",
"description": "原型生成与关联。支持 HTML 本地留源、OSS 正式交付、Requirement/iframe 验证及 Stitch AI。",
"version": "2.2.0",
"author": {
"name": "qiudl"
}
},
"install_name": "req-prototype",
"install_type": "skill",
"dir_category": "req"
}

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