Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a1400758e | ||
|
|
a58dd1aff3 | ||
|
|
bb5e6be73e |
@@ -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
|
||||||
|
|||||||
@@ -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,18 +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/skills/`(包括 references、scripts 和 assets)
|
|
||||||
|
|
||||||
也支持命令行参数跳过交互:
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -172,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,4 +1,6 @@
|
|||||||
# Setup Guide
|
# Claude Code Marketplace Setup
|
||||||
|
|
||||||
|
本页只描述 Claude Code marketplace。Codex 用户直接运行 `./install-skills.sh`,技能默认安装到 `~/.agents/skills`。
|
||||||
|
|
||||||
## 1. Clone the Gitea Repository
|
## 1. Clone the Gitea Repository
|
||||||
|
|
||||||
@@ -114,7 +116,7 @@ ai-proj-helper/
|
|||||||
- 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`
|
||||||
|
|||||||
+10
-3
@@ -1,6 +1,6 @@
|
|||||||
# Skill Sync Guide
|
# Skill Sync Guide
|
||||||
|
|
||||||
仓库中的插件是团队技能的发布源,本机 `~/.claude/skills/` 是安装目标。个人技能保留在
|
仓库中的插件是团队技能的发布源。默认安装目标是 Codex 的用户级标准目录 `~/.agents/skills/`。个人技能保留在
|
||||||
`skills-personal/` 或其他本机目录,不会进入公开 marketplace。
|
`skills-personal/` 或其他本机目录,不会进入公开 marketplace。
|
||||||
|
|
||||||
## 从仓库更新本机
|
## 从仓库更新本机
|
||||||
@@ -11,6 +11,12 @@ git pull
|
|||||||
./install-skills.sh
|
./install-skills.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Claude Code 需要显式选择目标:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./install-skills.sh --agent claude
|
||||||
|
```
|
||||||
|
|
||||||
安装器会复制完整技能目录,包括 `SKILL.md`、`references/`、`scripts/` 和 `assets/`。它用内容摘要区分仓库升级和本地修改:
|
安装器会复制完整技能目录,包括 `SKILL.md`、`references/`、`scripts/` 和 `assets/`。它用内容摘要区分仓库升级和本地修改:
|
||||||
|
|
||||||
- 目标未修改时,版本升级会自动安装。
|
- 目标未修改时,版本升级会自动安装。
|
||||||
@@ -23,11 +29,12 @@ git pull
|
|||||||
```bash
|
```bash
|
||||||
./install-skills.sh --list
|
./install-skills.sh --list
|
||||||
./install-skills.sh --category dev
|
./install-skills.sh --category dev
|
||||||
|
./install-skills.sh --exclude ai-proj-cicd-release
|
||||||
```
|
```
|
||||||
|
|
||||||
## 将本机技能发布到仓库
|
## 将本机技能发布到仓库
|
||||||
|
|
||||||
不要批量复制整个 `~/.claude/skills/`。系统技能、第三方托管技能、包含机器路径或凭据的技能不应发布。
|
不要批量复制整个 `~/.agents/skills/` 或其他 Agent 的安装目录。系统技能、第三方托管技能、包含机器路径或凭据的技能不应发布。
|
||||||
|
|
||||||
1. 选择确实属于本仓库、可供团队复用的技能。
|
1. 选择确实属于本仓库、可供团队复用的技能。
|
||||||
2. 在对应 `skills-*/<name>-plugin/` 下放置 `.claude-plugin/plugin.json` 和完整 `skills/` 目录。
|
2. 在对应 `skills-*/<name>-plugin/` 下放置 `.claude-plugin/plugin.json` 和完整 `skills/` 目录。
|
||||||
@@ -51,7 +58,7 @@ git pull
|
|||||||
|
|
||||||
**本地修改被跳过怎么办?**
|
**本地修改被跳过怎么办?**
|
||||||
|
|
||||||
先比较仓库源和 `~/.claude/skills/<name>/`。保留本地修改时将其整理成插件变更;确认丢弃时再对该次安装使用 `--force`。
|
先比较仓库源和 `~/.agents/skills/<name>/`。保留本地修改时将其整理成插件变更;确认丢弃时再对该次安装使用 `--force`。Claude 目标改查 `~/.claude/skills/`。
|
||||||
|
|
||||||
**marketplace 没更新?**
|
**marketplace 没更新?**
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ai-proj-helper 初始化脚本
|
# ai-proj-helper 初始化脚本
|
||||||
# 配置 MCP 连接 + 安装技能到 ~/.claude/skills/
|
# 配置 Claude MCP 连接 + 安装 Claude 技能
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -187,7 +187,8 @@ fi
|
|||||||
# Use the versioned installer as the single installation path so references,
|
# Use the versioned installer as the single installation path so references,
|
||||||
# scripts and assets stay beside SKILL.md and local edits are not overwritten.
|
# scripts and assets stay beside SKILL.md and local edits are not overwritten.
|
||||||
echo "📦 安装技能到 ~/.claude/skills/ ..."
|
echo "📦 安装技能到 ~/.claude/skills/ ..."
|
||||||
"$SCRIPT_DIR/install-skills.sh"
|
"$SCRIPT_DIR/install-skills.sh" --agent claude
|
||||||
|
SKILL_COUNT=$(python3 -c 'import json, os; p=os.path.expanduser("~/.claude/.installed-skills.json"); print(len(json.load(open(p))) if os.path.exists(p) else 0)' 2>/dev/null || echo 0)
|
||||||
echo "✅ 技能安装完成"
|
echo "✅ 技能安装完成"
|
||||||
|
|
||||||
# ── Verify MCP connection ────────────────────────────────────────────
|
# ── Verify MCP connection ────────────────────────────────────────────
|
||||||
@@ -250,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"
|
||||||
|
|||||||
+58
-16
@@ -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,12 +18,14 @@
|
|||||||
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
|
||||||
@@ -43,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 ;;
|
||||||
@@ -58,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
|
||||||
@@ -94,7 +124,7 @@ state_set() {
|
|||||||
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','content_digest':'$digest'}
|
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
|
||||||
}
|
}
|
||||||
@@ -149,7 +179,9 @@ files = [target] if target.is_file() else sorted(
|
|||||||
path for path in target.rglob("*") if path.is_file() or path.is_symlink()
|
path for path in target.rglob("*") if path.is_file() or path.is_symlink()
|
||||||
)
|
)
|
||||||
for path in files:
|
for path in files:
|
||||||
relative = path.name if target.is_file() else path.relative_to(target).as_posix()
|
# 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(relative.encode("utf-8"))
|
||||||
digest.update(b"\0")
|
digest.update(b"\0")
|
||||||
if path.is_symlink():
|
if path.is_symlink():
|
||||||
@@ -222,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"
|
||||||
@@ -239,8 +276,13 @@ 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
|
||||||
|
|
||||||
@@ -250,7 +292,7 @@ install_plugin() {
|
|||||||
src_dir="$(resolve_skills_src "$skills_dir")"
|
src_dir="$(resolve_skills_src "$skills_dir")"
|
||||||
|
|
||||||
local source_path target_path
|
local source_path target_path
|
||||||
if [[ "$install_type" == "command" ]]; then
|
if [[ "$effective_install_type" == "command" ]]; then
|
||||||
source_path="$src_dir/SKILL.md"
|
source_path="$src_dir/SKILL.md"
|
||||||
target_path="$COMMANDS_DIR/${install_name}.md"
|
target_path="$COMMANDS_DIR/${install_name}.md"
|
||||||
else
|
else
|
||||||
@@ -274,13 +316,13 @@ install_plugin() {
|
|||||||
|
|
||||||
if [[ "$FORCE" == false && -n "$target_digest" && "$target_digest" == "$source_digest" ]]; then
|
if [[ "$FORCE" == false && -n "$target_digest" && "$target_digest" == "$source_digest" ]]; then
|
||||||
if [[ "$DRY_RUN" == false && ( "$current_version" != "$version" || "$recorded_digest" != "$source_digest" ) ]]; then
|
if [[ "$DRY_RUN" == false && ( "$current_version" != "$version" || "$recorded_digest" != "$source_digest" ) ]]; then
|
||||||
state_set "$install_name" "$version" "$install_type" "$source_digest"
|
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
|
||||||
fi
|
fi
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local legacy_subset=false
|
local legacy_subset=false
|
||||||
if [[ "$install_type" == "skill" && -z "$recorded_digest" && -n "$target_digest" ]]; then
|
if [[ "$effective_install_type" == "skill" && -z "$recorded_digest" && -n "$target_digest" ]]; then
|
||||||
if is_compatible_subset "$source_path" "$target_path"; then
|
if is_compatible_subset "$source_path" "$target_path"; then
|
||||||
legacy_subset=true
|
legacy_subset=true
|
||||||
fi
|
fi
|
||||||
@@ -294,7 +336,7 @@ install_plugin() {
|
|||||||
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
|
||||||
@@ -308,13 +350,13 @@ install_plugin() {
|
|||||||
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" "$source_digest"
|
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
|
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
|
||||||
@@ -324,7 +366,7 @@ install_plugin() {
|
|||||||
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" "$source_digest"
|
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
|
INSTALL_ACTION=true
|
||||||
fi
|
fi
|
||||||
@@ -387,7 +429,7 @@ 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"
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,19 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
|||||||
**插件扩展**:
|
**插件扩展**:
|
||||||
- `req-compare` — 对比式 PRD 编写(系统平移/竞品借鉴时激活)
|
- `req-compare` — 对比式 PRD 编写(系统平移/竞品借鉴时激活)
|
||||||
- `req-prototype` — UI 原型生成
|
- `req-prototype` — UI 原型生成
|
||||||
|
- `defect-analysis` — 设计访谈确认后,对最新版 PRD 反复审计和修订直至收敛
|
||||||
|
|
||||||
|
## 模块设计访谈模式
|
||||||
|
|
||||||
|
设计模块、系统、跨域流程,或目标/边界/业务规则尚未确定时,必须先执行问答式设计访谈,不得直接补全假设后生成 PRD。用户明确要求“你问我答”时也进入此模式。范围小、规则已完整确认的需求可以直接编写 PRD。
|
||||||
|
|
||||||
|
进入此模式后,**完整读取并执行** [references/design-interview-and-defect-loop.md](references/design-interview-and-defect-loop.md)。该协议定义:
|
||||||
|
|
||||||
|
- 每轮只问一个会改变方案的关键问题;
|
||||||
|
- 将问题、AI 建议、用户原话、决策和未决项逐轮写入 ai-proj 需求的讨论文档;
|
||||||
|
- 讨论结论经用户确认后,才能创建或更新 PRD;
|
||||||
|
- 使用 `defect-analysis` 对最新版 PRD 执行“审计 → 修订 → 全量重审”循环;
|
||||||
|
- 讨论文档缺失、写入失败、用户未确认,或仍有未处置的致命/高严重度缺陷时,不得宣称设计完成或提交评审。
|
||||||
|
|
||||||
## 客户原话原则(REQ-20260416-0017 P1-8)
|
## 客户原话原则(REQ-20260416-0017 P1-8)
|
||||||
|
|
||||||
@@ -395,6 +408,8 @@ mcp__ai-proj__export_task_document_to_file
|
|||||||
- 需求池(ai-proj 需求列表)
|
- 需求池(ai-proj 需求列表)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
若命中“模块设计访谈模式”,本阶段改为执行访谈协议并持续写入 ai-proj 讨论文档;访谈未确认前不进入 PRD 定稿。
|
||||||
|
|
||||||
### 2. 需求分析
|
### 2. 需求分析
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -436,6 +451,8 @@ mcp__ai-proj__export_task_document_to_file
|
|||||||
- 修改意见
|
- 修改意见
|
||||||
```
|
```
|
||||||
|
|
||||||
|
模块设计访谈模式下,本阶段必须调用 `defect-analysis`,并按访谈协议将每轮发现、处置、PRD 修订和收敛结论回写到同一讨论文档。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 竞品分析
|
## 竞品分析
|
||||||
@@ -480,6 +497,10 @@ mcp__ai-proj__export_task_document_to_file
|
|||||||
|
|
||||||
### PRD 完整性检查
|
### PRD 完整性检查
|
||||||
|
|
||||||
|
- [ ] 模块/系统设计已完成单轮单问访谈,且全过程已写入 ai-proj 讨论文档
|
||||||
|
- [ ] 讨论结论已由用户明确确认
|
||||||
|
- [ ] `defect-analysis` 已基于最新版 PRD 收敛到一轮 0 个新缺陷
|
||||||
|
- [ ] 无未处置的致命/高严重度缺陷
|
||||||
- [ ] 背景与目标明确
|
- [ ] 背景与目标明确
|
||||||
- [ ] 用户群体定义清晰
|
- [ ] 用户群体定义清晰
|
||||||
- [ ] 功能需求完整
|
- [ ] 功能需求完整
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# 模块设计访谈与缺陷收敛协议
|
||||||
|
|
||||||
|
本协议用于模块、系统、跨域流程等需要先澄清关键产品决策的设计任务。目标是让设计依据可追溯,并让 PRD 在提交评审前经过可验证的缺陷收敛。
|
||||||
|
|
||||||
|
## 1. 进入与退出条件
|
||||||
|
|
||||||
|
满足任一条件时进入访谈模式:
|
||||||
|
|
||||||
|
- 用户明确要求“你问我答”、逐项讨论或产品访谈;
|
||||||
|
- 设计对象是模块、系统、跨域流程或涉及多个角色/组织;
|
||||||
|
- 目标、范围、数据归属、权限、状态流转、冲突优先级、异常策略中存在关键未决项。
|
||||||
|
|
||||||
|
需求范围小、上述决策均已明确时,可以直接编写 PRD。不要为了流程而重复询问用户已经回答的问题。
|
||||||
|
|
||||||
|
访谈模式只有同时满足以下条件才可结束:
|
||||||
|
|
||||||
|
1. 未决问题已清零,或明确列为非目标/后续项;
|
||||||
|
2. AI 已给出结构化讨论结论;
|
||||||
|
3. 用户明确确认讨论结论;
|
||||||
|
4. PRD 已按结论创建或更新;
|
||||||
|
5. `defect-analysis` 已对最新版 PRD 收敛;
|
||||||
|
6. 用户确认收敛后的最终方案。
|
||||||
|
|
||||||
|
## 2. 讨论文档是跨轮次事实源
|
||||||
|
|
||||||
|
当当前工作已有 ai-proj Requirement 时,在提第一个问题前查找其已关联的讨论文档;没有时创建一个 documentation 角色的关联任务,并为任务创建文档:
|
||||||
|
|
||||||
|
- 任务标题:`【讨论】需求讨论: {需求标题}`
|
||||||
|
- 文档标题:`{REQ-ID} 需求讨论记录`
|
||||||
|
- 一个 Requirement 只维护一个当前讨论文档,不因会话中断重复创建。
|
||||||
|
|
||||||
|
查找时同时核对 Requirement 关联关系、任务角色和标题,不能只按相似标题猜测。发现多个候选讨论文档时,列出标识和最近更新时间,请用户指定或授权合并;在此之前不得静默选择其中一个继续写入。
|
||||||
|
|
||||||
|
初始化文档时,先把触发本次设计的用户原始消息按时间和消息边界逐条写入“原始诉求”,再记录 `Q001`。不得只留下 AI 总结而丢失原始上下文。
|
||||||
|
|
||||||
|
“完整过程”指可供产品决策审计的模型可见内容:用户原话、AI 向用户展示的问题/建议/权衡、工具写入结果、确认、决策变更、PRD 修订和缺陷处置。不得记录或声称记录隐藏推理、系统提示、访问凭据及其他不可披露的内部信息。
|
||||||
|
|
||||||
|
若尚未指定 Requirement,先请用户提供已有 Requirement,或明确授权创建。取得 Requirement 和讨论文档前不得开始声称“已留痕”的正式访谈;不得仅为执行本技能本身擅自创建 Requirement。用户明确要求“创建需求并设计”才构成创建授权。
|
||||||
|
|
||||||
|
每次恢复会话时,先读取 Requirement、现有 PRD 和讨论文档,从最后一个未决问题继续。讨论文档是跨会话、上下文压缩后的权威记录;模型记忆不能覆盖文档中的用户原话和已确认决策。
|
||||||
|
|
||||||
|
### 写入纪律
|
||||||
|
|
||||||
|
- 提问时先写入问题原文、问题意图和 AI 建议,再向用户提问。
|
||||||
|
- 收到回答后,先把用户原话和由此形成的决策写入,再提出下一问。
|
||||||
|
- 每轮使用稳定编号 `Q001`、`Q002`……;重试写入时复用编号,禁止重复追加同一轮。
|
||||||
|
- AI 提供推荐方案或选项时,为待确认内容写出明确编号或完整原文。用户仅回复“确认”“是”“前者”等短答案时,最终决策必须引用对应编号和被确认的完整内容,不能只记录孤立短词。
|
||||||
|
- 用户原话逐字保留在引用块中;AI 的解释、推论和建议必须分栏,不能伪装成用户决定。访问令牌、密码、私钥及依法需要保护的个人敏感信息不得落库,用 `[敏感信息已脱敏]` 替代并注明脱敏原因。
|
||||||
|
- 文档以追加式记录为主。状态为“待回答”的问题块可以在收到回答后原位补全一次;变为“已确认”后不得静默改写。纠正已确认结论时追加“决策变更”,并引用被替代的编号。
|
||||||
|
- 更新整篇文档前重新读取最新版并保留未知内容;若读取后文档又发生变化,基于最新版合并后重试,不能用旧副本覆盖其他会话的记录。
|
||||||
|
- 写入后读取文档确认本轮编号和正文存在。写入或校验失败时立即报告,停止进入下一轮,且不得声称“已记录”。
|
||||||
|
- Requirement 描述只同步用户确认后的“讨论结论”摘要;完整过程保留在讨论文档中。
|
||||||
|
|
||||||
|
## 3. 单轮单问访谈
|
||||||
|
|
||||||
|
每轮只问一个会实质改变产品方案的问题。优先按依赖关系覆盖以下决策面,而不是机械地逐项提问:
|
||||||
|
|
||||||
|
1. 用户问题、目标与成功指标;
|
||||||
|
2. 角色、主体和数据归属;
|
||||||
|
3. 范围、非目标及版本边界;
|
||||||
|
4. 实体关系与基数,例如一对一、一对多、多对多;
|
||||||
|
5. 权限来源、授权人和信任边界;
|
||||||
|
6. 创建、加入、变更、退出、撤销等状态与生命周期;
|
||||||
|
7. 多来源冲突时的优先级和人工覆盖规则;
|
||||||
|
8. 失败、超时、失联、重复请求和恢复策略;
|
||||||
|
9. 兼容、迁移、审计、数据隔离和验收方式。
|
||||||
|
|
||||||
|
问题应让用户做产品决策,不要求用户代替 AI 设计实现细节。用户让 AI 建议时,先给出一个推荐方案和主要权衡,再请用户确认或修正。若回答引入新的实体、状态或例外规则,沿其影响继续追问;若答案已能从用户原话或现有文档确定,则直接记录,不重复确认。
|
||||||
|
|
||||||
|
每轮记录以下内容:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Q001 · {决策主题}
|
||||||
|
|
||||||
|
- 状态:待回答 | 已确认 | 已替代
|
||||||
|
- AI 问题(原文):...
|
||||||
|
- 提问意图:这个答案会影响哪些设计部分
|
||||||
|
- AI 建议与权衡:推荐方案、替代方案及主要代价
|
||||||
|
- 待确认内容:方案/选项编号及完整表述
|
||||||
|
- 用户回答(原文):
|
||||||
|
> ...
|
||||||
|
- 最终决策:只写由用户回答直接支持的结论
|
||||||
|
- 影响范围:PRD 章节、实体、流程、权限或验收标准
|
||||||
|
- 未决项:无 | 下一步待确认内容
|
||||||
|
- 记录时间:ISO 8601 时间
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 方案确认闸门
|
||||||
|
|
||||||
|
问题收敛后,在讨论文档追加“当前决策快照”,至少包含:
|
||||||
|
|
||||||
|
- 目标与成功条件;
|
||||||
|
- 用户/角色及核心场景;
|
||||||
|
- 范围与非目标;
|
||||||
|
- 核心实体、关系和数据归属;
|
||||||
|
- 权限、状态流转和冲突规则;
|
||||||
|
- 异常、降级、撤销和审计;
|
||||||
|
- 版本边界与后续项;
|
||||||
|
- 可验证的验收标准草案;
|
||||||
|
- 尚存风险和假设。
|
||||||
|
|
||||||
|
然后向用户展示同一份摘要并询问是否确认。只有用户明确表示确认,才能:
|
||||||
|
|
||||||
|
1. 将摘要同步到 Requirement 描述的 `## 讨论结论`;
|
||||||
|
2. 创建或更新 PRD;
|
||||||
|
3. 进入缺陷收敛循环。
|
||||||
|
|
||||||
|
每一步完成后重新读取目标对象确认写入成功。讨论结论未同步成功时不得开始写 PRD;PRD 未写入或读取到的内容与本次版本不一致时不得开始缺陷审计。
|
||||||
|
|
||||||
|
若用户修改任何结论,记录为新的问答或“决策变更”,更新快照后重新确认。
|
||||||
|
|
||||||
|
## 5. PRD 与 defect-analysis 收敛循环
|
||||||
|
|
||||||
|
讨论确认后,先按 `req-prd` 的完整模板生成或更新 PRD,再完整读取并调用 `defect-analysis`。每次审计都以**当前最新版完整 PRD**、已确认讨论记录和必要的真实代码/数据契约为输入,不能只审查上轮改动片段。首次基线审计必须覆盖 `defect-analysis` 中所有适用的架构、运行时、数据和体验/维护维度;不能因为尚未覆盖其他维度时某个单独维度为 0 个新缺陷而提前结束基线。
|
||||||
|
|
||||||
|
循环执行:
|
||||||
|
|
||||||
|
1. `defect-analysis` 检查最新版 PRD,并按其规则输出带严重度和轮次的缺陷;
|
||||||
|
2. 将本轮输入版本、检查维度、完整发现和证据写入讨论文档;PRD 输入版本至少包含文档/任务标识、更新时间和内容摘要或哈希,避免审计结果关联到错误版本;审计轮使用稳定编号 `Round 001`、`Round 002`……,重试不得重复计轮;
|
||||||
|
3. 对每个发现标记处置:接受、误报、延后;
|
||||||
|
4. 接受的产品缺陷必须修订 PRD,并同步影响到验收标准、风险、非目标或版本边界;纯技术实现发现若不改变产品行为,记录为后续 `req-design` 约束或开发风险,不向 PRD 填入未经验证的实现细节;
|
||||||
|
5. 误报必须记录反证;延后必须记录原因、风险、负责人或后续需求,不得静默忽略;
|
||||||
|
6. 记录 PRD 修改摘要和仍未解决的问题,重新读取 PRD 确认修订已经持久化;
|
||||||
|
7. 对修改后的完整 PRD 重新调用 `defect-analysis`。
|
||||||
|
|
||||||
|
若某个修复会改变已确认的目标、范围、实体关系、权限、用户流程、冲突规则或验收口径,不能由 AI 静默应用。将它追加为新的问答或“决策变更”,说明缺陷证据、推荐方案和代价,获得用户确认并更新决策快照后,再修订 PRD;随后重新开始最新版 PRD 的全量审计。
|
||||||
|
|
||||||
|
完成全维度基线后,只有 `defect-analysis` 对最新版完整 PRD 出现一轮“0 个新缺陷”时才能标记收敛。达到 20 轮仍有新发现只是阶段复盘点:汇总剩余风险并请求用户决定是否继续;不得把“达到轮数”写成“已收敛”。用户已明确要求持续审计时,按该技能规则继续下一阶段。
|
||||||
|
|
||||||
|
存在以下任一情况时,不得提交 PRD 评审或宣称完成:
|
||||||
|
|
||||||
|
- 未处置的致命或高严重度缺陷;
|
||||||
|
- 讨论文档缺失或有未成功写入的轮次;
|
||||||
|
- PRD 与已确认决策不一致;
|
||||||
|
- 缺少 0 新增缺陷的收敛轮;
|
||||||
|
- 用户尚未确认收敛后的最终方案。
|
||||||
|
|
||||||
|
## 6. 讨论文档结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {REQ-ID} 需求讨论记录
|
||||||
|
|
||||||
|
## 元数据
|
||||||
|
- Requirement:...
|
||||||
|
- 状态:访谈中 | 待方案确认 | PRD 优化中 | 待最终确认 | 已收敛
|
||||||
|
- 最新 PRD:任务/文档标识
|
||||||
|
- 更新时间:...
|
||||||
|
|
||||||
|
## 原始诉求
|
||||||
|
> 用户原话,按时间追加
|
||||||
|
|
||||||
|
## 问答记录
|
||||||
|
### Q001 · ...
|
||||||
|
...
|
||||||
|
|
||||||
|
## 决策变更
|
||||||
|
### D001 · 替代 Qxxx 的结论
|
||||||
|
...
|
||||||
|
|
||||||
|
## 当前决策快照
|
||||||
|
...
|
||||||
|
|
||||||
|
## 未决问题
|
||||||
|
- ...
|
||||||
|
|
||||||
|
## 方案确认
|
||||||
|
- 用户确认原话:...
|
||||||
|
- 确认时间:...
|
||||||
|
|
||||||
|
## PRD / 缺陷优化记录
|
||||||
|
### Round 1 · {检查维度}
|
||||||
|
- PRD 版本/摘要:...
|
||||||
|
- 新缺陷:...
|
||||||
|
- 处置与证据:...
|
||||||
|
- PRD 修订:...
|
||||||
|
- 剩余风险:...
|
||||||
|
|
||||||
|
## 收敛结论
|
||||||
|
- 收敛轮次:...
|
||||||
|
- 0 新增缺陷证据:...
|
||||||
|
- 未解决的中/低风险及接受理由:...
|
||||||
|
- 用户最终确认原话:...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 最终交付说明
|
||||||
|
|
||||||
|
最终回复必须同时给出:
|
||||||
|
|
||||||
|
- ai-proj Requirement 标识;
|
||||||
|
- 讨论任务/文档标识;
|
||||||
|
- PRD 任务/文档标识;
|
||||||
|
- 问答轮数、缺陷审计轮数和收敛轮;
|
||||||
|
- 仍被接受的中/低风险;
|
||||||
|
- 用户两次确认:讨论方案确认、收敛后最终确认。
|
||||||
|
|
||||||
|
任何标识或写入状态无法验证时,用“未验证/未写入”如实标注。
|
||||||
@@ -13,10 +13,10 @@ mkdir -p "$TEST_HOME"
|
|||||||
cp "$PROJECT_DIR/install-skills.sh" "$FIXTURE_REPO/install-skills.sh"
|
cp "$PROJECT_DIR/install-skills.sh" "$FIXTURE_REPO/install-skills.sh"
|
||||||
|
|
||||||
write_manifest() {
|
write_manifest() {
|
||||||
local plugin="$1" name="$2" version="$3"
|
local plugin="$1" name="$2" version="$3" install_type="${4:-skill}"
|
||||||
mkdir -p "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin"
|
mkdir -p "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin"
|
||||||
cat > "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin/plugin.json" <<JSON
|
cat > "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin/plugin.json" <<JSON
|
||||||
{"name":"${plugin}-plugin","version":"${version}","install_name":"${name}","install_type":"skill","dir_category":"dev"}
|
{"name":"${plugin}-plugin","version":"${version}","install_name":"${name}","install_type":"${install_type}","dir_category":"dev"}
|
||||||
JSON
|
JSON
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ EOF
|
|||||||
printf 'reference one\n' > "$FIXTURE_REPO/skills-dev/example-plugin/skills/references/guide.md"
|
printf 'reference one\n' > "$FIXTURE_REPO/skills-dev/example-plugin/skills/references/guide.md"
|
||||||
|
|
||||||
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||||
test -f "$TEST_HOME/.claude/skills/example/references/guide.md"
|
test -f "$TEST_HOME/.agents/skills/example/references/guide.md"
|
||||||
|
|
||||||
# A repository version upgrade replaces an unchanged prior install.
|
# A repository version upgrade replaces an unchanged prior install.
|
||||||
write_manifest example example 2.0.0
|
write_manifest example example 2.0.0
|
||||||
@@ -43,17 +43,17 @@ description: Installer fixture version two.
|
|||||||
version two
|
version two
|
||||||
EOF
|
EOF
|
||||||
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||||
grep -q 'version two' "$TEST_HOME/.claude/skills/example/SKILL.md"
|
grep -q 'version two' "$TEST_HOME/.agents/skills/example/SKILL.md"
|
||||||
grep -q '"version": "2.0.0"' "$TEST_HOME/.claude/.installed-skills.json"
|
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.
|
# A local edit is preserved even when the repository advances again.
|
||||||
printf 'local edit\n' >> "$TEST_HOME/.claude/skills/example/SKILL.md"
|
printf 'local edit\n' >> "$TEST_HOME/.agents/skills/example/SKILL.md"
|
||||||
write_manifest example example 3.0.0
|
write_manifest example example 3.0.0
|
||||||
printf 'repository version three\n' >> "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md"
|
printf 'repository version three\n' >> "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md"
|
||||||
output="$(HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh")"
|
output="$(HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh")"
|
||||||
grep -q 'local files were modified' <<<"$output"
|
grep -q 'local files were modified' <<<"$output"
|
||||||
grep -q 'local edit' "$TEST_HOME/.claude/skills/example/SKILL.md"
|
grep -q 'local edit' "$TEST_HOME/.agents/skills/example/SKILL.md"
|
||||||
if grep -q 'repository version three' "$TEST_HOME/.claude/skills/example/SKILL.md"; then
|
if grep -q 'repository version three' "$TEST_HOME/.agents/skills/example/SKILL.md"; then
|
||||||
echo 'local modification was overwritten' >&2
|
echo 'local modification was overwritten' >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -70,9 +70,45 @@ description: Legacy installation fixture.
|
|||||||
legacy content
|
legacy content
|
||||||
EOF
|
EOF
|
||||||
printf 'legacy reference\n' > "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/references/guide.md"
|
printf 'legacy reference\n' > "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/references/guide.md"
|
||||||
mkdir -p "$TEST_HOME/.claude/skills/legacy"
|
mkdir -p "$TEST_HOME/.agents/skills/legacy"
|
||||||
cp "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/SKILL.md" "$TEST_HOME/.claude/skills/legacy/SKILL.md"
|
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
|
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
|
||||||
test -f "$TEST_HOME/.claude/skills/legacy/references/guide.md"
|
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'
|
echo 'install-skills tests passed'
|
||||||
|
|||||||
Reference in New Issue
Block a user