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
|
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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user