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:
141
build.py
141
build.py
@@ -566,6 +566,141 @@ def install_gemini(output_dir: Path):
|
||||
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):
|
||||
"""Install personas to OpenClaw format (IDENTITY.md + individual persona files)."""
|
||||
oc_dir = output_dir / "_openclaw"
|
||||
@@ -598,7 +733,7 @@ def install_openclaw(output_dir: Path):
|
||||
def main():
|
||||
import argparse
|
||||
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")
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -641,7 +776,7 @@ def main():
|
||||
# Platform installation
|
||||
if args.install:
|
||||
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:
|
||||
if target == "claude":
|
||||
install_claude(output_dir)
|
||||
@@ -651,6 +786,8 @@ def main():
|
||||
install_gemini(output_dir)
|
||||
elif target == "openclaw":
|
||||
install_openclaw(output_dir)
|
||||
elif target == "paperclip":
|
||||
install_paperclip(output_dir, personas_dir)
|
||||
|
||||
print_summary(config, len(persona_dirs), total_variants, total_words)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user