feat: integrate all shared libraries directly into repo
Shared library now lives at personas/_shared/ with full source data:
- skills/ — 42 skills from shared-skills + kali-claw (SKILL.md + references)
- paperclip-skills/ — 52 skills from paperclip-docs (ceo-advisor, coding-agent, etc.)
- design-md/ — 58 brand DESIGN.md files (Stripe, Claude, Linear, Apple, Vercel...)
- ui-ux-pro-max/ — BM25 search engine + 14 CSV data files (67 styles, 161 products)
- openclaw-personas/ — 6 original personas + SOUL.md + IDENTITY.md + TOOLS.md
- kali-tools/ — 16 Kali Linux tool reference docs
- osint-sources/ + ad-attack-tools/ — investigation references
Build system enhancements:
- Skills auto-mapped to personas via SKILL_PERSONA_MAP (domain-based)
- Each persona JSON/YAML output now includes "skills" array
- generated/_index/skills_index.json indexes all 42+52 skills + 58 brands + 14 data files
- Skills, escalation graph, and trigger index all generated per build
Sources: shared-skills (Gitea), kali-claw (Gitea), paperclip-docs (Born2beRoot),
awesome-design-md (VoltAgent), ui-ux-pro-max-skill (nextlevelbuilder)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
102
build.py
102
build.py
@@ -133,7 +133,7 @@ def parse_persona_md(filepath: Path, flat_config: dict) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config: dict, escalation_graph: dict = None):
|
||||
def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config: dict, escalation_graph: dict = None, skills_index: dict = None):
|
||||
"""Build all variants for a persona directory."""
|
||||
md_files = sorted(persona_dir.glob("*.md"))
|
||||
if not md_files:
|
||||
@@ -183,6 +183,15 @@ def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config
|
||||
if escalation_graph and persona_name in escalation_graph:
|
||||
output["escalates_to"] = escalation_graph[persona_name]
|
||||
|
||||
# Inject mapped skills for this persona
|
||||
if skills_index:
|
||||
mapped_skills = []
|
||||
for skill_name, skill_info in skills_index.get("skills", {}).items():
|
||||
if persona_name in skill_info.get("personas", []):
|
||||
mapped_skills.append(skill_name)
|
||||
if mapped_skills:
|
||||
output["skills"] = sorted(mapped_skills)
|
||||
|
||||
# Inject section word counts for quality tracking
|
||||
output["_stats"] = {
|
||||
"total_words": sum(len(s.split()) for s in parsed["sections"].values()),
|
||||
@@ -211,6 +220,82 @@ def build_persona(persona_dir: Path, output_dir: Path, flat_config: dict, config
|
||||
return count
|
||||
|
||||
|
||||
SKILL_PERSONA_MAP = {
|
||||
# Cybersecurity skills → personas
|
||||
"pentest": ["neo"], "nmap-recon": ["neo", "vortex"], "security-scanner": ["neo", "phantom"],
|
||||
"sql-injection-testing": ["neo", "phantom"], "stealth-browser": ["neo", "oracle"],
|
||||
"security-audit-toolkit": ["neo", "forge"], "pwnclaw-security-scan": ["neo"],
|
||||
"senior-secops": ["bastion"], "clawsec": ["neo", "vortex"],
|
||||
"pcap-analyzer": ["vortex", "bastion"], "sys-guard-linux-remediator": ["bastion"],
|
||||
"ctf-writeup-generator": ["neo"], "dns-networking": ["vortex", "architect"],
|
||||
"network-scanner": ["neo", "vortex"], "security-skill-scanner": ["neo"],
|
||||
"pentest-active-directory": ["neo"], "pentest-api-attacker": ["neo", "phantom"],
|
||||
"pentest-auth-bypass": ["neo", "phantom"], "pentest-c2-operator": ["neo", "sentinel"],
|
||||
"gov-cybersecurity": ["sentinel", "bastion"],
|
||||
# Intelligence skills → personas
|
||||
"osint-investigator": ["oracle"], "seithar-intel": ["sentinel", "frodo"],
|
||||
"freshrss": ["frodo", "oracle"], "freshrss-reader": ["frodo", "oracle"],
|
||||
"war-intel-monitor": ["frodo", "marshal"], "news-crawler": ["frodo", "herald"],
|
||||
"dellight-intelligence-ops": ["frodo", "echo"], "dellight-strategic-intelligence": ["frodo"],
|
||||
"agent-intelligence-network-scan": ["oracle"], "social-trust-manipulation-detector": ["ghost"],
|
||||
# Infrastructure skills → personas
|
||||
"docker-essentials": ["architect"], "session-logs": ["architect"],
|
||||
# Document processing → personas
|
||||
"image-ocr": ["oracle", "scribe"], "mistral-ocr": ["oracle", "scribe"],
|
||||
"pdf-text-extractor": ["scribe", "scholar"], "youtube-transcript": ["herald", "scholar"],
|
||||
# Web scraping → personas
|
||||
"deep-scraper": ["oracle"], "crawl-for-ai": ["oracle", "herald"],
|
||||
}
|
||||
|
||||
|
||||
def build_skills_index(shared_dir: Path) -> dict:
|
||||
"""Index all shared skills from _shared/skills/ and _shared/paperclip-skills/."""
|
||||
index = {"skills": {}, "paperclip_skills": {}, "design_brands": [], "ui_ux_styles": 0}
|
||||
|
||||
# Index shared-skills
|
||||
skills_dir = shared_dir / "skills"
|
||||
if skills_dir.exists():
|
||||
for skill_dir in sorted(skills_dir.iterdir()):
|
||||
if not skill_dir.is_dir():
|
||||
continue
|
||||
skill_md = skill_dir / "SKILL.md"
|
||||
if skill_md.exists():
|
||||
content = skill_md.read_text(encoding="utf-8")
|
||||
first_line = ""
|
||||
for line in content.split("\n"):
|
||||
line = line.strip()
|
||||
if line and not line.startswith(("---", "#", "name:", "description:")):
|
||||
first_line = line[:120]
|
||||
break
|
||||
index["skills"][skill_dir.name] = {
|
||||
"personas": SKILL_PERSONA_MAP.get(skill_dir.name, []),
|
||||
"summary": first_line,
|
||||
"has_references": (skill_dir / "references").is_dir(),
|
||||
}
|
||||
|
||||
# Index paperclip-skills
|
||||
pskills_dir = shared_dir / "paperclip-skills"
|
||||
if pskills_dir.exists():
|
||||
for skill_dir in sorted(pskills_dir.iterdir()):
|
||||
if not skill_dir.is_dir():
|
||||
continue
|
||||
skill_md = skill_dir / "SKILL.md"
|
||||
if skill_md.exists():
|
||||
index["paperclip_skills"][skill_dir.name] = True
|
||||
|
||||
# Index design brands
|
||||
design_dir = shared_dir / "design-md"
|
||||
if design_dir.exists():
|
||||
index["design_brands"] = sorted([d.name for d in design_dir.iterdir() if d.is_dir()])
|
||||
|
||||
# Count UI/UX data
|
||||
uiux_dir = shared_dir / "ui-ux-pro-max" / "data"
|
||||
if uiux_dir.exists():
|
||||
index["ui_ux_styles"] = sum(1 for f in uiux_dir.glob("*.csv"))
|
||||
|
||||
return index
|
||||
|
||||
|
||||
def build_escalation_graph(personas_dir: Path, flat_config: dict) -> dict:
|
||||
"""Extract cross-persona escalation paths from Boundaries sections."""
|
||||
graph = {} # {persona: [escalation_targets]}
|
||||
@@ -358,6 +443,15 @@ def build_catalog(personas_dir: Path, output_dir: Path, config: dict, flat_confi
|
||||
)
|
||||
print(f" Index: {index_path}/escalation_graph.json, trigger_index.json")
|
||||
|
||||
# Write skills index if shared dir exists
|
||||
shared_dir = personas_dir / "_shared"
|
||||
if shared_dir.exists():
|
||||
si = build_skills_index(shared_dir)
|
||||
(index_path / "skills_index.json").write_text(
|
||||
json.dumps(si, indent=2, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
print(f" Skills: {len(si.get('skills', {}))} shared + {len(si.get('paperclip_skills', {}))} paperclip + {len(si.get('design_brands', []))} design brands + {si.get('ui_ux_styles', 0)} UI/UX data files")
|
||||
|
||||
# Print validation warnings
|
||||
if all_warnings:
|
||||
print(f"\n WARNINGS ({len(all_warnings)}):")
|
||||
@@ -533,12 +627,14 @@ def main():
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
print(f"Building {len(persona_dirs)} personas -> {output_dir}\n")
|
||||
|
||||
# Pre-build escalation graph for cross-persona injection
|
||||
# Pre-build escalation graph and skills index
|
||||
escalation_graph = build_escalation_graph(personas_dir, flat_config)
|
||||
shared_dir = personas_dir / "_shared"
|
||||
skills_index = build_skills_index(shared_dir) if shared_dir.exists() else {}
|
||||
|
||||
total_variants = 0
|
||||
for pdir in persona_dirs:
|
||||
total_variants += build_persona(pdir, output_dir, flat_config, config, escalation_graph)
|
||||
total_variants += build_persona(pdir, output_dir, flat_config, config, escalation_graph, skills_index)
|
||||
|
||||
total_words = build_catalog(personas_dir, output_dir, config, flat_config)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user