Skip to content

Commit 10dccbc

Browse files
committed
Added skill validation
1 parent c4d34eb commit 10dccbc

3 files changed

Lines changed: 73 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
88

99
- Renamed the local OpenClaw skill package directory to `skills/` and updated live repository references, including
1010
the ClawHub upload script path.
11+
- Extended `./code-quality.sh` to validate and package every committed skill under `skills/` using the local
12+
`skill-creator` tooling before running the Node test suite.
1113

1214
## [0.8.0] - 2026-03-04
1315

code-quality.sh

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ echo "Running Code Quality Checks"
1515
echo "========================================="
1616
echo ""
1717

18-
echo -e "${YELLOW}[1/5] Running TypeScript type check...${NC}"
18+
echo -e "${YELLOW}[1/6] Running TypeScript type check...${NC}"
1919
if npm run typecheck; then
2020
echo -e "${GREEN}✓ Type check passed${NC}"
2121
else
@@ -24,7 +24,7 @@ else
2424
fi
2525
echo ""
2626

27-
echo -e "${YELLOW}[2/5] Running ESLint...${NC}"
27+
echo -e "${YELLOW}[2/6] Running ESLint...${NC}"
2828
if npm run lint; then
2929
echo -e "${GREEN}✓ Lint check passed${NC}"
3030
else
@@ -33,7 +33,7 @@ else
3333
fi
3434
echo ""
3535

36-
echo -e "${YELLOW}[3/5] Running Prettier format check...${NC}"
36+
echo -e "${YELLOW}[3/6] Running Prettier format check...${NC}"
3737
if npm run format:check; then
3838
echo -e "${GREEN}✓ Format check passed${NC}"
3939
else
@@ -43,7 +43,16 @@ else
4343
fi
4444
echo ""
4545

46-
echo -e "${YELLOW}[4/5] Running tests...${NC}"
46+
echo -e "${YELLOW}[4/6] Validating repository skills...${NC}"
47+
if python3 -B "$(dirname "$0")/scripts/test_repo_skills.py"; then
48+
echo -e "${GREEN}✓ Skill validation passed${NC}"
49+
else
50+
echo -e "${RED}✗ Skill validation failed${NC}"
51+
FAILURES=$((FAILURES + 1))
52+
fi
53+
echo ""
54+
55+
echo -e "${YELLOW}[5/6] Running tests...${NC}"
4756
if npm test; then
4857
echo -e "${GREEN}✓ Tests passed${NC}"
4958
else
@@ -52,7 +61,7 @@ else
5261
fi
5362
echo ""
5463

55-
echo -e "${YELLOW}[5/5] Running coverage check...${NC}"
64+
echo -e "${YELLOW}[6/6] Running coverage check...${NC}"
5665
if npm run test:coverage; then
5766
echo -e "${GREEN}✓ Coverage check passed${NC}"
5867
else

scripts/test_repo_skills.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Regression checks for skills committed in this repository.
4+
"""
5+
6+
from __future__ import annotations
7+
8+
import sys
9+
import tempfile
10+
from pathlib import Path
11+
from unittest import TestCase, TextTestRunner, defaultTestLoader
12+
13+
SCRIPT_DIR = Path(__file__).resolve().parent
14+
REPO_ROOT = SCRIPT_DIR.parent
15+
SKILLS_ROOT = REPO_ROOT / "skills"
16+
SKILL_CREATOR_SCRIPTS = SKILLS_ROOT / "skill-creator" / "scripts"
17+
SKILL_DOC_FILENAME = "SKILL.md"
18+
19+
if str(SKILL_CREATOR_SCRIPTS) not in sys.path:
20+
sys.path.insert(0, str(SKILL_CREATOR_SCRIPTS))
21+
22+
from package_skill import package_skill
23+
from quick_validate import validate_skill
24+
25+
26+
def iter_skill_dirs() -> list[Path]:
27+
return sorted(skill_md.parent for skill_md in SKILLS_ROOT.glob(f"*/{SKILL_DOC_FILENAME}"))
28+
29+
30+
class TestRepositorySkills(TestCase):
31+
def test_repository_contains_skills(self) -> None:
32+
self.assertTrue(iter_skill_dirs(), f"No skills found under {SKILLS_ROOT}")
33+
34+
def test_all_repository_skills_validate(self) -> None:
35+
for skill_dir in iter_skill_dirs():
36+
with self.subTest(skill=skill_dir.name):
37+
valid, message = validate_skill(skill_dir)
38+
self.assertTrue(valid, f"{skill_dir.name}: {message}")
39+
40+
def test_all_repository_skills_package(self) -> None:
41+
with tempfile.TemporaryDirectory(prefix="test_repo_skills_") as temp_dir:
42+
output_dir = Path(temp_dir)
43+
for skill_dir in iter_skill_dirs():
44+
with self.subTest(skill=skill_dir.name):
45+
result = package_skill(skill_dir, output_dir)
46+
self.assertIsNotNone(result, f"{skill_dir.name}: packaging failed")
47+
self.assertTrue(result.exists(), f"{skill_dir.name}: archive missing")
48+
49+
50+
def main() -> int:
51+
suite = defaultTestLoader.loadTestsFromTestCase(TestRepositorySkills)
52+
result = TextTestRunner(verbosity=2).run(suite)
53+
return 0 if result.wasSuccessful() else 1
54+
55+
56+
if __name__ == "__main__":
57+
sys.exit(main())

0 commit comments

Comments
 (0)