feat: add --install paperclip to build system

Generates Paperclip-compatible agent output per persona:
- agents/<codename>/SOUL.md — identity, skills, escalation, full prompt
- agents/<codename>/hermes-config.yaml — model, provider, MCP, toolsets
- agents/<codename>/AGENTS.md — workspace overview with org connections
- skills/ — 42 shared skills copied in SKILL.md + references format

Usage: python3 build.py --install paperclip
Output: generated/_paperclip/ (29 agents + 42 skills)

Full platform matrix now: claude, antigravity, gemini, openclaw, paperclip, all

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
salvacybersec
2026-04-06 21:16:45 +03:00
parent 8a0b6d7895
commit 9bbfa3092d

141
build.py
View File

@@ -566,6 +566,141 @@ def install_gemini(output_dir: Path):
return count return count
def install_paperclip(output_dir: Path, personas_dir: Path):
"""Install personas as Paperclip agents (SOUL.md + hermes-config.yaml + AGENTS.md per agent)."""
pc_dir = output_dir / "_paperclip"
agents_dir = pc_dir / "agents"
skills_dir = pc_dir / "skills"
agents_dir.mkdir(parents=True, exist_ok=True)
skills_dir.mkdir(parents=True, exist_ok=True)
# Build escalation graph for AGENTS.md org chart
flat_config = {}
escalation_graph = build_escalation_graph(personas_dir, flat_config)
# Domain → toolset mapping for hermes-config
domain_toolsets = {
"cybersecurity": ["terminal", "file", "web", "code_execution"],
"intelligence": ["terminal", "file", "web"],
"military": ["terminal", "file", "web"],
"engineering": ["terminal", "file", "web", "code_execution"],
"law-economics": ["file", "web"],
"history": ["file", "web"],
"linguistics": ["file", "web"],
"academia": ["file", "web"],
}
agent_count = 0
skill_count = 0
for persona_dir in sorted(output_dir.iterdir()):
if not persona_dir.is_dir() or persona_dir.name.startswith("_"):
continue
general_json = persona_dir / "general.json"
general_prompt = persona_dir / "general.prompt.md"
if not general_json.exists():
continue
data = json.loads(general_json.read_text(encoding="utf-8"))
codename = data.get("codename", persona_dir.name)
name = data.get("name", codename.title())
role = data.get("role", "Specialist")
domain = data.get("domain", "general")
address_to = data.get("address_to", "")
tone = data.get("tone", "")
escalates_to = escalation_graph.get(persona_dir.name, [])
skills = data.get("skills", [])
agent_dir = agents_dir / codename
agent_dir.mkdir(parents=True, exist_ok=True)
# 1. SOUL.md — persona prompt adapted to Paperclip format
soul_lines = [
f"# {name}{role}\n",
f"## Kimlik",
f"- **Ad:** {name}",
f"- **Kod Adı:** {codename}",
f"- **Hitap:** {address_to}",
f"- **Domain:** {domain}",
f"- **Ton:** {tone}",
"",
]
if escalates_to:
soul_lines.append(f"## İlişkiler")
soul_lines.append(f"- **Escalation:** {', '.join(escalates_to)}")
soul_lines.append("")
if skills:
soul_lines.append(f"## Skills")
for s in skills:
soul_lines.append(f"- {s}")
soul_lines.append("")
# Append the full prompt body
if general_prompt.exists():
soul_lines.append("## Detaylı Tanım\n")
soul_lines.append(general_prompt.read_text(encoding="utf-8"))
(agent_dir / "SOUL.md").write_text("\n".join(soul_lines), encoding="utf-8")
# 2. hermes-config.yaml
toolsets = domain_toolsets.get(domain, ["terminal", "file", "web"])
hermes_config = {
"model": "qwen/qwen3.6-plus:free",
"provider": "openrouter",
"defaults": {"quiet": True, "reasoning_effort": "medium"},
"mcp_servers": {
"web-search": {
"command": "npx",
"args": ["-y", "ddg-mcp-search"],
},
},
"skills": {"external_dirs": ["~/.hermes/skills"]},
"toolsets": toolsets,
}
(agent_dir / "hermes-config.yaml").write_text(
yaml.dump(hermes_config, allow_unicode=True, default_flow_style=False),
encoding="utf-8",
)
# 3. AGENTS.md — workspace overview with org connections
agents_md_lines = [
f"# {name} — Workspace\n",
f"- **Agent:** {name} ({role})",
f"- **Domain:** {domain}",
"",
]
if escalates_to:
agents_md_lines.append("## Bağlantılar\n")
for target in escalates_to:
agents_md_lines.append(f"- → {target}")
agents_md_lines.append("")
(agent_dir / "AGENTS.md").write_text("\n".join(agents_md_lines), encoding="utf-8")
agent_count += 1
# Copy shared skills as Paperclip skills (SKILL.md format already compatible)
shared_skills = personas_dir / "_shared" / "skills"
if shared_skills.exists():
for skill_dir in sorted(shared_skills.iterdir()):
if not skill_dir.is_dir():
continue
skill_md = skill_dir / "SKILL.md"
if skill_md.exists():
dest = skills_dir / skill_dir.name
dest.mkdir(parents=True, exist_ok=True)
(dest / "SKILL.md").write_text(skill_md.read_text(encoding="utf-8"), encoding="utf-8")
# Copy references if exist
refs = skill_dir / "references"
if refs.is_dir():
import shutil
shutil.copytree(refs, dest / "references", dirs_exist_ok=True)
skill_count += 1
print(f" Paperclip: {agent_count} agents + {skill_count} skills to {pc_dir}")
return agent_count
def install_openclaw(output_dir: Path): def install_openclaw(output_dir: Path):
"""Install personas to OpenClaw format (IDENTITY.md + individual persona files).""" """Install personas to OpenClaw format (IDENTITY.md + individual persona files)."""
oc_dir = output_dir / "_openclaw" oc_dir = output_dir / "_openclaw"
@@ -598,7 +733,7 @@ def install_openclaw(output_dir: Path):
def main(): def main():
import argparse import argparse
parser = argparse.ArgumentParser(description="Build persona library and optionally install to platforms.") parser = argparse.ArgumentParser(description="Build persona library and optionally install to platforms.")
parser.add_argument("--install", choices=["claude", "antigravity", "gemini", "openclaw", "all"], parser.add_argument("--install", choices=["claude", "antigravity", "gemini", "openclaw", "paperclip", "all"],
help="Install generated personas to a target platform") help="Install generated personas to a target platform")
args = parser.parse_args() args = parser.parse_args()
@@ -641,7 +776,7 @@ def main():
# Platform installation # Platform installation
if args.install: if args.install:
print(f"\n--- Installing to: {args.install} ---\n") print(f"\n--- Installing to: {args.install} ---\n")
targets = ["claude", "antigravity", "gemini", "openclaw"] if args.install == "all" else [args.install] targets = ["claude", "antigravity", "gemini", "openclaw", "paperclip"] if args.install == "all" else [args.install]
for target in targets: for target in targets:
if target == "claude": if target == "claude":
install_claude(output_dir) install_claude(output_dir)
@@ -651,6 +786,8 @@ def main():
install_gemini(output_dir) install_gemini(output_dir)
elif target == "openclaw": elif target == "openclaw":
install_openclaw(output_dir) install_openclaw(output_dir)
elif target == "paperclip":
install_paperclip(output_dir, personas_dir)
print_summary(config, len(persona_dirs), total_variants, total_words) print_summary(config, len(persona_dirs), total_variants, total_words)