Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a58dd1aff3 | ||
|
|
bb5e6be73e | ||
|
|
ef0e9ca1f0 | ||
|
|
ad4e2b16a8 | ||
|
|
ba107a54a8 | ||
|
|
4ea6920a13 | ||
|
|
039a749e3e | ||
|
|
daca19a262 | ||
|
|
ddb5d5cb06 | ||
|
|
da57cccf96 | ||
|
|
7eed2b8f10 | ||
|
|
5a45916b2c |
+42
-118
@@ -95,6 +95,32 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"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",
|
"name": "db-migration-plugin",
|
||||||
"source": "./skills-dev/db-migration-plugin",
|
"source": "./skills-dev/db-migration-plugin",
|
||||||
@@ -111,7 +137,7 @@
|
|||||||
"name": "defect-analysis-plugin",
|
"name": "defect-analysis-plugin",
|
||||||
"source": "./skills-dev/defect-analysis-plugin",
|
"source": "./skills-dev/defect-analysis-plugin",
|
||||||
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"utility",
|
"utility",
|
||||||
@@ -329,7 +355,7 @@
|
|||||||
"name": "frontend-design-plugin",
|
"name": "frontend-design-plugin",
|
||||||
"source": "./skills-dev/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.",
|
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"category": "development",
|
"category": "development",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"development",
|
"development",
|
||||||
@@ -338,6 +364,18 @@
|
|||||||
],
|
],
|
||||||
"strict": false
|
"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",
|
"name": "pull-request-plugin",
|
||||||
"source": "./skills-dev/pull-request-plugin",
|
"source": "./skills-dev/pull-request-plugin",
|
||||||
@@ -355,7 +393,7 @@
|
|||||||
"name": "review-checklist-plugin",
|
"name": "review-checklist-plugin",
|
||||||
"source": "./skills-dev/review-checklist-plugin",
|
"source": "./skills-dev/review-checklist-plugin",
|
||||||
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
|
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"utility",
|
"utility",
|
||||||
@@ -548,7 +586,7 @@
|
|||||||
"name": "doubao-voice-plugin",
|
"name": "doubao-voice-plugin",
|
||||||
"source": "./skills-integration/doubao-voice-plugin",
|
"source": "./skills-integration/doubao-voice-plugin",
|
||||||
"description": "Doubao (豆包) Voice API integration for TTS and ASR",
|
"description": "Doubao (豆包) Voice API integration for TTS and ASR",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"category": "utility",
|
"category": "utility",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"utility",
|
"utility",
|
||||||
@@ -684,120 +722,6 @@
|
|||||||
"tools"
|
"tools"
|
||||||
],
|
],
|
||||||
"strict": false
|
"strict": false
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gitea-plugin",
|
|
||||||
"source": "./skills-personal/gitea-plugin",
|
|
||||||
"description": "Gitea 代码托管与 CI/CD 管理。用于 Gitea Actions workflow 管理、Runner 管理、PR 操作、仓库配置。",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "utility",
|
|
||||||
"keywords": [
|
|
||||||
"utility",
|
|
||||||
"tools"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "openclaw-plugin",
|
|
||||||
"source": "./skills-personal/openclaw-plugin",
|
|
||||||
"description": "OpenClaw (龙虾) 远程 AI 计算调度系统 - 概念设计与运维管理",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "utility",
|
|
||||||
"keywords": [
|
|
||||||
"utility",
|
|
||||||
"tools"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ops-servers-plugin",
|
|
||||||
"source": "./skills-personal/ops-servers-plugin",
|
|
||||||
"description": "企业服务器管理。用于云服务器分组管理、系统监控、备份管理、故障排查。当用户提到云服务器、生产环境、腾讯云、阿里云相关任务时自动激活。",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "devops",
|
|
||||||
"keywords": [
|
|
||||||
"devops",
|
|
||||||
"deployment",
|
|
||||||
"operations"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ops-tools-plugin",
|
|
||||||
"source": "./skills-personal/ops-tools-plugin",
|
|
||||||
"description": "Plugin for ops-tools",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "devops",
|
|
||||||
"keywords": [
|
|
||||||
"devops",
|
|
||||||
"deployment",
|
|
||||||
"operations"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "qiudl-personal-plugin",
|
|
||||||
"source": "./skills-personal/qiudl-personal-plugin",
|
|
||||||
"description": "Plugin for qiudl-personal",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "utility",
|
|
||||||
"keywords": [
|
|
||||||
"utility",
|
|
||||||
"tools"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "reload-session-plugin",
|
|
||||||
"source": "./skills-personal/reload-session-plugin",
|
|
||||||
"description": "Reload a previously saved Claude session to continue the conversation.",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "workflow",
|
|
||||||
"keywords": [
|
|
||||||
"session",
|
|
||||||
"workflow",
|
|
||||||
"productivity"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "req-deploy-plugin",
|
|
||||||
"source": "./skills-personal/req-deploy-plugin",
|
|
||||||
"description": "Plugin for req-deploy",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "devops",
|
|
||||||
"keywords": [
|
|
||||||
"devops",
|
|
||||||
"deployment",
|
|
||||||
"operations"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "save-session-plugin",
|
|
||||||
"source": "./skills-personal/save-session-plugin",
|
|
||||||
"description": "Auto-save Claude session conversation with AI-generated title, summary, and tags in searchable JSON format.",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "workflow",
|
|
||||||
"keywords": [
|
|
||||||
"session",
|
|
||||||
"workflow",
|
|
||||||
"productivity"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "search-sessions-plugin",
|
|
||||||
"source": "./skills-personal/search-sessions-plugin",
|
|
||||||
"description": "Search saved Claude sessions by title, tags, date, or content.",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"category": "workflow",
|
|
||||||
"keywords": [
|
|
||||||
"session",
|
|
||||||
"workflow",
|
|
||||||
"productivity"
|
|
||||||
],
|
|
||||||
"strict": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
# ai-proj-helper
|
# ai-proj-helper
|
||||||
|
|
||||||
Claude Code 技能市场 + MCP 配置管理工具。
|
Codex 优先、兼容 Claude Code 的 Agent Skills 市场与 MCP 配置管理工具。
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./init.sh
|
./install-skills.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
交互式配置 MCP 连接(默认 SSE 模式)+ 自动注册技能市场到 `~/.claude/plugins/known_marketplaces.json`。支持命令行参数:
|
默认安装到 Codex 标准目录 `~/.agents/skills`。Claude Code 的 MCP 与 marketplace 初始化使用:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./init.sh --mode sse --token aiproj_pk_xxx
|
./init.sh --mode sse --token aiproj_pk_xxx
|
||||||
@@ -18,11 +18,11 @@ Claude Code 技能市场 + MCP 配置管理工具。
|
|||||||
|
|
||||||
```
|
```
|
||||||
ai-proj-helper/
|
ai-proj-helper/
|
||||||
├── skills-core/ # 基础设施 (1): ai-proj
|
├── skills-core/ # 基础设施技能
|
||||||
├── skills-dev/ # 开发 (4): dev-arch, dev-coding, dev-test, pull-request
|
├── skills-dev/ # 开发与发布技能
|
||||||
├── skills-req/ # 需求 (4): req, req-prd, req-dev, req-test-gate
|
├── skills-req/ # 需求管理技能
|
||||||
├── skills-integration/ # 集成 (8): feishu, feishu-bitable, feishu-docx, wecom, siyuan, siyuan-to-feishu, data-excel, doubao-voice
|
├── skills-integration/ # 第三方集成技能
|
||||||
├── skills-biz/ # 商务 (4): biz-contract, biz-ops, biz-plan, finance
|
├── skills-biz/ # 商务技能
|
||||||
├── skills-personal/ # 个人(.gitignore 排除)
|
├── skills-personal/ # 个人(.gitignore 排除)
|
||||||
├── claude-config.yaml # 技能启用/禁用 + MCP 配置
|
├── claude-config.yaml # 技能启用/禁用 + MCP 配置
|
||||||
├── init.sh # MCP 初始化
|
├── init.sh # MCP 初始化
|
||||||
@@ -51,4 +51,4 @@ skills:
|
|||||||
|
|
||||||
- `skills-personal/` 不被 Git 跟踪,用于存放个人配置和工具
|
- `skills-personal/` 不被 Git 跟踪,用于存放个人配置和工具
|
||||||
- 其余 `skills-*` 目录均由 Git 版本控制
|
- 其余 `skills-*` 目录均由 Git 版本控制
|
||||||
- 所有目录都会被 `generate-marketplace.py` 自动扫描并加入 marketplace.json
|
- 只有受 Git 跟踪的分类目录会被 `generate-marketplace.py` 扫描并加入公开 marketplace;`skills-personal/` 始终排除
|
||||||
|
|||||||
@@ -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`)
|
- **ai-proj 账号 + MCP API Key**:联系管理员获取(Key 格式: `aiproj_pk_xxx`)
|
||||||
|
|
||||||
### 一键部署(2 步搞定)
|
### 一键部署(2 步搞定)
|
||||||
@@ -76,17 +76,16 @@ PRD 文档存储在思源笔记中,可以导出发送到飞书群,通过飞
|
|||||||
git clone https://gitea.pipexerp.com/pipexerp/ai-proj-helper.git
|
git clone https://gitea.pipexerp.com/pipexerp/ai-proj-helper.git
|
||||||
cd ai-proj-helper
|
cd ai-proj-helper
|
||||||
|
|
||||||
# 2. 运行初始化(按提示输入 API Key 即可)
|
# 2. 默认安装到 Codex 的用户级标准目录 ~/.agents/skills
|
||||||
./init.sh
|
./install-skills.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
脚本会自动完成:
|
安装器会复制完整技能目录,包括 `SKILL.md`、references、scripts 和 assets。Codex 会自动发现 `~/.agents/skills` 中的技能;若没有出现,重启 Codex。
|
||||||
- 配置 MCP 服务器连接(`~/.claude/.mcp.json`)
|
|
||||||
- 注册技能市场到 Claude Code(`~/.claude/plugins/known_marketplaces.json`)
|
|
||||||
|
|
||||||
也支持命令行参数跳过交互:
|
Claude Code 用户显式选择 Claude 目标;需要同时配置 MCP 和 marketplace 时运行 `init.sh`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
./install-skills.sh --agent claude
|
||||||
./init.sh --mode sse --token aiproj_pk_xxx
|
./init.sh --mode sse --token aiproj_pk_xxx
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -163,7 +162,7 @@ skills:
|
|||||||
|
|
||||||
- **mode**: MCP 连接模式。`sse` 直连远程服务器(推荐),`stdio` 在本地启动 Node.js 进程
|
- **mode**: MCP 连接模式。`sse` 直连远程服务器(推荐),`stdio` 在本地启动 Node.js 进程
|
||||||
- **disabled**: 不需要的技能可以加到这里,重新运行 `./init.sh` 生效
|
- **disabled**: 不需要的技能可以加到这里,重新运行 `./init.sh` 生效
|
||||||
- **personal_dir**: 个人技能目录,默认不被 Git 跟踪
|
- **personal_dir**: 本机个人技能目录,默认不被 Git 跟踪,也不会写入公开 marketplace
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
@@ -171,13 +170,17 @@ skills:
|
|||||||
|
|
||||||
A: 需要先联系管理员获取 MCP API Key(格式 `aiproj_pk_xxx`),然后在提示处输入。
|
A: 需要先联系管理员获取 MCP API Key(格式 `aiproj_pk_xxx`),然后在提示处输入。
|
||||||
|
|
||||||
|
**Q: 安装后 Codex 没有识别到技能?**
|
||||||
|
|
||||||
|
A: 确认技能位于 `~/.agents/skills/<name>/SKILL.md`,然后重启 Codex。Codex CLI 也可用 `/skills` 查看。
|
||||||
|
|
||||||
**Q: 安装后 Claude Code 没有识别到技能?**
|
**Q: 安装后 Claude Code 没有识别到技能?**
|
||||||
|
|
||||||
A: 重启 Claude Code 后生效。如果仍不生效,检查 `~/.claude/plugins/known_marketplaces.json` 中是否包含 `ai-proj-helper` 条目。
|
A: 重启 Claude Code 后生效。如果仍不生效,检查 `~/.claude/plugins/known_marketplaces.json` 中是否包含 `ai-proj-helper` 条目。
|
||||||
|
|
||||||
**Q: 如何更新到最新版本?**
|
**Q: 如何更新到最新版本?**
|
||||||
|
|
||||||
A: 进入项目目录执行 `git pull`,然后重新运行 `./init.sh`。
|
A: 进入项目目录执行 `git pull`,然后运行 `./install-skills.sh`;Claude Code 用户增加 `--agent claude`。
|
||||||
|
|
||||||
**Q: 如何禁用不需要的技能?**
|
**Q: 如何禁用不需要的技能?**
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
## 1. Clone the Gitea Repository
|
||||||
- Name: `claude-marketplace`
|
|
||||||
- Visibility: Private or Public (your choice)
|
|
||||||
- **Do NOT** initialize with README (we already have one)
|
|
||||||
|
|
||||||
## 2. Push to Gitea
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Users/junhuang/coolbuy/claude-marketplace
|
git clone https://gitea.pipexerp.com/pipexerp/ai-proj-helper.git
|
||||||
git push -u origin main
|
cd ai-proj-helper
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. Test Installation
|
## 2. Test Installation
|
||||||
|
|
||||||
### Add the marketplace
|
### Add the marketplace
|
||||||
```bash
|
```bash
|
||||||
# SSH (recommended)
|
# SSH
|
||||||
/plugin marketplace add git@gitea.pipexerp.com:huangjun/claude-marketplace.git
|
/plugin marketplace add ssh://git@gitea.pipexerp.com:10022/pipexerp/ai-proj-helper.git
|
||||||
|
|
||||||
# OR HTTPS (requires credential configuration)
|
# 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
|
### List available plugins
|
||||||
@@ -42,12 +37,12 @@ git push -u origin main
|
|||||||
# Check for your installed plugins
|
# Check for your installed plugins
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4. Update Plugins Later
|
## 3. Update Plugins Later
|
||||||
|
|
||||||
When you make changes and push updates:
|
When you make changes and push updates:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Users/junhuang/coolbuy/claude-marketplace
|
cd /path/to/ai-proj-helper
|
||||||
|
|
||||||
# Make changes to plugins
|
# Make changes to plugins
|
||||||
# ...
|
# ...
|
||||||
@@ -67,7 +62,7 @@ Users update with:
|
|||||||
/plugin update ai-proj-plugin@coolbuy-claude-plugins
|
/plugin update ai-proj-plugin@coolbuy-claude-plugins
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5. Private Repository Setup
|
## 4. Repository Authentication
|
||||||
|
|
||||||
If your Gitea repo is private, users need 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
|
3. Give it "Read repository" permissions
|
||||||
4. Copy the token and add to your environment
|
4. Copy the token and add to your environment
|
||||||
|
|
||||||
## 6. Structure Overview
|
## 5. Structure Overview
|
||||||
|
|
||||||
```
|
```
|
||||||
claude-marketplace/
|
ai-proj-helper/
|
||||||
├── .claude-plugin/
|
├── .claude-plugin/
|
||||||
│ └── marketplace.json # Catalog of all plugins
|
│ └── marketplace.json # Catalog of all plugins
|
||||||
├── plugins/
|
├── skills-core/ # Core plugins
|
||||||
│ ├── ai-proj-plugin/
|
├── skills-dev/ # Development and release plugins
|
||||||
│ │ ├── .claude-plugin/
|
├── skills-req/ # Requirement plugins
|
||||||
│ │ │ └── plugin.json # Plugin metadata
|
├── skills-integration/ # Integration plugins
|
||||||
│ │ └── skills/
|
├── skills-biz/ # Business plugins
|
||||||
│ │ └── SKILL.md # Skill definition
|
|
||||||
│ └── [33 more plugins...]
|
|
||||||
├── README.md # User documentation
|
├── README.md # User documentation
|
||||||
├── SETUP.md # This file
|
├── SETUP.md # This file
|
||||||
└── convert-skills.sh # Conversion script (reference)
|
├── generate-marketplace.py # Marketplace generator
|
||||||
|
└── install-skills.sh # Versioned local installer
|
||||||
```
|
```
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
1. ✅ Push to Gitea: `git push -u origin main`
|
1. ✅ Test locally: `/plugin marketplace add <url>`
|
||||||
2. ✅ Test locally: `/plugin marketplace add <url>`
|
2. ✅ Install plugins: `/plugin install <name>@coolbuy-claude-plugins`
|
||||||
3. ✅ Install plugins: `/plugin install <name>@coolbuy-claude-plugins`
|
3. ✅ Share with team: Send them the repository URL
|
||||||
4. ✅ Share with team: Send them the repository URL
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
@@ -123,7 +116,7 @@ claude-marketplace/
|
|||||||
- Check plugin name is correct
|
- Check plugin name is correct
|
||||||
- Ensure marketplace.json is valid: `cat .claude-plugin/marketplace.json | jq`
|
- 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)
|
- Skills are Agent Skills (auto-invoked by Claude when relevant)
|
||||||
- They don't create slash commands
|
- They don't create slash commands
|
||||||
- Check plugin installation: `/plugin list`
|
- Check plugin installation: `/plugin list`
|
||||||
|
|||||||
+40
-189
@@ -1,218 +1,69 @@
|
|||||||
# Skill Sync Guide
|
# 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
|
```bash
|
||||||
cd /path/to/claude-marketplace
|
git pull
|
||||||
./sync-skills.sh
|
./install-skills.sh --dry-run
|
||||||
|
./install-skills.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
This will:
|
Claude Code 需要显式选择目标:
|
||||||
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
|
```bash
|
||||||
code ~/.claude/skills/my-skill/SKILL.md
|
./install-skills.sh --agent claude
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Run Sync Script
|
安装器会复制完整技能目录,包括 `SKILL.md`、`references/`、`scripts/` 和 `assets/`。它用内容摘要区分仓库升级和本地修改:
|
||||||
|
|
||||||
|
- 目标未修改时,版本升级会自动安装。
|
||||||
|
- 旧版只安装了 `SKILL.md` 时,会安全补齐仓库中的其他同源文件。
|
||||||
|
- 目标存在本地修改时会跳过;确认覆盖后才使用 `--force`。
|
||||||
|
- `--cleanup` 会删除状态文件记录中已从仓库移除的技能,使用前先运行 `--dry-run --cleanup`。
|
||||||
|
|
||||||
|
按分类安装或查看清单:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/path/to/claude-marketplace
|
./install-skills.sh --list
|
||||||
./sync-skills.sh
|
./install-skills.sh --category dev
|
||||||
|
./install-skills.sh --exclude ai-proj-cicd-release
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Review Changes
|
## 将本机技能发布到仓库
|
||||||
|
|
||||||
|
不要批量复制整个 `~/.agents/skills/` 或其他 Agent 的安装目录。系统技能、第三方托管技能、包含机器路径或凭据的技能不应发布。
|
||||||
|
|
||||||
|
1. 选择确实属于本仓库、可供团队复用的技能。
|
||||||
|
2. 在对应 `skills-*/<name>-plugin/` 下放置 `.claude-plugin/plugin.json` 和完整 `skills/` 目录。
|
||||||
|
3. 清除用户名、绝对路径、内网地址、密钥标识和历史凭据;把环境差异改为从仓库配置解析。
|
||||||
|
4. 更新插件版本并运行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git status
|
|
||||||
git diff
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Commit & Push
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add .
|
|
||||||
git commit -m "Update skill: description of changes"
|
|
||||||
git push
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Team Updates
|
|
||||||
|
|
||||||
Team members update with:
|
|
||||||
```bash
|
|
||||||
/plugin marketplace update coolbuy-claude-plugins
|
|
||||||
/plugin update <plugin-name>@coolbuy-claude-plugins
|
|
||||||
```
|
|
||||||
|
|
||||||
## Automated Sync (Optional)
|
|
||||||
|
|
||||||
### Git Hook (Pre-commit)
|
|
||||||
|
|
||||||
Auto-sync when committing changes to skills:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In your dotfiles/skills repo
|
|
||||||
cat > .git/hooks/pre-commit << 'EOF'
|
|
||||||
#!/bin/bash
|
|
||||||
# Auto-sync skills to marketplace
|
|
||||||
~/path/to/claude-marketplace/sync-skills.sh
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x .git/hooks/pre-commit
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cron Job (Scheduled)
|
|
||||||
|
|
||||||
Sync daily at 9 AM:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
crontab -e
|
|
||||||
|
|
||||||
# Add this line:
|
|
||||||
0 9 * * * cd ~/path/to/claude-marketplace && ./sync-skills.sh && git add . && git commit -m "Daily sync" && git push
|
|
||||||
```
|
|
||||||
|
|
||||||
## Skill Splitting Guidelines
|
|
||||||
|
|
||||||
From `~/.claude/CLAUDE.md`:
|
|
||||||
|
|
||||||
- **Token Limit**: Single skill ≤ 10,000 tokens
|
|
||||||
- **Check Size**: `wc -w ~/.claude/skills/<skill>/SKILL.md`
|
|
||||||
- **When to Split**: If > 7,500 words (≈10,000 tokens)
|
|
||||||
|
|
||||||
### Split Strategy
|
|
||||||
|
|
||||||
When a skill grows too large:
|
|
||||||
|
|
||||||
1. **Entry Skill** - Overview + command routing (<100 lines)
|
|
||||||
- Example: `req/SKILL.md`
|
|
||||||
|
|
||||||
2. **Command Reference** - Detailed commands (<200 lines)
|
|
||||||
- Example: `req-commands/SKILL.md`
|
|
||||||
|
|
||||||
3. **Workflow Guide** - Complete processes (<200 lines)
|
|
||||||
- Example: `req-workflow/SKILL.md`
|
|
||||||
|
|
||||||
4. **Methodology** - Complex concepts (<150 lines)
|
|
||||||
- Example: `req-review/SKILL.md`
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Sync Script Fails
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check permissions
|
|
||||||
ls -la sync-skills.sh
|
|
||||||
|
|
||||||
# Make executable
|
|
||||||
chmod +x sync-skills.sh
|
|
||||||
|
|
||||||
# Check paths
|
|
||||||
echo $HOME/.claude/skills
|
|
||||||
```
|
|
||||||
|
|
||||||
### marketplace.json Not Updated
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Manually regenerate
|
|
||||||
python3 generate-marketplace.py
|
python3 generate-marketplace.py
|
||||||
|
claude plugin validate .
|
||||||
# Or edit directly
|
git diff --check
|
||||||
code .claude-plugin/marketplace.json
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Git Conflicts
|
5. 审核变更后通过分支和 PR 发布。
|
||||||
|
|
||||||
```bash
|
## 本地个人技能
|
||||||
# Discard local changes
|
|
||||||
git checkout .claude-plugin/marketplace.json
|
|
||||||
|
|
||||||
# Or merge manually
|
`skills-personal/` 受 `.gitignore` 保护,仅供当前机器使用。生成器明确排除此目录,避免
|
||||||
git mergetool
|
`marketplace.json` 引用公开克隆中不存在的文件。若个人技能要转为团队技能,应先按上面的发布流程完成脱敏和审核。
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
## 常见问题
|
||||||
|
|
||||||
### 1. Descriptive Frontmatter
|
**本地修改被跳过怎么办?**
|
||||||
|
|
||||||
Always include in `SKILL.md`:
|
先比较仓库源和 `~/.agents/skills/<name>/`。保留本地修改时将其整理成插件变更;确认丢弃时再对该次安装使用 `--force`。Claude 目标改查 `~/.claude/skills/`。
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
name: skill-name
|
|
||||||
description: Clear, concise description of what this skill does
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Version Bumping
|
**marketplace 没更新?**
|
||||||
|
|
||||||
When making significant changes:
|
运行 `python3 generate-marketplace.py`,然后检查 `.claude-plugin/marketplace.json` 是否只包含受 Git 跟踪且真实存在的 source。
|
||||||
```bash
|
|
||||||
# Update version in plugin.json
|
|
||||||
{
|
|
||||||
"version": "1.1.0" # was 1.0.0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Testing Before Sync
|
**如何移除技能?**
|
||||||
|
|
||||||
```bash
|
删除插件目录、重新生成 marketplace、提交变更。使用者随后执行 `./install-skills.sh --dry-run --cleanup`,确认后再去掉 `--dry-run`。
|
||||||
# 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.
|
|
||||||
|
|||||||
+6
-13
@@ -14,28 +14,27 @@ script_dir = Path(__file__).parent.resolve()
|
|||||||
config_file = script_dir / "claude-config.yaml"
|
config_file = script_dir / "claude-config.yaml"
|
||||||
marketplace_file = script_dir / ".claude-plugin" / "marketplace.json"
|
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 = [
|
SKILL_DIRS = [
|
||||||
("core", "skills-core"),
|
("core", "skills-core"),
|
||||||
("dev", "skills-dev"),
|
("dev", "skills-dev"),
|
||||||
("req", "skills-req"),
|
("req", "skills-req"),
|
||||||
("integration", "skills-integration"),
|
("integration", "skills-integration"),
|
||||||
("biz", "skills-biz"),
|
("biz", "skills-biz"),
|
||||||
("personal", "skills-personal"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
"""Load claude-config.yaml and return disabled list + personal_dir."""
|
"""Load claude-config.yaml and return the disabled plugin list."""
|
||||||
disabled = []
|
disabled = []
|
||||||
personal = "skills-personal"
|
|
||||||
|
|
||||||
if config_file.exists() and HAS_YAML:
|
if config_file.exists() and HAS_YAML:
|
||||||
with open(config_file) as f:
|
with open(config_file) as f:
|
||||||
cfg = yaml.safe_load(f) or {}
|
cfg = yaml.safe_load(f) or {}
|
||||||
skills_cfg = cfg.get("skills", {})
|
skills_cfg = cfg.get("skills", {})
|
||||||
disabled = skills_cfg.get("disabled", []) or []
|
disabled = skills_cfg.get("disabled", []) or []
|
||||||
personal = skills_cfg.get("personal_dir", personal)
|
|
||||||
elif config_file.exists():
|
elif config_file.exists():
|
||||||
# Fallback: parse disabled list without PyYAML
|
# Fallback: parse disabled list without PyYAML
|
||||||
in_disabled = False
|
in_disabled = False
|
||||||
@@ -54,10 +53,7 @@ def load_config():
|
|||||||
disabled.append(val)
|
disabled.append(val)
|
||||||
elif stripped and not stripped.startswith("#"):
|
elif stripped and not stripped.startswith("#"):
|
||||||
break
|
break
|
||||||
if stripped.startswith("personal_dir:"):
|
return disabled
|
||||||
personal = stripped.split(":", 1)[1].strip().strip('"').strip("'")
|
|
||||||
|
|
||||||
return disabled, personal
|
|
||||||
|
|
||||||
|
|
||||||
# Category mapping
|
# Category mapping
|
||||||
@@ -116,15 +112,12 @@ def scan_plugins(directory, source_prefix, disabled):
|
|||||||
|
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
disabled_skills, personal_dir_name = load_config()
|
disabled_skills = load_config()
|
||||||
|
|
||||||
# Collect plugins from all skill directories
|
# Collect plugins from all skill directories
|
||||||
plugins = []
|
plugins = []
|
||||||
counts = {}
|
counts = {}
|
||||||
for label, dir_name in SKILL_DIRS:
|
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
|
skill_path = script_dir / dir_name
|
||||||
if not skill_path.is_dir():
|
if not skill_path.is_dir():
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ai-proj-helper 初始化脚本
|
# ai-proj-helper 初始化脚本
|
||||||
# 配置 MCP 连接 + 安装技能到 ~/.claude/skills/
|
# 配置 Claude MCP 连接 + 安装 Claude 技能
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -183,32 +183,13 @@ EOF
|
|||||||
fi
|
fi
|
||||||
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/ ..."
|
echo "📦 安装技能到 ~/.claude/skills/ ..."
|
||||||
SKILLS_DIR="$HOME/.claude/skills"
|
"$SCRIPT_DIR/install-skills.sh" --agent claude
|
||||||
mkdir -p "$SKILLS_DIR"
|
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 "✅ 技能安装完成"
|
||||||
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"
|
|
||||||
|
|
||||||
# ── Verify MCP connection ────────────────────────────────────────────
|
# ── Verify MCP connection ────────────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
@@ -270,7 +251,7 @@ if $HAS_CLAUDE; then
|
|||||||
else
|
else
|
||||||
echo " ✅ MCP 服务器 → $MCP_CONFIG"
|
echo " ✅ MCP 服务器 → $MCP_CONFIG"
|
||||||
fi
|
fi
|
||||||
echo " ✅ 技能 ($SKILL_COUNT 个) → $SKILLS_DIR"
|
echo " ✅ 技能 ($SKILL_COUNT 个) → ~/.claude/skills"
|
||||||
echo ""
|
echo ""
|
||||||
echo "重启 Claude Code 即可使用。"
|
echo "重启 Claude Code 即可使用。"
|
||||||
echo "如需更改配置,编辑 claude-config.yaml 后重新运行 ./init.sh"
|
echo "如需更改配置,编辑 claude-config.yaml 后重新运行 ./init.sh"
|
||||||
|
|||||||
+181
-63
@@ -1,13 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# install-skills.sh — Cross-machine Claude skill sync from ai-proj-helper
|
# install-skills.sh — Cross-agent skill sync from ai-proj-helper
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./install-skills.sh [options]
|
# ./install-skills.sh [options]
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
|
# --agent <agent> Install target: codex (default) or claude
|
||||||
# --dry-run Preview changes without writing anything
|
# --dry-run Preview changes without writing anything
|
||||||
# --category <cat> Only install plugins in dir_category=<cat>
|
# --category <cat> Only install plugins in dir_category=<cat>
|
||||||
# Valid values: biz, core, dev, integration, personal, req
|
# Valid values: biz, core, dev, integration, personal, req
|
||||||
|
# --exclude <name> Skip one install_name (repeatable)
|
||||||
# --force Overwrite even if local files were modified
|
# --force Overwrite even if local files were modified
|
||||||
# --cleanup Remove locally installed skills that are no longer in repo
|
# --cleanup Remove locally installed skills that are no longer in repo
|
||||||
# --list List all available plugins without installing
|
# --list List all available plugins without installing
|
||||||
@@ -16,15 +18,18 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
SKILLS_DIR="${HOME}/.claude/skills"
|
SKILLS_DIR=""
|
||||||
COMMANDS_DIR="${HOME}/.claude/commands"
|
COMMANDS_DIR=""
|
||||||
STATE_FILE="${HOME}/.claude/.installed-skills.json"
|
STATE_FILE=""
|
||||||
|
|
||||||
|
AGENT_TARGET="codex"
|
||||||
DRY_RUN=false
|
DRY_RUN=false
|
||||||
CATEGORY_FILTER=""
|
CATEGORY_FILTER=""
|
||||||
|
EXCLUDED_NAMES=()
|
||||||
FORCE=false
|
FORCE=false
|
||||||
CLEANUP=false
|
CLEANUP=false
|
||||||
LIST_ONLY=false
|
LIST_ONLY=false
|
||||||
|
INSTALL_ACTION=false
|
||||||
|
|
||||||
# ── Colour helpers ─────────────────────────────────────────────────────────────
|
# ── Colour helpers ─────────────────────────────────────────────────────────────
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
@@ -42,11 +47,19 @@ dry() { echo -e "${YELLOW}[dry]${RESET} $*"; }
|
|||||||
# ── Argument parsing ───────────────────────────────────────────────────────────
|
# ── Argument parsing ───────────────────────────────────────────────────────────
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
--agent)
|
||||||
|
[[ $# -ge 2 ]] || { error "--agent requires codex or claude"; exit 1; }
|
||||||
|
AGENT_TARGET="$2"; shift ;;
|
||||||
--dry-run) DRY_RUN=true ;;
|
--dry-run) DRY_RUN=true ;;
|
||||||
--force) FORCE=true ;;
|
--force) FORCE=true ;;
|
||||||
--cleanup) CLEANUP=true ;;
|
--cleanup) CLEANUP=true ;;
|
||||||
--list) LIST_ONLY=true ;;
|
--list) LIST_ONLY=true ;;
|
||||||
--category) CATEGORY_FILTER="$2"; shift ;;
|
--category)
|
||||||
|
[[ $# -ge 2 ]] || { error "--category requires a value"; exit 1; }
|
||||||
|
CATEGORY_FILTER="$2"; shift ;;
|
||||||
|
--exclude)
|
||||||
|
[[ $# -ge 2 ]] || { error "--exclude requires an install_name"; exit 1; }
|
||||||
|
EXCLUDED_NAMES+=("$2"); shift ;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
grep '^#' "$0" | grep -v '!/usr' | sed 's/^# \?//'
|
grep '^#' "$0" | grep -v '!/usr' | sed 's/^# \?//'
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
@@ -57,6 +70,24 @@ while [[ $# -gt 0 ]]; do
|
|||||||
shift
|
shift
|
||||||
done
|
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 helpers (plain JSON via python3) ─────────────────────────────────────
|
||||||
state_get() {
|
state_get() {
|
||||||
# state_get <install_name> -> prints version or empty string
|
# state_get <install_name> -> prints version or empty string
|
||||||
@@ -72,14 +103,28 @@ except: pass
|
|||||||
fi
|
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() {
|
||||||
# state_set <install_name> <version> <install_type>
|
# state_set <install_name> <version> <install_type> <content_digest>
|
||||||
local name="$1" ver="$2" itype="$3"
|
local name="$1" ver="$2" itype="$3" digest="$4"
|
||||||
python3 -c "
|
python3 -c "
|
||||||
import json,os
|
import json,os
|
||||||
f='$STATE_FILE'
|
f='$STATE_FILE'
|
||||||
d=json.load(open(f)) if os.path.exists(f) else {}
|
d=json.load(open(f)) if os.path.exists(f) else {}
|
||||||
d['$name']={'version':'$ver','install_type':'$itype'}
|
d['$name']={'version':'$ver','install_type':'$itype','content_digest':'$digest','agent':'$AGENT_TARGET'}
|
||||||
json.dump(d,open(f,'w'),indent=2)
|
json.dump(d,open(f,'w'),indent=2)
|
||||||
" 2>/dev/null
|
" 2>/dev/null
|
||||||
}
|
}
|
||||||
@@ -116,6 +161,66 @@ read_field() {
|
|||||||
python3 -c "import json,sys; d=json.load(open('$1')); print(d.get('$2',''))" 2>/dev/null || true
|
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.
|
# Resolve the actual source directory to rsync from.
|
||||||
# If skills/ has SKILL.md at the top level, use it directly.
|
# 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.
|
# If skills/ has a single subdirectory (e.g. skills/dev-test/SKILL.md), use that subdirectory.
|
||||||
@@ -135,33 +240,9 @@ resolve_skills_src() {
|
|||||||
echo "$skills_dir"
|
echo "$skills_dir"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Conflict detection (has local been modified since we installed it?) ────────
|
|
||||||
has_local_modification() {
|
|
||||||
# Returns 0 (true) if local target differs from repo source, 1 if identical or new
|
|
||||||
local install_name="$1" install_type="$2" plugin_skills_dir="$3"
|
|
||||||
|
|
||||||
if [[ "$install_type" == "command" ]]; then
|
|
||||||
local src="$plugin_skills_dir/SKILL.md"
|
|
||||||
local dst="$COMMANDS_DIR/${install_name}.md"
|
|
||||||
[[ -f "$dst" ]] && ! diff -q "$src" "$dst" &>/dev/null && return 0
|
|
||||||
else
|
|
||||||
local dst_dir="$SKILLS_DIR/$install_name"
|
|
||||||
if [[ -d "$dst_dir" ]]; then
|
|
||||||
# Compare each file from source
|
|
||||||
while IFS= read -r -d '' src_file; do
|
|
||||||
local rel="${src_file#$plugin_skills_dir/}"
|
|
||||||
local dst_file="$dst_dir/$rel"
|
|
||||||
if [[ -f "$dst_file" ]] && ! diff -q "$src_file" "$dst_file" &>/dev/null; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done < <(find "$plugin_skills_dir" -type f -print0)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Install a single plugin ────────────────────────────────────────────────────
|
# ── Install a single plugin ────────────────────────────────────────────────────
|
||||||
install_plugin() {
|
install_plugin() {
|
||||||
|
INSTALL_ACTION=false
|
||||||
local json_path="$1"
|
local json_path="$1"
|
||||||
local plugin_dir
|
local plugin_dir
|
||||||
plugin_dir="$(dirname "$(dirname "$json_path")")" # strip /.claude-plugin/plugin.json
|
plugin_dir="$(dirname "$(dirname "$json_path")")" # strip /.claude-plugin/plugin.json
|
||||||
@@ -173,6 +254,11 @@ install_plugin() {
|
|||||||
dir_category="$(read_field "$json_path" dir_category)"
|
dir_category="$(read_field "$json_path" dir_category)"
|
||||||
version="$(read_field "$json_path" version)"
|
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)
|
# Skip if no install metadata (legacy plugin without our new fields)
|
||||||
if [[ -z "$install_name" || -z "$install_type" ]]; then
|
if [[ -z "$install_name" || -z "$install_type" ]]; then
|
||||||
warn "$(basename "$plugin_dir"): missing install_name/install_type, skipping"
|
warn "$(basename "$plugin_dir"): missing install_name/install_type, skipping"
|
||||||
@@ -190,35 +276,67 @@ install_plugin() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local effective_install_type="$install_type"
|
||||||
|
if [[ "$AGENT_TARGET" == "codex" ]]; then
|
||||||
|
effective_install_type="skill"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$LIST_ONLY" == true ]]; then
|
if [[ "$LIST_ONLY" == true ]]; then
|
||||||
echo " [$dir_category] $install_type:$install_name v$version"
|
echo " [$dir_category] $effective_install_type:$install_name v$version"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check current installed version
|
|
||||||
local current_version
|
|
||||||
current_version="$(state_get "$install_name")"
|
|
||||||
|
|
||||||
# Skip if up-to-date (same version) and no force
|
|
||||||
if [[ "$current_version" == "$version" && "$FORCE" == false ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Conflict detection: warn if local files were modified
|
|
||||||
if [[ -n "$current_version" && "$FORCE" == false ]]; then
|
|
||||||
if has_local_modification "$install_name" "$install_type" "$skills_dir"; then
|
|
||||||
warn "$install_name: local files were modified — skipping (use --force to overwrite)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Resolve actual source (handles plugins where content sits one level deeper,
|
# Resolve actual source (handles plugins where content sits one level deeper,
|
||||||
# e.g. skills/dev-test/SKILL.md instead of skills/SKILL.md)
|
# e.g. skills/dev-test/SKILL.md instead of skills/SKILL.md).
|
||||||
local src_dir
|
local src_dir
|
||||||
src_dir="$(resolve_skills_src "$skills_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
|
# Perform install
|
||||||
if [[ "$install_type" == "command" ]]; then
|
if [[ "$effective_install_type" == "command" ]]; then
|
||||||
# Single-file command → ~/.claude/commands/<name>.md
|
# Single-file command → ~/.claude/commands/<name>.md
|
||||||
local src_md="$src_dir/SKILL.md"
|
local src_md="$src_dir/SKILL.md"
|
||||||
if [[ ! -f "$src_md" ]]; then
|
if [[ ! -f "$src_md" ]]; then
|
||||||
@@ -228,25 +346,29 @@ install_plugin() {
|
|||||||
|
|
||||||
if [[ "$DRY_RUN" == true ]]; then
|
if [[ "$DRY_RUN" == true ]]; then
|
||||||
dry "$install_name → $COMMANDS_DIR/${install_name}.md"
|
dry "$install_name → $COMMANDS_DIR/${install_name}.md"
|
||||||
|
INSTALL_ACTION=true
|
||||||
else
|
else
|
||||||
mkdir -p "$COMMANDS_DIR"
|
mkdir -p "$COMMANDS_DIR"
|
||||||
cp "$src_md" "$COMMANDS_DIR/${install_name}.md"
|
cp "$src_md" "$COMMANDS_DIR/${install_name}.md"
|
||||||
state_set "$install_name" "$version" "$install_type"
|
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
|
||||||
ok "$install_name → command (v$version)"
|
ok "$install_name → command (v$version)"
|
||||||
|
INSTALL_ACTION=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
# Skill directory → ~/.claude/skills/<name>/
|
# Standard skill directory → the selected agent's discovery root.
|
||||||
local dst_dir="$SKILLS_DIR/$install_name"
|
local dst_dir="$SKILLS_DIR/$install_name"
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == true ]]; then
|
if [[ "$DRY_RUN" == true ]]; then
|
||||||
dry "$install_name → $dst_dir/"
|
dry "$install_name → $dst_dir/"
|
||||||
|
INSTALL_ACTION=true
|
||||||
else
|
else
|
||||||
mkdir -p "$dst_dir"
|
mkdir -p "$dst_dir"
|
||||||
# rsync resolved source (handles nested skills/ structures)
|
# rsync resolved source (handles nested skills/ structures)
|
||||||
rsync -a --delete "$src_dir/" "$dst_dir/"
|
rsync -a --delete "$src_dir/" "$dst_dir/"
|
||||||
state_set "$install_name" "$version" "$install_type"
|
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
|
||||||
ok "$install_name → skill (v$version)"
|
ok "$install_name → skill (v$version)"
|
||||||
|
INSTALL_ACTION=true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -307,19 +429,15 @@ main() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Installing Claude skills from: $REPO_DIR"
|
info "Installing skills for $AGENT_TARGET from: $REPO_DIR"
|
||||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written"
|
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be written"
|
||||||
[[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER"
|
[[ -n "$CATEGORY_FILTER" ]] && info "Category filter: $CATEGORY_FILTER"
|
||||||
|
|
||||||
local installed=0 skipped=0
|
local installed=0
|
||||||
|
|
||||||
while IFS= read -r json_path; do
|
while IFS= read -r json_path; do
|
||||||
local before
|
|
||||||
before="$(state_all_names | wc -l || true)"
|
|
||||||
install_plugin "$json_path"
|
install_plugin "$json_path"
|
||||||
local after
|
if [[ "$INSTALL_ACTION" == true ]]; then
|
||||||
after="$(state_all_names | wc -l || true)"
|
|
||||||
if [[ "$after" -gt "$before" ]] || [[ "$DRY_RUN" == true ]]; then
|
|
||||||
((installed++)) || true
|
((installed++)) || true
|
||||||
fi
|
fi
|
||||||
done < <(find_plugins)
|
done < <(find_plugins)
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "defect-analysis-plugin",
|
"name": "defect-analysis-plugin",
|
||||||
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
"description": "系统性设计缺陷分析。对需求方案/代码架构进行多维度检查,发现隐藏的技术风险和设计漏洞。当用户提到缺陷检查、方案审查、设计审计时自动激活。",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ description: 系统性设计缺陷分析。对需求方案/代码架构进行多
|
|||||||
|
|
||||||
- 每轮检查一个维度,输出发现的缺陷列表
|
- 每轮检查一个维度,输出发现的缺陷列表
|
||||||
- 如果某轮发现 0 个新缺陷 → **收敛,停止**
|
- 如果某轮发现 0 个新缺陷 → **收敛,停止**
|
||||||
- 如果 5 轮后仍有新发现 → 继续,最多 10 轮
|
- 如果 5 轮后仍有新发现 → 继续;20 轮作为阶段复盘点,不得误报为已收敛
|
||||||
|
- 达到 20 轮仍有新发现时,汇总剩余风险面并请求用户确认是否继续;用户已明确要求持续审计时可继续下一阶段
|
||||||
|
- 只有出现一轮 0 个新缺陷时才标记收敛;达到授权范围、时间或预算边界时应报告“尚未收敛”,不得伪装完成
|
||||||
- 每个缺陷标注严重度和轮次
|
- 每个缺陷标注严重度和轮次
|
||||||
|
|
||||||
## 输出格式
|
## 输出格式
|
||||||
|
|||||||
@@ -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 后端开发
|
## Go 后端开发
|
||||||
|
|
||||||
### 分层架构
|
### 分层架构
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
name: dev-review
|
name: dev-review
|
||||||
description: 代码评审技能。五视角对抗性扫描法,用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。
|
description: 代码评审技能。六视角对抗性扫描法(含 Karpathy Scope 审计),用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。
|
||||||
---
|
---
|
||||||
|
|
||||||
# 代码评审 Skill (dev-review)
|
# 代码评审 Skill (dev-review)
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
独立的代码评审技能,核心方法论是**五视角对抗性扫描法**。
|
独立的代码评审技能,核心方法论是**六视角对抗性扫描法**(五个传统安全/健壮性视角 + Karpathy Scope 审计视角)。
|
||||||
|
|
||||||
**适用场景**:
|
**适用场景**:
|
||||||
- `/req cr [REQ-ID]` — 需求流程中的代码评审阶段
|
- `/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 不可用时是否有降级方案?(缓存穿透到数据库)
|
- [ ] Redis 不可用时是否有降级方案?(缓存穿透到数据库)
|
||||||
- [ ] token 过期/刷新逻辑是否正确?(access vs refresh 不同策略)
|
- [ ] token 过期/刷新逻辑是否正确?(access vs refresh 不同策略)
|
||||||
|
|
||||||
|
### 视角6:Scope 审计者(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 报告模板
|
## CR 报告模板
|
||||||
@@ -160,7 +182,7 @@ file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
|
|||||||
### 变更概要
|
### 变更概要
|
||||||
{1-3 句描述本次变更的目的和范围}
|
{1-3 句描述本次变更的目的和范围}
|
||||||
|
|
||||||
### 五视角扫描结果
|
### 六视角扫描结果
|
||||||
|
|
||||||
#### 1. 攻击者视角
|
#### 1. 攻击者视角
|
||||||
{扫描发现,或 "未发现问题"}
|
{扫描发现,或 "未发现问题"}
|
||||||
@@ -177,6 +199,9 @@ file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
|
|||||||
#### 5. 依赖者视角
|
#### 5. 依赖者视角
|
||||||
{扫描发现,或 "未发现问题"}
|
{扫描发现,或 "未发现问题"}
|
||||||
|
|
||||||
|
#### 6. Scope 审计者视角(Karpathy)
|
||||||
|
{扫描发现,或 "所有变更文件均在设计清单范围内,无过度实现"}
|
||||||
|
|
||||||
### 审查发现汇总
|
### 审查发现汇总
|
||||||
|
|
||||||
| # | 严重度 | 文件:行号 | ���角 | 描述 | 建议 |
|
| # | 严重度 | 文件:行号 | ���角 | 描述 | 建议 |
|
||||||
@@ -221,7 +246,7 @@ file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
|
|||||||
| 文档存在 | CR 任务有附加文档 |
|
| 文档存在 | CR 任务有附加文档 |
|
||||||
| 字数 | ≥ 500 字 |
|
| 字数 | ≥ 500 字 |
|
||||||
| 代码引用 | 含 `file:line` 格式的引用 |
|
| 代码引用 | 含 `file:line` 格式的引用 |
|
||||||
| 五视角扫描 | 含全部 5 个视角章节 |
|
| 六视角扫描 | 含全部 6 个视角章节(含 Scope 审计者) |
|
||||||
| 结论章节 | 含明确的通过/不通过结论 |
|
| 结论章节 | 含明确的通过/不通过结论 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend-design-plugin",
|
"name": "frontend-design-plugin",
|
||||||
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
|
"description": "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: frontend-design
|
name: frontend-design
|
||||||
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
|
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
|
||||||
arguments: [component|page|storybook] <description>
|
arguments: "[component|page|storybook] <description>"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Frontend Design 前端设计技能
|
# Frontend Design 前端设计技能
|
||||||
|
|||||||
@@ -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+Enum,50行 | 一个函数,3行 |
|
||||||
|
| "修空邮件bug" | 顺手加用户名校验+类型注解 | 只改空邮件的那2行 |
|
||||||
|
| "修认证bug" | 直接修改,无验证标准 | 先写复现测试,修复后验证通过 |
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "review-checklist-plugin",
|
"name": "review-checklist-plugin",
|
||||||
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
|
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
# 通用代码评审检查清单
|
|
||||||
|
|
||||||
适用于所有项目,补充五视角扫描法。
|
|
||||||
|
|
||||||
## API 设计
|
|
||||||
- [ ] RESTful 命名是否规范?(复数名词、无动词)
|
|
||||||
- [ ] 分页参数是否有默认值和上限?
|
|
||||||
- [ ] 响应格式是否统一?(code/message/data)
|
|
||||||
|
|
||||||
## 错误处理
|
|
||||||
- [ ] 错误是否被正确传播?(不要吞掉错误)
|
|
||||||
- [ ] 用户可见的错误消息是否友好?(不暴露技术细节)
|
|
||||||
- [ ] 是否有 panic recover 兜底?
|
|
||||||
|
|
||||||
## 性能
|
|
||||||
- [ ] 列表查询是否有分页?(不允许无限制查询)
|
|
||||||
- [ ] N+1 查询问题?(循环内查数据库)
|
|
||||||
- [ ] 是否有不必要的全表扫描?(缺少索引)
|
|
||||||
|
|
||||||
## 可维护性
|
|
||||||
- [ ] 魔法数字是否提取为常量?
|
|
||||||
- [ ] 复杂业务逻辑是否有注释说明?
|
|
||||||
- [ ] 函数是否过长?(超过 100 行考虑拆分)
|
|
||||||
@@ -13,16 +13,17 @@ description: 项目级代码评审检查清单。按项目积累特定检查项
|
|||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
1. `dev-review` 执行五视角扫描时,自动加载当前项目的检查清单
|
1. `dev-review` 执行五视角扫描时,先读取 `references/general.md`
|
||||||
2. 扫描完成后,逐条检查清单项
|
2. 如果仓库是 AI-Proj,读取 `references/ai-proj.md`;如果是 Coolbuy PaaS,读取 `references/coolbuy-paas.md`
|
||||||
3. 检查结果附加到 CR 报告的「项目检查清单」章节
|
3. 扫描完成后,逐条检查适用的清单项
|
||||||
|
4. 检查结果附加到 CR 报告的「项目检查清单」章节
|
||||||
|
|
||||||
## 检查清单文件
|
## 检查清单文件
|
||||||
|
|
||||||
```
|
```
|
||||||
review-checklist-plugin/
|
review-checklist/
|
||||||
├── skills/SKILL.md # 本文件
|
├── SKILL.md # 本文件
|
||||||
└── checklists/
|
└── references/
|
||||||
├── ai-proj.md # AI-Proj 项目清单
|
├── ai-proj.md # AI-Proj 项目清单
|
||||||
├── coolbuy-paas.md # 酷采3.0 项目清单
|
├── coolbuy-paas.md # 酷采3.0 项目清单
|
||||||
└── general.md # 通用清单(所有项目适用)
|
└── general.md # 通用清单(所有项目适用)
|
||||||
@@ -32,7 +33,7 @@ review-checklist-plugin/
|
|||||||
|
|
||||||
当 CR 中发现了一个**项目特有**的问题模式,且未来可能复发时:
|
当 CR 中发现了一个**项目特有**的问题模式,且未来可能复发时:
|
||||||
|
|
||||||
1. 打开对应项目的检查清单文件
|
1. 打开 `references/` 中对应项目的检查清单文件
|
||||||
2. 添加条目,格式:`- [ ] {检查项} — 教训:{来源}`
|
2. 添加条目,格式:`- [ ] {检查项} — 教训:{来源}`
|
||||||
3. 标注严重度和适用范围
|
3. 标注严重度和适用范围
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -18,6 +18,14 @@
|
|||||||
- [ ] JWT token 类型是否区分 access/refresh?— 教训:token 混用导致安全漏洞
|
- [ ] JWT token 类型是否区分 access/refresh?— 教训:token 混用导致安全漏洞
|
||||||
- [ ] bcrypt cost 是否使用 12?— 教训:默认 cost 10 导致登录失败
|
- [ ] 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 发 camel,gin 静默忽略不匹配参数(REQ-20260711-0003)
|
||||||
|
|
||||||
### Redis
|
### Redis
|
||||||
- [ ] Redis key 是否有 TTL?— 缺少 TTL 导致内存泄露
|
- [ ] Redis key 是否有 TTL?— 缺少 TTL 导致内存泄露
|
||||||
- [ ] Redis 不可用时是否降级到数据库?
|
- [ ] Redis 不可用时是否降级到数据库?
|
||||||
@@ -37,6 +45,6 @@
|
|||||||
|
|
||||||
## 通用
|
## 通用
|
||||||
|
|
||||||
- [ ] `.env` ��凭据文件是否被意外加入 git?
|
- [ ] `.env` 等凭据文件是否被意外加入 git?
|
||||||
- [ ] 是否有硬编码的 URL/IP/端口?— 应使用配置
|
- [ ] 是否有硬编码的 URL/IP/端口?— 应使用配置
|
||||||
- [ ] 错误日志是否包含足够的上下文信息?(user_id, tenant_id, request_id)
|
- [ ] 错误日志是否包含足够的上下文信息?(user_id, tenant_id, request_id)
|
||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
### 数据迁移
|
### 数据迁移
|
||||||
- [ ] 从酷采2.0迁移的字段映射是否正确?(varchar ID → bigint ID)
|
- [ ] 从酷采2.0迁移的字段映射是否正确?(varchar ID → bigint ID)
|
||||||
- [ ] 迁移脚本是否处理了酷采2.0���软删除标记(is_delete → deleted_at)?
|
- [ ] 迁移脚本是否处理了酷采2.0 的软删除标记(is_delete → deleted_at)?
|
||||||
|
|
||||||
## 前端(Vue 3 + Ant Design Vue)
|
## 前端(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/文件/任务队列),问用户选哪种 |
|
||||||
|
| "让搜索更快" | 同时加缓存+索引+async,200行 | 列出3种"更快"含义+估算,等确认再做 |
|
||||||
|
| "加个折扣计算" | Strategy+Abstract+Enum+DataClass,50行 | `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 行考虑拆分)
|
||||||
@@ -1,16 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "doubao-voice-plugin",
|
"name": "doubao-voice-plugin",
|
||||||
"description": "Doubao (豆包) Voice API integration for TTS and ASR",
|
"description": "Doubao (豆包) Voice API integration for TTS and ASR",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "qiudl"
|
"name": "qiudl"
|
||||||
},
|
},
|
||||||
"skills": [
|
|
||||||
{
|
|
||||||
"name": "doubao-voice",
|
|
||||||
"path": "./skills/SKILL.md"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"install_name": "doubao-voice",
|
"install_name": "doubao-voice",
|
||||||
"install_type": "skill",
|
"install_type": "skill",
|
||||||
"dir_category": "integration"
|
"dir_category": "integration"
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ analysis → design → dev → review → testing → [待部署池] → releas
|
|||||||
- PRD 文档是提交评审的前置条件;代码评审是测试的前置条件
|
- PRD 文档是提交评审的前置条件;代码评审是测试的前置条件
|
||||||
- **force=true 禁止自动使用** — 门禁未通过时必须 AskUserQuestion 确认 + 记录跳过原因
|
- **force=true 禁止自动使用** — 门禁未通过时必须 AskUserQuestion 确认 + 记录跳过原因
|
||||||
- **评审必须用户确认** — 禁止 AI 自审批
|
- **评审必须用户确认** — 禁止 AI 自审批
|
||||||
- **归档前门禁检查** — `/req done` 按需求类型(code/skill/ops)动态检查
|
- **归档前 AC 逐条验收(Gate 0,强制)** — `/req done` 必须先提取需求描述中的验收标准逐条确认,无 AC 或有未确认项均阻断,用户必须提供证据或说明跳过原因
|
||||||
|
- **归档前类型化门禁检查** — Gate 0 通过后,`/req done` 再按需求类型(code/skill/ops)动态检查
|
||||||
- **部署是项目级动作**,由 `/req deploy` 统一触发
|
- **部署是项目级动作**,由 `/req deploy` 统一触发
|
||||||
- **需求完成后必须 git 提交并 push** — commit 格式:`feat(skill): REQ-XXXX 需求标题`
|
- **需求完成后必须 git 提交并 push** — commit 格式:`feat(skill): REQ-XXXX 需求标题`
|
||||||
- **操作前先确认实际 ID** — 从 URL 提取 ID(如 `/requirements/897` → ID=897)
|
- **操作前先确认实际 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 全部通过的需求)
|
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>`:
|
**`/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
|
- **推断类型**:有 implementation → code;无 implementation 有 prd/test → skill;仅 deploy → ops
|
||||||
- **code 检查**:delivery_stage=released + deploy 任务完成 + verification 任务完成(PDV 通过) + 部署文档 + 所有任务完成
|
- **code 检查**:delivery_stage=released + deploy 任务完成 + verification 任务完成(PDV 通过) + 部署文档 + 所有任务完成
|
||||||
- **skill 检查**:delivery_stage≥testing + 所有任务完成
|
- **skill 检查**:delivery_stage≥testing + 所有任务完成
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: req-prototype
|
name: req-prototype
|
||||||
description: 原型生成与关联。支持两种模式:(1) Stitch AI 基于 PRD 自动生成 UI 原型截图;(2) AI 编写 HTML 原型并上传关联到需求详情页 iframe。当执行 /req prototype 或需要生成/上传界面原型时使用。
|
description: 原型生成与关联。支持两种模式:(1) Stitch AI 基于 PRD 自动生成 UI 原型截图;(2) AI 编写 HTML 原型并上传关联到需求详情页 iframe。当执行 /req prototype 或需要生成/上传界面原型时使用。
|
||||||
arguments: [REQ-ID] [upload|edit|variant] [--device desktop|mobile|tablet] [--model pro|flash] [--note "..."] [--prompt "..."]
|
arguments: <REQ-ID> [subcommand] [options]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 原型设计 Skill (req-prototype)
|
# 原型设计 Skill (req-prototype)
|
||||||
@@ -22,7 +22,6 @@ arguments: [REQ-ID] [upload|edit|variant] [--device desktop|mobile|tablet] [--mo
|
|||||||
| 检查项 | 方式 | 失败处理 |
|
| 检查项 | 方式 | 失败处理 |
|
||||||
|--------|------|----------|
|
|--------|------|----------|
|
||||||
| 需求存在 | `mcp__ai-proj__get_requirement` | 报错:需求不存在 |
|
| 需求存在 | `mcp__ai-proj__get_requirement` | 报错:需求不存在 |
|
||||||
| 后端运行中(upload 模式)| `curl http://localhost:8080/api/v1/health` | 报错:后端未启动 |
|
|
||||||
| PRD 文档存在(Stitch 模式)| 找 linkRole=prd 任务 + 检查文档 | 报错:请先执行 req-prd |
|
| PRD 文档存在(Stitch 模式)| 找 linkRole=prd 任务 + 检查文档 | 报错:请先执行 req-prd |
|
||||||
|
|
||||||
## 子命令
|
## 子命令
|
||||||
@@ -34,29 +33,29 @@ arguments: [REQ-ID] [upload|edit|variant] [--device desktop|mobile|tablet] [--mo
|
|||||||
**执行流程**:
|
**执行流程**:
|
||||||
|
|
||||||
```
|
```
|
||||||
1. 获取需求信息(mcp__ai-proj__get_requirement)
|
1. 获取需求信息(mcp__ai-proj__get_requirement),取得数字 id
|
||||||
2. 读取 PRD 或需求描述,提炼 UI 关键信息
|
2. 读取 PRD 或需求描述,提炼 UI 关键信息
|
||||||
3. AI 编写带完整样式的 HTML 原型文件(见设计规范)
|
3. AI 编写带完整样式的 HTML 原型文件(见设计规范)
|
||||||
4. 保存到 /tmp/proto_<req_id>_<timestamp>.html
|
4. 保存到 /tmp/proto_<req_id>_<timestamp>.html
|
||||||
5. 获取本地 JWT token(登录 API)
|
5. Base64 编码:base64 < /tmp/proto_<req_id>_<timestamp>.html
|
||||||
6. 上传到后端(multipart POST)
|
6. 调用 mcp__ai-proj__upload_prototype 上传(传入 requirementId + base64 content)
|
||||||
7. 确认上传成功,输出预览 URL
|
7. 确认上传成功,输出 COS 预览 URL
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 5-6 执行方式**:
|
**Step 5-6 执行方式**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 5. 获取 token(本地开发环境)
|
# 5. Base64 编码 HTML 文件
|
||||||
TOKEN=$(curl -s http://localhost:8080/api/v1/auth/login \
|
B64=$(base64 < /tmp/proto_<req_id>_<timestamp>.html)
|
||||||
-H 'Content-Type: application/json' \
|
```
|
||||||
-d '{"username":"qiudl","password":"Admin@2026~"}' \
|
|
||||||
| python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["access_token"])')
|
|
||||||
|
|
||||||
# 6. 上传 HTML 原型并关联到需求
|
```
|
||||||
curl -s -X POST "http://localhost:8080/api/v1/requirements/<REQ_DB_ID>/prototype" \
|
# 6. 通过 MCP 工具上传(无需本地后端)
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
mcp__ai-proj__upload_prototype(
|
||||||
-F "file=@/tmp/proto_<req_id>_<timestamp>.html;type=text/html" \
|
requirementId = <需求数字ID>,
|
||||||
-F "version_note=<--note 的值或空>"
|
content = <B64 字符串>,
|
||||||
|
versionNote = <--note 的值或"初稿 v1">
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
**成功响应**:
|
**成功响应**:
|
||||||
@@ -64,15 +63,17 @@ curl -s -X POST "http://localhost:8080/api/v1/requirements/<REQ_DB_ID>/prototype
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
|
"message": "原型已上传并关联到需求 <id>(version=N/A)",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "/api/v1/uploads/prototypes/proto_xxx.html",
|
"url": "https://ai-proj-1252326374.cos.ap-beijing.myqcloud.com/prototypes/<uuid>.html",
|
||||||
"version_note": "...",
|
"versionNote": "...",
|
||||||
"uploaded_at": "..."
|
"uploadedAt": "...",
|
||||||
|
"requirementId": <id>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**效果**:需求详情页(`/requirements/<id>` 或 `/platform/requirements/<id>`)自动出现「原型预览」卡片,iframe 加载上传的 HTML。
|
**效果**:需求详情页自动出现「原型预览」卡片,iframe 加载 COS 上的 HTML 文件。**无需本地后端运行**。
|
||||||
|
|
||||||
**参数**:
|
**参数**:
|
||||||
|
|
||||||
@@ -280,11 +281,10 @@ generated_at: "<timestamp>"
|
|||||||
|
|
||||||
| 异常 | 处理 |
|
| 异常 | 处理 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 后端 500 / `column prototype_urls does not exist` | 需执行数据库迁移:`psql -U <owner> -d <db> -c "ALTER TABLE requirements ADD COLUMN IF NOT EXISTS prototype_urls JSONB DEFAULT '[]';"` |
|
| `mcp__ai-proj__upload_prototype` 返回失败 | 检查 requirementId 是否为数字 ID(非 display_id REQ-xxx) |
|
||||||
| token 获取失败(401)| 检查用户名密码,或改用生产环境 token |
|
|
||||||
| 需求 ID 不存在(404)| 确认使用数据库自增 ID,而非 display_id(REQ-xxx) |
|
|
||||||
| HTML 文件超过 5MB | 精简样式或拆分多版本上传 |
|
| HTML 文件超过 5MB | 精简样式或拆分多版本上传 |
|
||||||
| iframe 不显示 | 检查 `prototype_urls` 字段是否非空:`mcp__ai-proj__get_requirement` 确认 |
|
| iframe 不显示 | 检查 `prototype_urls` 字段是否非空:`mcp__ai-proj__get_requirement` 确认 |
|
||||||
|
| base64 命令失败 | macOS 用 `base64 < file`,Linux 用 `base64 -w 0 < file` |
|
||||||
|
|
||||||
### 原型展示规则
|
### 原型展示规则
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,11 @@ description: 复盘总结。自动采集数据、计算质量评分、跨需求
|
|||||||
### 2. 质量评分(Quality Score)
|
### 2. 质量评分(Quality Score)
|
||||||
|
|
||||||
```
|
```
|
||||||
QS = lookback_pass_rate × 0.3
|
QS = lookback_pass_rate × 0.25
|
||||||
+ audit_defect_score × 0.3
|
+ audit_defect_score × 0.25
|
||||||
+ cr_density_score × 0.2
|
+ cr_density_score × 0.15
|
||||||
+ test_pass_rate × 0.2
|
+ test_pass_rate × 0.15
|
||||||
|
+ karpathy_score × 0.20 ← Karpathy 四原则执行质量
|
||||||
|
|
||||||
audit_defect_score:
|
audit_defect_score:
|
||||||
0 缺陷 = 100
|
0 缺陷 = 100
|
||||||
@@ -38,8 +39,26 @@ audit_defect_score:
|
|||||||
cr_density_score:
|
cr_density_score:
|
||||||
100 - (发现数 / 变更行数 × 1000)
|
100 - (发现数 / 变更行数 × 1000)
|
||||||
下限 0,上限 100
|
下限 0,上限 100
|
||||||
|
|
||||||
|
karpathy_score(四原则执行质量,各 25 分,共 100):
|
||||||
|
Think Before PRD (25):
|
||||||
|
PRD 无返工 → 25 | 因需求误解返工 1 次 → 15 | ≥2 次 → 0
|
||||||
|
Simplicity (25):
|
||||||
|
CR 第六视角无 Scope 违规 → 25 | 1 个 Low → 20 | ≥1 个 Medium+ → 10 | High+ → 0
|
||||||
|
Surgical (25):
|
||||||
|
check-surgical.sh PASS + CR 无顺手改 → 25 | 警告但未阻塞 → 15 | FAIL → 0
|
||||||
|
Goal-Driven (25):
|
||||||
|
dev-coding 有验证脚本记录(VP-Steps 先于代码执行)→ 25 | 事后补写 → 15 | 无 → 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Karpathy 数据来源**(按优先级):
|
||||||
|
1. dev-coding 任务文档中是否有「验证脚本」节
|
||||||
|
2. CR 报告第六视角的发现数和严重度
|
||||||
|
3. `git log` 中是否有 check-surgical.sh baseline 更新提交(说明有过违规)
|
||||||
|
4. PRD 任务文档的版本数(> 1 说明有返工)
|
||||||
|
|
||||||
|
**无数据时**:各维度默认 20 分(中性),在报告中标注 `(无记录,按中性计算)`
|
||||||
|
|
||||||
### 3. 历史趋势对比
|
### 3. 历史趋势对比
|
||||||
|
|
||||||
读取 `memory/retro_metrics.md` 的明细数据:
|
读取 `memory/retro_metrics.md` 的明细数据:
|
||||||
@@ -87,10 +106,12 @@ AI: "近 5 次需求中 3 次 audit 发现了 {缺陷类型}。
|
|||||||
|
|
||||||
追加一行到明细:
|
追加一行到明细:
|
||||||
```
|
```
|
||||||
| REQ-xxx | 2026-04-18 | 5h44m | 92 | 2 | 279 | 3 | frontend |
|
| REQ-xxx | 2026-04-18 | 5h44m | 92 | 2 | 279 | 3 | frontend | 85 |
|
||||||
```
|
```
|
||||||
|
|
||||||
更新汇总:重新计算近 10 次平均值和趋势箭头(↑↓→)。
|
列说明:`REQ | date | time | QS | audit_defects | changed_lines | cr_findings | type | karpathy_score`
|
||||||
|
|
||||||
|
更新汇总:重新计算近 10 次平均值和趋势箭头(↑↓→),包含 karpathy_score 趋势。
|
||||||
|
|
||||||
超过 30 条明细 → 最早的移入 `retro_metrics_archive.md`。
|
超过 30 条明细 → 最早的移入 `retro_metrics_archive.md`。
|
||||||
|
|
||||||
@@ -116,10 +137,19 @@ AI: "近 5 次需求中 3 次 audit 发现了 {缺陷类型}。
|
|||||||
## 质量指标
|
## 质量指标
|
||||||
| 指标 | 本次 | 近10次均 | 对比 |
|
| 指标 | 本次 | 近10次均 | 对比 |
|
||||||
|------|------|---------|------|
|
|------|------|---------|------|
|
||||||
| 质量分 | 92 | 85 | ↑ |
|
| 质量分 (QS) | 92 | 85 | ↑ |
|
||||||
| audit 缺陷 | 2(低) | 3.2 | ↓ |
|
| audit 缺陷 | 2(低) | 3.2 | ↓ |
|
||||||
| CR 发现 | 0 | 1.5 | ↓ |
|
| CR 发现 | 0 | 1.5 | ↓ |
|
||||||
| 测试通过率 | 100% | 95% | ↑ |
|
| 测试通过率 | 100% | 95% | ↑ |
|
||||||
|
| Karpathy 分 | 85 | 78 | ↑ |
|
||||||
|
|
||||||
|
## Karpathy 四原则评分
|
||||||
|
| 原则 | 得分 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| Think Before PRD | 25/25 | PRD 无返工 |
|
||||||
|
| Simplicity | 20/25 | CR 第六视角发现 1 个 Low |
|
||||||
|
| Surgical | 25/25 | check-surgical.sh PASS |
|
||||||
|
| Goal-Driven | 15/25 | 验证脚本为事后补写 |
|
||||||
|
|
||||||
## git 统计
|
## git 统计
|
||||||
| 提交数 | 变更文件 | +行 | -行 |
|
| 提交数 | 变更文件 | +行 | -行 |
|
||||||
|
|||||||
@@ -87,12 +87,24 @@ done
|
|||||||
**报告格式**:
|
**报告格式**:
|
||||||
```
|
```
|
||||||
### 约定检查 (Gate 0B)
|
### 约定检查 (Gate 0B)
|
||||||
| 脚本 | 结果 | 详情 |
|
| 脚本 | 类型 | 结果 | 详情 |
|
||||||
|------|------|------|
|
|------|------|------|------|
|
||||||
| check-architecture.sh | ✅ PASS | 5 rules, all within baseline |
|
| check-architecture.sh | Ratchet | ✅ PASS | 5 rules, all within baseline |
|
||||||
| check-modal-safety.sh | ✅ PASS | 0 violations |
|
| check-modal-safety.sh | Hard wall | ✅ PASS | 0 violations |
|
||||||
|
| check-surgical.sh | Ratchet | ✅ PASS | 0 format-only violations (baseline=0) |
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**本项目已建立的约定检查脚本**:
|
||||||
|
|
||||||
|
| 脚本 | 类型 | 检测内容 | 来源 |
|
||||||
|
|------|------|---------|------|
|
||||||
|
| `check-architecture.sh` | Ratchet | Handler 直接引用 database/ 层 | 分层架构规范 |
|
||||||
|
| `check-modal-safety.sh` | Hard wall | Modal.success 后立即操作 UI | REQ-20260416 |
|
||||||
|
| `check-surgical.sh` | Ratchet | PR diff 中疑似仅注释/格式变更的文件 | Karpathy Surgical(REQ-20260421-0003)|
|
||||||
|
|
||||||
|
> `check-surgical.sh` 使用 Ratchet 模式:`.surgical-baseline.json` 记录基线,违规数只能降不能升。
|
||||||
|
> 首次无基线时仅告警,不阻塞。运行 `./scripts/check-surgical.sh baseline` 建立基线。
|
||||||
|
|
||||||
> 这样 Harness 建立的约定脚本会在每次 `/req test` 时自动运行,无需手动执行 `/harness report`。
|
> 这样 Harness 建立的约定脚本会在每次 `/req test` 时自动运行,无需手动执行 `/harness report`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Executable
+114
@@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
TEST_ROOT="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$TEST_ROOT"' EXIT
|
||||||
|
|
||||||
|
FIXTURE_REPO="$TEST_ROOT/repo"
|
||||||
|
TEST_HOME="$TEST_ROOT/home"
|
||||||
|
mkdir -p "$FIXTURE_REPO/skills-dev/example-plugin/.claude-plugin"
|
||||||
|
mkdir -p "$FIXTURE_REPO/skills-dev/example-plugin/skills/references"
|
||||||
|
mkdir -p "$TEST_HOME"
|
||||||
|
cp "$PROJECT_DIR/install-skills.sh" "$FIXTURE_REPO/install-skills.sh"
|
||||||
|
|
||||||
|
write_manifest() {
|
||||||
|
local plugin="$1" name="$2" version="$3" install_type="${4:-skill}"
|
||||||
|
mkdir -p "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin"
|
||||||
|
cat > "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin/plugin.json" <<JSON
|
||||||
|
{"name":"${plugin}-plugin","version":"${version}","install_name":"${name}","install_type":"${install_type}","dir_category":"dev"}
|
||||||
|
JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
write_manifest example example 1.0.0
|
||||||
|
cat > "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md" <<'EOF'
|
||||||
|
---
|
||||||
|
name: example
|
||||||
|
description: Installer fixture version one.
|
||||||
|
---
|
||||||
|
version one
|
||||||
|
EOF
|
||||||
|
printf 'reference one\n' > "$FIXTURE_REPO/skills-dev/example-plugin/skills/references/guide.md"
|
||||||
|
|
||||||
|
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||||
|
test -f "$TEST_HOME/.agents/skills/example/references/guide.md"
|
||||||
|
|
||||||
|
# A repository version upgrade replaces an unchanged prior install.
|
||||||
|
write_manifest example example 2.0.0
|
||||||
|
cat > "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md" <<'EOF'
|
||||||
|
---
|
||||||
|
name: example
|
||||||
|
description: Installer fixture version two.
|
||||||
|
---
|
||||||
|
version two
|
||||||
|
EOF
|
||||||
|
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||||
|
grep -q 'version two' "$TEST_HOME/.agents/skills/example/SKILL.md"
|
||||||
|
grep -q '"version": "2.0.0"' "$TEST_HOME/.agents/.ai-proj-helper-installed-skills.json"
|
||||||
|
|
||||||
|
# A local edit is preserved even when the repository advances again.
|
||||||
|
printf 'local edit\n' >> "$TEST_HOME/.agents/skills/example/SKILL.md"
|
||||||
|
write_manifest example example 3.0.0
|
||||||
|
printf 'repository version three\n' >> "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md"
|
||||||
|
output="$(HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh")"
|
||||||
|
grep -q 'local files were modified' <<<"$output"
|
||||||
|
grep -q 'local edit' "$TEST_HOME/.agents/skills/example/SKILL.md"
|
||||||
|
if grep -q 'repository version three' "$TEST_HOME/.agents/skills/example/SKILL.md"; then
|
||||||
|
echo 'local modification was overwritten' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Legacy SKILL.md-only installs gain missing repository resources when their
|
||||||
|
# existing content is an unchanged subset of the source.
|
||||||
|
write_manifest legacy legacy 1.0.0
|
||||||
|
mkdir -p "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/references"
|
||||||
|
cat > "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/SKILL.md" <<'EOF'
|
||||||
|
---
|
||||||
|
name: legacy
|
||||||
|
description: Legacy installation fixture.
|
||||||
|
---
|
||||||
|
legacy content
|
||||||
|
EOF
|
||||||
|
printf 'legacy reference\n' > "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/references/guide.md"
|
||||||
|
mkdir -p "$TEST_HOME/.agents/skills/legacy"
|
||||||
|
cp "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/SKILL.md" "$TEST_HOME/.agents/skills/legacy/SKILL.md"
|
||||||
|
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||||
|
test -f "$TEST_HOME/.agents/skills/legacy/references/guide.md"
|
||||||
|
|
||||||
|
# Command manifests become standard Codex skills, while explicit Claude
|
||||||
|
# installs retain the legacy single-file command layout. Both must be stable on
|
||||||
|
# a second run despite the Claude filename change.
|
||||||
|
write_manifest sample-command sample-command 1.0.0 command
|
||||||
|
mkdir -p "$FIXTURE_REPO/skills-dev/sample-command-plugin/skills"
|
||||||
|
cat > "$FIXTURE_REPO/skills-dev/sample-command-plugin/skills/SKILL.md" <<'EOF'
|
||||||
|
---
|
||||||
|
name: sample-command
|
||||||
|
description: Command installation fixture.
|
||||||
|
---
|
||||||
|
command content
|
||||||
|
EOF
|
||||||
|
|
||||||
|
CODEX_TEST_HOME="$TEST_ROOT/codex-home"
|
||||||
|
mkdir -p "$CODEX_TEST_HOME"
|
||||||
|
HOME="$CODEX_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||||
|
test -f "$CODEX_TEST_HOME/.agents/skills/sample-command/SKILL.md"
|
||||||
|
test ! -e "$CODEX_TEST_HOME/.claude/commands/sample-command.md"
|
||||||
|
codex_output="$(HOME="$CODEX_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" --dry-run)"
|
||||||
|
grep -q '0 plugins would be installed/updated' <<<"$codex_output"
|
||||||
|
if grep -q 'sample-command: local files were modified' <<<"$codex_output"; then
|
||||||
|
echo 'Codex command skill was reported as modified' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLAUDE_TEST_HOME="$TEST_ROOT/claude-home"
|
||||||
|
mkdir -p "$CLAUDE_TEST_HOME"
|
||||||
|
HOME="$CLAUDE_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" --agent claude >/dev/null
|
||||||
|
test -f "$CLAUDE_TEST_HOME/.claude/commands/sample-command.md"
|
||||||
|
claude_output="$(HOME="$CLAUDE_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" --agent claude --dry-run)"
|
||||||
|
grep -q '0 plugins would be installed/updated' <<<"$claude_output"
|
||||||
|
if grep -q 'sample-command: local files were modified' <<<"$claude_output"; then
|
||||||
|
echo 'Claude command was reported as modified after filename conversion' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'install-skills tests passed'
|
||||||
@@ -22,6 +22,8 @@ PLUGIN_MAP = {
|
|||||||
"publish-plugin": ("publish", "skill", "core"),
|
"publish-plugin": ("publish", "skill", "core"),
|
||||||
|
|
||||||
# skills-dev
|
# skills-dev
|
||||||
|
"ai-proj-cicd-release-plugin": ("ai-proj-cicd-release", "skill", "dev"),
|
||||||
|
"ai-proj-macos-release-plugin": ("ai-proj-macos-release", "skill", "dev"),
|
||||||
"agent-browser-plugin": ("agent-browser", "skill", "dev"),
|
"agent-browser-plugin": ("agent-browser", "skill", "dev"),
|
||||||
"agent-swarm-plugin": ("agent-swarm", "skill", "dev"),
|
"agent-swarm-plugin": ("agent-swarm", "skill", "dev"),
|
||||||
"ai-chat-plugin": ("ai-chat", "skill", "dev"),
|
"ai-chat-plugin": ("ai-chat", "skill", "dev"),
|
||||||
@@ -44,6 +46,7 @@ PLUGIN_MAP = {
|
|||||||
"executing-plans-plugin": ("executing-plans", "skill", "dev"),
|
"executing-plans-plugin": ("executing-plans", "skill", "dev"),
|
||||||
"finishing-branch-plugin": ("finishing-a-development-branch","skill", "dev"), # name mismatch!
|
"finishing-branch-plugin": ("finishing-a-development-branch","skill", "dev"), # name mismatch!
|
||||||
"frontend-design-plugin": ("frontend-design", "skill", "dev"),
|
"frontend-design-plugin": ("frontend-design", "skill", "dev"),
|
||||||
|
"karpathy-guidelines-plugin": ("karpathy-guidelines", "skill", "dev"),
|
||||||
"pull-request-plugin": ("pull-request", "skill", "dev"),
|
"pull-request-plugin": ("pull-request", "skill", "dev"),
|
||||||
"review-checklist-plugin": ("review-checklist", "skill", "dev"),
|
"review-checklist-plugin": ("review-checklist", "skill", "dev"),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user