Compare commits
No commits in common. "4475bdbde204541a8aa4348530ad99acea5acc73" and "7db5e4a697e682ee990979e873cf92b4f89e1ea3" have entirely different histories.
4475bdbde2
...
7db5e4a697
|
|
@ -69,16 +69,6 @@
|
||||||
"source_list": "skills/thirdparty/.sources/codebase-migrate.list",
|
"source_list": "skills/thirdparty/.sources/codebase-migrate.list",
|
||||||
"skills_subdir": ".",
|
"skills_subdir": ".",
|
||||||
"include_skill_dirs": ["codebase-migrate"]
|
"include_skill_dirs": ["codebase-migrate"]
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "uncle-bob-craft",
|
|
||||||
"upstream_repo": "https://github.com/sickn33/antigravity-awesome-skills.git",
|
|
||||||
"upstream_ref": "main",
|
|
||||||
"snapshot_dir": "antigravity-awesome-skills",
|
|
||||||
"sync_mode": "copy_skill_dirs",
|
|
||||||
"source_list": "skills/thirdparty/.sources/uncle-bob-craft.list",
|
|
||||||
"skills_subdir": "skills",
|
|
||||||
"include_skill_dirs": ["uncle-bob-craft"]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -186,15 +186,11 @@ python <deploy_root>/scripts/playbook.py -config playbook.toml
|
||||||
- `superpowers.list`
|
- `superpowers.list`
|
||||||
- `ui-ux-pro-max.list`
|
- `ui-ux-pro-max.list`
|
||||||
- `andrej-karpathy-skills.list`
|
- `andrej-karpathy-skills.list`
|
||||||
- `brooks-lint.list`
|
|
||||||
- `codebase-recon.list`
|
|
||||||
- `codebase-migrate.list`
|
|
||||||
- `uncle-bob-craft.list`
|
|
||||||
|
|
||||||
部署链路:
|
部署链路:
|
||||||
|
|
||||||
- `thirdparty/skill` 分支保存各上游快照,例如 `andrej-karpathy-skills/`、`awesome-codex-skills/`、`antigravity-awesome-skills/`
|
- `thirdparty/skill` 分支保存上游快照 `andrej-karpathy-skills/`
|
||||||
- 自动同步后,选中的下游 skill 会落到仓库内 `skills/thirdparty/<skill-name>/`
|
- 自动同步后,`karpathy-guidelines/` 会落到仓库内 `skills/thirdparty/karpathy-guidelines/`
|
||||||
- 运行 `[install_skills]` 时,再复制到目标平台 skills 目录(`~/.agents/skills/` 或 `~/.claude/skills/`)
|
- 运行 `[install_skills]` 时,再复制到目标平台 skills 目录(`~/.agents/skills/` 或 `~/.claude/skills/`)
|
||||||
- 该 skill 本身不依赖 Playbook 文档路径重写,也不需要像 `ui-ux-pro-max` 那样额外渲染
|
- 该 skill 本身不依赖 Playbook 文档路径重写,也不需要像 `ui-ux-pro-max` 那样额外渲染
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -760,10 +760,6 @@ _CLAUDE_BLOCK_END = "<!-- playbook:claude:end -->"
|
||||||
_CLAUDE_MD_CANDIDATES = ["CLAUDE.md", ".claude/CLAUDE.md"]
|
_CLAUDE_MD_CANDIDATES = ["CLAUDE.md", ".claude/CLAUDE.md"]
|
||||||
|
|
||||||
|
|
||||||
def _claude_block_needs_heading(text: str) -> bool:
|
|
||||||
return text.lstrip().startswith(_CLAUDE_BLOCK_START)
|
|
||||||
|
|
||||||
|
|
||||||
def sync_claude_md(project_root: Path, config: dict) -> None:
|
def sync_claude_md(project_root: Path, config: dict) -> None:
|
||||||
claude_md_config = config.get("playbook", {}).get("claude_md")
|
claude_md_config = config.get("playbook", {}).get("claude_md")
|
||||||
|
|
||||||
|
|
@ -800,9 +796,7 @@ def sync_claude_md(project_root: Path, config: dict) -> None:
|
||||||
if not claude_md.exists():
|
if not claude_md.exists():
|
||||||
ensure_dir(claude_md.parent)
|
ensure_dir(claude_md.parent)
|
||||||
claude_md.write_text(
|
claude_md.write_text(
|
||||||
"# CLAUDE.md\n\n" + "\n".join(block_lines) + "\n",
|
"\n".join(block_lines) + "\n", encoding="utf-8", newline="\n"
|
||||||
encoding="utf-8",
|
|
||||||
newline="\n",
|
|
||||||
)
|
)
|
||||||
log(f"Created {claude_md.relative_to(project_root)} with playbook block.")
|
log(f"Created {claude_md.relative_to(project_root)} with playbook block.")
|
||||||
return
|
return
|
||||||
|
|
@ -825,10 +819,9 @@ def sync_claude_md(project_root: Path, config: dict) -> None:
|
||||||
in_block = False
|
in_block = False
|
||||||
continue
|
continue
|
||||||
updated.append(line)
|
updated.append(line)
|
||||||
updated_text = "\n".join(updated) + "\n"
|
claude_md.write_text(
|
||||||
if _claude_block_needs_heading(updated_text):
|
"\n".join(updated) + "\n", encoding="utf-8", newline="\n"
|
||||||
updated_text = "# CLAUDE.md\n\n" + updated_text.lstrip()
|
)
|
||||||
claude_md.write_text(updated_text, encoding="utf-8", newline="\n")
|
|
||||||
log("Updated CLAUDE.md (playbook block).")
|
log("Updated CLAUDE.md (playbook block).")
|
||||||
elif "@AGENTS.md" in text:
|
elif "@AGENTS.md" in text:
|
||||||
log("Skip: CLAUDE.md already references AGENTS.md")
|
log("Skip: CLAUDE.md already references AGENTS.md")
|
||||||
|
|
|
||||||
|
|
@ -79,22 +79,3 @@
|
||||||
| Skill | 来源 | 作用 |
|
| Skill | 来源 | 作用 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `codebase-migrate` | `awesome-codex-skills` | 大代码库迁移、多文件 refactor、分批变更与 CI 验证工作流 |
|
| `codebase-migrate` | `awesome-codex-skills` | 大代码库迁移、多文件 refactor、分批变更与 CI 验证工作流 |
|
||||||
| `uncle-bob-craft` | `antigravity-awesome-skills` | Clean Architecture、SOLID、设计模式误用和代码工匠实践审查 |
|
|
||||||
|
|
||||||
#### `uncle-bob-craft` 定位
|
|
||||||
|
|
||||||
`uncle-bob-craft` 是原则型补强 skill,不替代现有 `brooks-*` 或
|
|
||||||
`codebase-*` 主流程。它适合在问题明确聚焦 Clean Architecture、SOLID、
|
|
||||||
职责拆分、依赖方向、设计模式是否滥用、代码工匠实践时使用。
|
|
||||||
|
|
||||||
| 对比项 | 分工 |
|
|
||||||
| --- | --- |
|
|
||||||
| `codebase-recon` | 负责重构前侦察、热点分析和影响面判断;`uncle-bob-craft` 不负责代码库级侦察 |
|
|
||||||
| `brooks-audit` | 负责架构边界、模块职责和长期维护性审查;`uncle-bob-craft` 只作为 Clean Architecture / SOLID 视角补强 |
|
|
||||||
| `codebase-migrate` | 负责大规模迁移、多文件 refactor 和 CI 验证节奏;`uncle-bob-craft` 不负责迁移编排 |
|
|
||||||
| `brooks-review` | 负责 PR / diff 级代码审查;`uncle-bob-craft` 仅在审查重点是 SOLID、职责拆分或设计模式误用时配合使用 |
|
|
||||||
| `brooks-test` | 负责测试质量、断言边界和测试脆弱性审查;`uncle-bob-craft` 不负责测试质量专项审查 |
|
|
||||||
|
|
||||||
推荐触发方式:有明确 Clean Architecture、SOLID、Uncle Bob、职责拆分、
|
|
||||||
依赖方向或设计模式误用问题时使用;普通 PR 审查仍优先 `brooks-review`,
|
|
||||||
架构级审查仍优先 `brooks-audit`。
|
|
||||||
|
|
|
||||||
|
|
@ -57,20 +57,3 @@ skills:
|
||||||
- CI-verified migration workflows
|
- CI-verified migration workflows
|
||||||
notes:
|
notes:
|
||||||
- Sourced from awesome-codex-skills.
|
- Sourced from awesome-codex-skills.
|
||||||
|
|
||||||
- id: uncle-bob-craft
|
|
||||||
sync: enabled
|
|
||||||
upstream_repo: https://github.com/sickn33/antigravity-awesome-skills
|
|
||||||
upstream_ref: main
|
|
||||||
upstream_path: skills/uncle-bob-craft
|
|
||||||
upstream_layout: single_skill_from_suite
|
|
||||||
selected_for:
|
|
||||||
- Clean Architecture review
|
|
||||||
- SOLID review
|
|
||||||
- design-pattern misuse checks
|
|
||||||
- architecture-boundary refactoring
|
|
||||||
- code craftsmanship review
|
|
||||||
playbook_fit: architecture and code-quality craft review support
|
|
||||||
notes:
|
|
||||||
- Upstream skill includes references for Clean Architecture, Clean Coder, Clean Agile, and design-pattern discipline.
|
|
||||||
- Registered as a single selected skill from a larger community skill suite.
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import json
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
@ -382,34 +380,6 @@ skills = ["brainstorming"]
|
||||||
)
|
)
|
||||||
self.assertEqual(clone_mirror.returncode, 0, msg=clone_mirror.stderr)
|
self.assertEqual(clone_mirror.returncode, 0, msg=clone_mirror.stderr)
|
||||||
|
|
||||||
thirdparty_ref = subprocess.run(
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
f"--git-dir={mirror}",
|
|
||||||
"rev-parse",
|
|
||||||
"refs/remotes/origin/thirdparty/skill",
|
|
||||||
],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
self.assertEqual(thirdparty_ref.returncode, 0, msg=thirdparty_ref.stderr)
|
|
||||||
expose_thirdparty_branch = subprocess.run(
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
f"--git-dir={mirror}",
|
|
||||||
"update-ref",
|
|
||||||
"refs/heads/thirdparty/skill",
|
|
||||||
thirdparty_ref.stdout.strip(),
|
|
||||||
],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
expose_thirdparty_branch.returncode,
|
|
||||||
0,
|
|
||||||
msg=expose_thirdparty_branch.stderr,
|
|
||||||
)
|
|
||||||
|
|
||||||
clone_repo = subprocess.run(
|
clone_repo = subprocess.run(
|
||||||
["git", "clone", str(mirror), str(repo)],
|
["git", "clone", str(mirror), str(repo)],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
|
|
@ -426,19 +396,10 @@ skills = ["brainstorming"]
|
||||||
|
|
||||||
manifest_src = ROOT / ".gitea" / "ci" / "thirdparty_skills.json"
|
manifest_src = ROOT / ".gitea" / "ci" / "thirdparty_skills.json"
|
||||||
manifest_dst = repo / ".gitea" / "ci" / "thirdparty_skills.json"
|
manifest_dst = repo / ".gitea" / "ci" / "thirdparty_skills.json"
|
||||||
manifest_data = json.loads(manifest_src.read_text(encoding="utf-8"))
|
manifest_dst.write_text(manifest_src.read_text(encoding="utf-8"), encoding="utf-8")
|
||||||
manifest_data["sources"] = [
|
|
||||||
entry
|
|
||||||
for entry in manifest_data["sources"]
|
|
||||||
if entry["id"] == "andrej-karpathy-skills"
|
|
||||||
]
|
|
||||||
manifest_dst.write_text(
|
|
||||||
json.dumps(manifest_data, indent=2) + "\n",
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
sync_src = ROOT / ".gitea" / "ci" / "sync_thirdparty_skills.sh"
|
sync_src = ROOT / ".gitea" / "ci" / "sync_thirdparty_skills.sh"
|
||||||
sync_dst = repo / ".gitea" / "ci" / "sync_thirdparty_skills.sh"
|
sync_dst = repo / ".gitea" / "ci" / "sync_thirdparty_skills.sh"
|
||||||
shutil.copy2(sync_src, sync_dst)
|
sync_dst.write_text(sync_src.read_text(encoding="utf-8"), encoding="utf-8")
|
||||||
|
|
||||||
sync_result = subprocess.run(
|
sync_result = subprocess.run(
|
||||||
["bash", ".gitea/ci/sync_thirdparty_skills.sh"],
|
["bash", ".gitea/ci/sync_thirdparty_skills.sh"],
|
||||||
|
|
@ -607,7 +568,6 @@ project_name = "Demo"
|
||||||
claude_md = Path(tmp_dir) / "CLAUDE.md"
|
claude_md = Path(tmp_dir) / "CLAUDE.md"
|
||||||
self.assertTrue(claude_md.exists())
|
self.assertTrue(claude_md.exists())
|
||||||
text = claude_md.read_text(encoding="utf-8")
|
text = claude_md.read_text(encoding="utf-8")
|
||||||
self.assertTrue(text.startswith("# CLAUDE.md\n\n"))
|
|
||||||
self.assertIn("@AGENTS.md", text)
|
self.assertIn("@AGENTS.md", text)
|
||||||
self.assertIn("<!-- playbook:claude:start -->", text)
|
self.assertIn("<!-- playbook:claude:start -->", text)
|
||||||
|
|
||||||
|
|
@ -666,39 +626,6 @@ project_name = "Demo"
|
||||||
self.assertIn("@AGENT_RULES.md", text)
|
self.assertIn("@AGENT_RULES.md", text)
|
||||||
self.assertEqual(text.count("<!-- playbook:claude:start -->"), 1)
|
self.assertEqual(text.count("<!-- playbook:claude:start -->"), 1)
|
||||||
|
|
||||||
def test_sync_claude_md_adds_heading_to_generated_block(self):
|
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
||||||
claude_md = Path(tmp_dir) / "CLAUDE.md"
|
|
||||||
claude_md.write_text(
|
|
||||||
"<!-- playbook:claude:start -->\n"
|
|
||||||
"\n"
|
|
||||||
"@AGENTS.md\n"
|
|
||||||
"@AGENT_RULES.md\n"
|
|
||||||
"\n"
|
|
||||||
"<!-- playbook:claude:end -->\n",
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
config_body = f"""
|
|
||||||
[playbook]
|
|
||||||
project_root = "{tmp_dir}"
|
|
||||||
deploy_root = "{CUSTOM_DEPLOY_ROOT}"
|
|
||||||
|
|
||||||
[sync_memory_bank]
|
|
||||||
project_name = "Demo"
|
|
||||||
"""
|
|
||||||
config_path = Path(tmp_dir) / "playbook.toml"
|
|
||||||
config_path.write_text(config_body, encoding="utf-8")
|
|
||||||
|
|
||||||
result = run_cli("-config", str(config_path))
|
|
||||||
|
|
||||||
self.assertEqual(result.returncode, 0)
|
|
||||||
text = claude_md.read_text(encoding="utf-8")
|
|
||||||
self.assertTrue(text.startswith("# CLAUDE.md\n\n"))
|
|
||||||
self.assertIn("@AGENTS.md", text)
|
|
||||||
self.assertIn("@AGENT_RULES.md", text)
|
|
||||||
self.assertEqual(text.count("<!-- playbook:claude:start -->"), 1)
|
|
||||||
|
|
||||||
def test_sync_claude_md_skips_when_already_references_agents(self):
|
def test_sync_claude_md_skips_when_already_references_agents(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
claude_md = Path(tmp_dir) / "CLAUDE.md"
|
claude_md = Path(tmp_dir) / "CLAUDE.md"
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ class SkillsReadmeTests(unittest.TestCase):
|
||||||
self.assertIn("codebase-recon", text)
|
self.assertIn("codebase-recon", text)
|
||||||
self.assertIn("pathfinding", text)
|
self.assertIn("pathfinding", text)
|
||||||
self.assertIn("codebase-migrate", text)
|
self.assertIn("codebase-migrate", text)
|
||||||
self.assertIn("uncle-bob-craft", text)
|
|
||||||
self.assertIn("已登记待同步", text)
|
self.assertIn("已登记待同步", text)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,6 @@ class ThirdpartySkillCurationTests(unittest.TestCase):
|
||||||
self.assertIn("upstream_path: codebase-migrate", text)
|
self.assertIn("upstream_path: codebase-migrate", text)
|
||||||
self.assertIn("large codebase migrations", text)
|
self.assertIn("large codebase migrations", text)
|
||||||
|
|
||||||
self.assertIn("id: uncle-bob-craft", text)
|
|
||||||
self.assertIn("upstream_repo: https://github.com/sickn33/antigravity-awesome-skills", text)
|
|
||||||
self.assertIn("upstream_path: skills/uncle-bob-craft", text)
|
|
||||||
self.assertIn("design-pattern misuse checks", text)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
|
||||||
"brooks-lint",
|
"brooks-lint",
|
||||||
"codebase-recon",
|
"codebase-recon",
|
||||||
"codebase-migrate",
|
"codebase-migrate",
|
||||||
"uncle-bob-craft",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -116,22 +115,6 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(migrate["include_skill_dirs"], ["codebase-migrate"])
|
self.assertEqual(migrate["include_skill_dirs"], ["codebase-migrate"])
|
||||||
|
|
||||||
craft = next(
|
|
||||||
item for item in data["sources"] if item["id"] == "uncle-bob-craft"
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
craft["upstream_repo"],
|
|
||||||
"https://github.com/sickn33/antigravity-awesome-skills.git",
|
|
||||||
)
|
|
||||||
self.assertEqual(craft["upstream_ref"], "main")
|
|
||||||
self.assertEqual(craft["sync_mode"], "copy_skill_dirs")
|
|
||||||
self.assertEqual(craft["snapshot_dir"], "antigravity-awesome-skills")
|
|
||||||
self.assertEqual(craft["skills_subdir"], "skills")
|
|
||||||
self.assertEqual(
|
|
||||||
craft["source_list"], "skills/thirdparty/.sources/uncle-bob-craft.list"
|
|
||||||
)
|
|
||||||
self.assertEqual(craft["include_skill_dirs"], ["uncle-bob-craft"])
|
|
||||||
|
|
||||||
def test_superpowers_manifest_prunes_non_superpowers_paths(self):
|
def test_superpowers_manifest_prunes_non_superpowers_paths(self):
|
||||||
data = load_manifest()
|
data = load_manifest()
|
||||||
superpowers = next(item for item in data["sources"] if item["id"] == "superpowers")
|
superpowers = next(item for item in data["sources"] if item["id"] == "superpowers")
|
||||||
|
|
@ -226,26 +209,6 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
|
||||||
clone_mirror = run_command("git", "clone", "--mirror", str(ROOT), str(mirror))
|
clone_mirror = run_command("git", "clone", "--mirror", str(ROOT), str(mirror))
|
||||||
self.assertEqual(clone_mirror.returncode, 0, msg=clone_mirror.stderr)
|
self.assertEqual(clone_mirror.returncode, 0, msg=clone_mirror.stderr)
|
||||||
|
|
||||||
thirdparty_ref = run_command(
|
|
||||||
"git",
|
|
||||||
f"--git-dir={mirror}",
|
|
||||||
"rev-parse",
|
|
||||||
"refs/remotes/origin/thirdparty/skill",
|
|
||||||
)
|
|
||||||
self.assertEqual(thirdparty_ref.returncode, 0, msg=thirdparty_ref.stderr)
|
|
||||||
expose_thirdparty_branch = run_command(
|
|
||||||
"git",
|
|
||||||
f"--git-dir={mirror}",
|
|
||||||
"update-ref",
|
|
||||||
"refs/heads/thirdparty/skill",
|
|
||||||
thirdparty_ref.stdout.strip(),
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
expose_thirdparty_branch.returncode,
|
|
||||||
0,
|
|
||||||
msg=expose_thirdparty_branch.stderr,
|
|
||||||
)
|
|
||||||
|
|
||||||
clone_work = run_command("git", "clone", str(mirror), str(work))
|
clone_work = run_command("git", "clone", str(mirror), str(work))
|
||||||
self.assertEqual(clone_work.returncode, 0, msg=clone_work.stderr)
|
self.assertEqual(clone_work.returncode, 0, msg=clone_work.stderr)
|
||||||
|
|
||||||
|
|
@ -254,16 +217,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(set_remote.returncode, 0, msg=set_remote.stderr)
|
self.assertEqual(set_remote.returncode, 0, msg=set_remote.stderr)
|
||||||
|
|
||||||
manifest_data = load_manifest()
|
shutil.copy2(MANIFEST, work / ".gitea" / "ci" / "thirdparty_skills.json")
|
||||||
manifest_data["sources"] = [
|
|
||||||
entry
|
|
||||||
for entry in manifest_data["sources"]
|
|
||||||
if entry["id"] == "andrej-karpathy-skills"
|
|
||||||
]
|
|
||||||
(work / ".gitea" / "ci" / "thirdparty_skills.json").write_text(
|
|
||||||
json.dumps(manifest_data, indent=2) + "\n",
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
shutil.copy2(SYNC_SCRIPT, work / ".gitea" / "ci" / "sync_thirdparty_skills.sh")
|
shutil.copy2(SYNC_SCRIPT, work / ".gitea" / "ci" / "sync_thirdparty_skills.sh")
|
||||||
|
|
||||||
sync_result = run_command("bash", ".gitea/ci/sync_thirdparty_skills.sh", cwd=work)
|
sync_result = run_command("bash", ".gitea/ci/sync_thirdparty_skills.sh", cwd=work)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue