diff --git a/.gitea/ci/thirdparty_skills.json b/.gitea/ci/thirdparty_skills.json index c3b7a03d..f03a2fad 100644 --- a/.gitea/ci/thirdparty_skills.json +++ b/.gitea/ci/thirdparty_skills.json @@ -69,6 +69,16 @@ "source_list": "skills/thirdparty/.sources/codebase-migrate.list", "skills_subdir": ".", "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"] } ] } diff --git a/SKILLS.md b/SKILLS.md index b7666b2c..468dcdaa 100644 --- a/SKILLS.md +++ b/SKILLS.md @@ -186,11 +186,15 @@ python /scripts/playbook.py -config playbook.toml - `superpowers.list` - `ui-ux-pro-max.list` - `andrej-karpathy-skills.list` +- `brooks-lint.list` +- `codebase-recon.list` +- `codebase-migrate.list` +- `uncle-bob-craft.list` 部署链路: -- `thirdparty/skill` 分支保存上游快照 `andrej-karpathy-skills/` -- 自动同步后,`karpathy-guidelines/` 会落到仓库内 `skills/thirdparty/karpathy-guidelines/` +- `thirdparty/skill` 分支保存各上游快照,例如 `andrej-karpathy-skills/`、`awesome-codex-skills/`、`antigravity-awesome-skills/` +- 自动同步后,选中的下游 skill 会落到仓库内 `skills/thirdparty//` - 运行 `[install_skills]` 时,再复制到目标平台 skills 目录(`~/.agents/skills/` 或 `~/.claude/skills/`) - 该 skill 本身不依赖 Playbook 文档路径重写,也不需要像 `ui-ux-pro-max` 那样额外渲染 diff --git a/skills/README.md b/skills/README.md index c89d46f6..824cb7ac 100644 --- a/skills/README.md +++ b/skills/README.md @@ -79,3 +79,22 @@ | Skill | 来源 | 作用 | | --- | --- | --- | | `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`。 diff --git a/skills/thirdparty/thirdparty-skills.yml b/skills/thirdparty/thirdparty-skills.yml index 7b5626b2..de701b5d 100644 --- a/skills/thirdparty/thirdparty-skills.yml +++ b/skills/thirdparty/thirdparty-skills.yml @@ -57,3 +57,20 @@ skills: - CI-verified migration workflows notes: - 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. diff --git a/tests/cli/test_playbook_cli.py b/tests/cli/test_playbook_cli.py index 97fc6c8d..474273c5 100644 --- a/tests/cli/test_playbook_cli.py +++ b/tests/cli/test_playbook_cli.py @@ -1,3 +1,5 @@ +import json +import shutil import subprocess import sys import tempfile @@ -380,6 +382,34 @@ skills = ["brainstorming"] ) 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( ["git", "clone", str(mirror), str(repo)], capture_output=True, @@ -396,10 +426,19 @@ skills = ["brainstorming"] manifest_src = ROOT / ".gitea" / "ci" / "thirdparty_skills.json" manifest_dst = repo / ".gitea" / "ci" / "thirdparty_skills.json" - manifest_dst.write_text(manifest_src.read_text(encoding="utf-8"), encoding="utf-8") + manifest_data = json.loads(manifest_src.read_text(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_dst = repo / ".gitea" / "ci" / "sync_thirdparty_skills.sh" - sync_dst.write_text(sync_src.read_text(encoding="utf-8"), encoding="utf-8") + shutil.copy2(sync_src, sync_dst) sync_result = subprocess.run( ["bash", ".gitea/ci/sync_thirdparty_skills.sh"], diff --git a/tests/test_skills_readme.py b/tests/test_skills_readme.py index 129152a9..4cc31452 100644 --- a/tests/test_skills_readme.py +++ b/tests/test_skills_readme.py @@ -38,6 +38,7 @@ class SkillsReadmeTests(unittest.TestCase): self.assertIn("codebase-recon", text) self.assertIn("pathfinding", text) self.assertIn("codebase-migrate", text) + self.assertIn("uncle-bob-craft", text) self.assertIn("已登记待同步", text) diff --git a/tests/test_thirdparty_skill_curation.py b/tests/test_thirdparty_skill_curation.py index 60be3373..a98bd380 100644 --- a/tests/test_thirdparty_skill_curation.py +++ b/tests/test_thirdparty_skill_curation.py @@ -32,6 +32,11 @@ class ThirdpartySkillCurationTests(unittest.TestCase): self.assertIn("upstream_path: codebase-migrate", 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__": unittest.main() diff --git a/tests/test_thirdparty_skills_pipeline.py b/tests/test_thirdparty_skills_pipeline.py index c68660b8..0230eb7a 100644 --- a/tests/test_thirdparty_skills_pipeline.py +++ b/tests/test_thirdparty_skills_pipeline.py @@ -56,6 +56,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): "brooks-lint", "codebase-recon", "codebase-migrate", + "uncle-bob-craft", ], ) @@ -115,6 +116,22 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): ) 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): data = load_manifest() superpowers = next(item for item in data["sources"] if item["id"] == "superpowers") @@ -209,6 +226,26 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): clone_mirror = run_command("git", "clone", "--mirror", str(ROOT), str(mirror)) 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)) self.assertEqual(clone_work.returncode, 0, msg=clone_work.stderr) @@ -217,7 +254,16 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): ) self.assertEqual(set_remote.returncode, 0, msg=set_remote.stderr) - shutil.copy2(MANIFEST, work / ".gitea" / "ci" / "thirdparty_skills.json") + manifest_data = load_manifest() + 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") sync_result = run_command("bash", ".gitea/ci/sync_thirdparty_skills.sh", cwd=work)