check.py — ตรวจสอบรูปร่างของ Manifest ทั้งหมด
หน้าที่ (Purpose)
Script นี้เป็น linter สำหรับระบบ Managed Agent ทั้งหมด มันตรวจสอบความสมบูรณ์และความถูกต้องของ manifest files (YAML, JSON) และ reference ต่าง ๆ ในโครงสร้าง plugin เพื่อป้องกันการ deploy agent ที่มีข้อมูลหายหรือพัง
เมื่อควรใช้:
- ก่อน
git commitในการเปลี่ยนแปลง manifest ใด ๆ - ในขั้นตอน CI/CD เพื่อให้มั่นใจว่า manifest สมบูรณ์ก่อน deploy
- เมื่อแก้ไข skills, system prompts, หรือ callable agents ที่มีการอ้างอิง path
- หลังจาก rename file หรือ directory ที่อาจแตกการอ้างอิง
Usage
python3 scripts/check.py
ไม่มี arguments หรือ flags — script อ่านจาก repository root (ROOT = Path(__file__).resolve().parents[1])
ข้อกำหนด:
- Python 3.7+
pyyamllibrary (pip install pyyaml)
รันจาก: Repository root
cd /path/to/financial-services
python3 scripts/check.py
ขั้นตอนการตรวจสอบ (5 Main Validation Steps)
Script ทำการตรวจสอบ 5 ขั้นตอนหลัก (บรรทัด 40-159):
Step 1: YAML Parse Validation (บรรทัด 41-47)
# Loop through all *.yaml files in managed-agent-cookbooks/
for yml in sorted(MANAGED.rglob("*.yaml")):
checked += 1
try:
with open(yml) as f:
yaml.safe_load(f) # Parse with yaml.safe_load()
except yaml.YAMLError as e:
err(f"YAML parse: {rel(yml)}: {e}")
ทำอะไร:
- วนลูปทุกไฟล์
*.yamlในmanaged-agent-cookbooks/directory - พยายาม parse ด้วย
yaml.safe_load() - หากเป็น invalid YAML จะบันทึก error
ตัวอย่าง error:
✗ YAML parse: managed-agent-cookbooks/pitch-agent/agent.yaml: mapping values are not allowed here (line 12)
Step 2: JSON Parse Validation (บรรทัด 49-61)
json_globs = [
".claude-plugin/marketplace.json",
"plugins/**/.claude-plugin/plugin.json",
"managed-agent-cookbooks/*/steering-examples.json",
]
for pat in json_globs:
for jf in sorted(ROOT.glob(pat)):
checked += 1
try:
json.loads(jf.read_text())
except json.JSONDecodeError as e:
err(f"JSON parse: {rel(jf)}: {e}")
ทำอะไร:
- ตรวจสอบ JSON files 3 ประเภท:
.claude-plugin/marketplace.json— plugin registryplugins/**/.claude-plugin/plugin.json— plugin metadatamanaged-agent-cookbooks/*/steering-examples.json— agent steering events
- Parse ด้วย
json.loads(), flag malformed JSON
ตัวอย่าง error:
✗ JSON parse: managed-agent-cookbooks/earnings-reviewer/steering-examples.json: Expecting ',' delimiter: line 5 column 10
Step 3: Agent Markdown Frontmatter Validation (บรรทัด 63-77)
for md in sorted(PLUGINS.glob("agent-plugins/*/agents/*.md")):
checked += 1
text = md.read_text()
if not text.startswith("---"):
err(f"frontmatter: {rel(md)}: missing leading ---")
continue
try:
_, fm, _ = text.split("---", 2) # Extract YAML block
meta = yaml.safe_load(fm)
for k in ("name", "description"):
if k not in meta:
err(f"frontmatter: {rel(md)}: missing '{k}'")
except (ValueError, yaml.YAMLError) as e:
err(f"frontmatter: {rel(md)}: {e}")
ทำอะไร:
- ทุกไฟล์
agent-plugins/*/agents/*.mdต้องมี YAML frontmatter - Frontmatter ต้องมี required fields:
name,description - Format:
--- name: "my-agent" description: "does something useful" ---
ตัวอย่าง error:
✗ frontmatter: plugins/agent-plugins/pitch-agent/agents/pitch-agent.md: missing 'description'
Step 4: Reference Resolution (บรรทัด 80-150)
นี่คือขั้นตอนหลัก — ตรวจสอบว่า paths ที่อ้างอิงมีอยู่จริง
Step 4a: Basic Path Resolution (บรรทัด 89-108)
def check_refs(yml: Path) -> None:
data = yaml.safe_load(yml.read_text()) or {}
base = yml.parent
# Check system.file
sys_spec = data.get("system")
if isinstance(sys_spec, dict) and "file" in sys_spec:
p = (base / sys_spec["file"]).resolve()
if not p.is_file():
err(f"ref: {rel(yml)}: system.file -> {sys_spec['file']} (not found)")
# Check skills[].path
for s in data.get("skills") or []:
if isinstance(s, dict) and "path" in s:
p = (base / s["path"]).resolve()
if not p.exists():
err(f"ref: {rel(yml)}: skills.path -> {s['path']} (not found)")
# Check skills[].from_plugin
if isinstance(s, dict) and "from_plugin" in s:
p = (base / s["from_plugin"]).resolve()
if not (p / "skills").is_dir():
err(f"ref: {rel(yml)}: skills.from_plugin -> {s['from_plugin']} (no skills/ dir)")
# Check callable_agents[].manifest
for c in data.get("callable_agents") or []:
if isinstance(c, dict) and "manifest" in c:
p = (base / c["manifest"]).resolve()
if not p.is_file():
err(f"ref: {rel(yml)}: callable_agents.manifest -> {c['manifest']} (not found)")
ตรวจสอบต่อไปนี้ ในทุกไฟล์ agent.yaml และ subagent .yaml:
| Reference Type | Check | Example |
|---|---|---|
system.file |
File ต้องมีอยู่ | system.file: ../../plugins/agent-plugins/pitch-agent/agents/pitch-agent.md |
skills[].path |
Directory/file ต้องมีอยู่ | skills: [{ path: ./skills/my-skill }] |
skills[].from_plugin |
skills/ subdirectory ต้องมีอยู่ |
skills: [{ from_plugin: ../../plugins/agent-plugins/pitch-agent }] |
callable_agents[].manifest |
Manifest file ต้องมีอยู่ | callable_agents: [{ manifest: ./subagents/researcher.yaml }] |
ตัวอย่าง error:
✗ ref: managed-agent-cookbooks/pitch-agent/agent.yaml: system.file -> ../../plugins/agent-plugins/pitch-agent/agents/pitch-agent.md (not found)
✗ ref: managed-agent-cookbooks/earnings-reviewer/agent.yaml: callable_agents.manifest -> ./subagents/transcript-reader.yaml (not found)
Step 4b: Bundled Skills Sync Check (บรรทัด 114-131)
src_by_name = {p.name: p for p in PLUGINS.glob("vertical-plugins/*/skills/*") if p.is_dir()}
for bundled in sorted(PLUGINS.glob("agent-plugins/*/skills/*")):
if not bundled.is_dir():
continue
src = src_by_name.get(bundled.name)
if not src:
err(f"bundled-skill: {rel(bundled)}: no vertical-plugins source named '{bundled.name}'")
continue
cmp = filecmp.dircmp(src, bundled)
if cmp.diff_files or cmp.left_only or cmp.right_only:
err(
f"bundled-skill: {rel(bundled)}: drifted from {rel(src)} "
f"(run scripts/sync-agent-skills.py)"
)
ทำอะไร:
- หลังจากแก้ไข skill ที่เก็บใน
vertical-plugins/*/skills/*ต้อง sync ไปยังทุก agent-plugin ที่ใช้มัน - Script นี้ตรวจสอบ:
- ทุก bundled skill ใน
agent-plugins/*/skills/ต้องมี source ในvertical-plugins/*/skills/ - Bundled skill ต้องตรงกับ source (ไฟล์เดียวกัน, ไม่มีไฟล์เพิ่มเติม)
- ทุก bundled skill ใน
ตัวอย่าง error:
✗ bundled-skill: plugins/agent-plugins/pitch-agent/skills/tearsheet: drifted from plugins/vertical-plugins/tearsheet (run scripts/sync-agent-skills.py)
วิธีแก:
python3 scripts/sync-agent-skills.py
Step 4b2: Agent Prose Skill References (บรรทัด 133-143)
for md in sorted(PLUGINS.glob("agent-plugins/*/agents/*.md")):
slug = md.parents[1].name
sk_dir = PLUGINS / "agent-plugins" / slug / "skills"
bundle = {p.name for p in sk_dir.iterdir() if p.is_dir()} if sk_dir.is_dir() else set()
for ref in set(re.findall(r"`([a-z0-9]+(?:-[a-z0-9]+)+)`", md.read_text())):
if ref in src_by_name and ref not in bundle:
err(
f"agent-prose: {rel(md)}: references `{ref}` but "
f"plugins/agent-plugins/{slug}/skills/{ref}/ is not bundled"
)
ทำอะไร:
- Agent markdown files อ้างอิงถึง skills ด้วย backtick notation:
`skill-name` - Script ตรวจสอบ: ทุก skill ที่อ้างอิงใน markdown ต้องมีอยู่ใน bundled skills ของ agent นั้น
- ใช้ regex
r"([a-z0-9]+(?:-[a-z0-9]+)+)"เพื่อหา skill references
ตัวอย่าง: ไฟล์ markdown พูด “ใช้ comparable-company-analysis skill”
- Script ตรวจ:
plugins/agent-plugins/pitch-agent/skills/comparable-company-analysis/ต้องมีอยู่
ตัวอย่าง error:
✗ agent-prose: plugins/agent-plugins/pitch-agent/agents/pitch-agent.md: references `tearsheet` but plugins/agent-plugins/pitch-agent/skills/tearsheet/ is not bundled
Step 4c: Marketplace Source Paths (บรรทัด 145-150)
mp = ROOT / ".claude-plugin" / "marketplace.json"
for p in json.loads(mp.read_text()).get("plugins", []):
src = (ROOT / p["source"]).resolve()
if not (src / ".claude-plugin" / "plugin.json").is_file():
err(f"marketplace: {p['name']} source -> {p['source']} (no plugin.json)")
ทำอะไร:
.claude-plugin/marketplace.jsonระบุรายชื่อ plugins ทั้งหมด- แต่ละ plugin entry มี
"source"field ที่ชี้ไปยัง directory - Directory นั้นต้องมี
.claude-plugin/plugin.jsonไฟล์
ตัวอย่าง error:
✗ marketplace: S&P Global source -> plugins/partner-built/spglobal (no plugin.json)
Step 5: Required Files per Managed Agent (บรรทัด 152-158)
for d in sorted(MANAGED.iterdir()):
if not d.is_dir():
continue
for req in ("agent.yaml", "README.md", "steering-examples.json"):
if not (d / req).is_file():
err(f"missing: {rel(d)}/{req}")
ทำอะไร:
- ทุก managed-agent directory ต้องมี 3 ไฟล์บังคับ:
agent.yaml— agent manifestREADME.md— documentationsteering-examples.json— steering events for orchestration
ตัวอย่าง error:
✗ missing: managed-agent-cookbooks/kyc-screener/steering-examples.json
Output Format
Success Case
$ python3 scripts/check.py
OK — 127 file(s) checked, 0 issues.
Exit code: 0
Failure Case
$ python3 scripts/check.py
FAIL — 3 issue(s) across 120 file(s):
✗ YAML parse: managed-agent-cookbooks/pitch-agent/agent.yaml: mapping values are not allowed here (line 12)
✗ ref: managed-agent-cookbooks/gl-reconciler/agent.yaml: system.file -> system.txt (not found)
✗ missing: managed-agent-cookbooks/kyc-screener/steering-examples.json
Exit codes:
0— ไม่พบ error1— พบ error (แสดงรายการหมด)2— Missing pyyaml library
ตัวอย่าง (Example Workflows)
Example 1: Quick Validation Before Commit
$ python3 scripts/check.py
OK — 127 file(s) checked, 0 issues.
$ git commit -m "Add new earnings-reviewer agent"
Example 2: Detecting Broken References
$ # Rename subagent file
mv ./managed-agent-cookbooks/pitch-agent/subagents/researcher.yaml \
./managed-agent-cookbooks/pitch-agent/subagents/researcher_v2.yaml
$ python3 scripts/check.py
FAIL — 1 issue(s) across 127 file(s):
✗ ref: managed-agent-cookbooks/pitch-agent/agent.yaml: callable_agents.manifest -> ./subagents/researcher.yaml (not found)
$ # Fix by updating agent.yaml
vi ./managed-agent-cookbooks/pitch-agent/agent.yaml # Change path to researcher_v2.yaml
$ python3 scripts/check.py
OK — 127 file(s) checked, 0 issues.
Example 3: Bundled Skills Drift
$ # Edit a skill in vertical-plugins
vi ./plugins/vertical-plugins/tearsheet/skill.md
$ python3 scripts/check.py
FAIL — 1 issue(s) across 127 file(s):
✗ bundled-skill: plugins/agent-plugins/pitch-agent/skills/tearsheet: drifted from plugins/vertical-plugins/tearsheet (run scripts/sync-agent-skills.py)
$ # Sync skills
python3 scripts/sync-agent-skills.py
Syncing: tearsheet
From: plugins/vertical-plugins/tearsheet
To: plugins/agent-plugins/pitch-agent/skills/tearsheet
✓ Updated 3 files
$ python3 scripts/check.py
OK — 127 file(s) checked, 0 issues.
Example 4: Missing Required File
$ # Accidentally delete steering-examples.json
rm ./managed-agent-cookbooks/earnings-reviewer/steering-examples.json
$ python3 scripts/check.py
FAIL — 1 issue(s) across 127 file(s):
✗ missing: managed-agent-cookbooks/earnings-reviewer/steering-examples.json
$ # Restore from template or copy from similar agent
cp ./managed-agent-cookbooks/pitch-agent/steering-examples.json \
./managed-agent-cookbooks/earnings-reviewer/steering-examples.json
$ # Edit to match earnings-reviewer's actual steering events
vi ./managed-agent-cookbooks/earnings-reviewer/steering-examples.json
$ python3 scripts/check.py
OK — 127 file(s) checked, 0 issues.
Check Functions — Internal Reference
| Function | Purpose | Triggered By |
|---|---|---|
check_refs(yml) |
Resolve system.file, skills, callable_agents paths | Step 4 |
yaml.safe_load() |
Parse YAML safely | Step 1 |
json.loads() |
Parse JSON | Step 2 |
filecmp.dircmp() |
Compare bundled skills with source | Step 4b |
re.findall() |
Extract skill references from markdown | Step 4b2 |
Integration with CI/CD Pipeline
Typical workflow:
# .github/workflows/validate.yml
name: Validate Manifests
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- run: pip install pyyaml
- run: python scripts/check.py # Fails if exit code != 0
เกี่ยวข้องกับอะไรอีกบ้าง
| Script | ความสัมพันธ์ |
|---|---|
| sync-agent-skills.py | ถ้าพบ “bundled-skill” error ให้รัน script นี้เพื่อ sync |
| deploy-managed-agent.sh | ควร run check.py ก่อนการ deploy เพื่อให้ manifest พร้อม |
| CI/CD workflow | โดยปกติเรียก check.py ในลำดับแรกของ validation pipeline |
Exit Code Reference
# From check.py (บรรทัด 160-166)
if errors:
print(f"FAIL — {len(errors)} issue(s) across {checked} file(s):\n", file=sys.stderr)
for e in errors:
print(f" ✗ {e}", file=sys.stderr)
sys.exit(1)
else:
print(f"OK — {checked} file(s) checked, 0 issues.")
sys.exit(0)
หมายเหตุ: Script ถูกออกแบบให้ run ก่อน deploy เพื่อป้องกันการ deploy agent ที่มี manifest errors