File size: 5,135 Bytes
116524e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env python3
"""Set up the ACE skill for an OpenClaw agent.



Copies the skill folder into the OpenClaw workspace and optionally

appends auto-learning instructions to AGENTS.md.



Usage:

    python examples/openclaw/setup.py                # interactive

    python examples/openclaw/setup.py --no-agents    # skip AGENTS.md

    python examples/openclaw/setup.py --openclaw-home /path/to/.openclaw

"""

import argparse
import shutil
import sys
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
SKILL_SRC = SCRIPT_DIR / "kayba-ace"
AGENTS_SNIPPET = SCRIPT_DIR / "AGENTS.md.snippet"

SKILL_NAME = "kayba-ace"

# Marker to detect if the snippet was already appended
AGENTS_MARKER = "## Auto-Learning"


def find_openclaw_home(override: str | None) -> Path:
    """Resolve OPENCLAW_HOME, checking common locations."""
    if override:
        p = Path(override).expanduser().resolve()
        if p.exists():
            return p
        print(f"  ERROR: --openclaw-home path does not exist: {p}")
        sys.exit(1)

    candidates = [
        Path.home() / ".openclaw",
    ]
    for c in candidates:
        if c.exists():
            return c

    print("  ERROR: Could not find OpenClaw installation.")
    print("  Checked: " + ", ".join(str(c) for c in candidates))
    print("  Use --openclaw-home to specify the path manually.")
    sys.exit(1)


def copy_skill(openclaw_home: Path) -> Path:
    """Copy the skill folder into the OpenClaw workspace."""
    dest = openclaw_home / "workspace" / "skills" / SKILL_NAME
    dest.mkdir(parents=True, exist_ok=True)

    # Copy all files (don't overwrite generated skillbook/processed files)
    generated = {"ace_skillbook.json", "ace_skillbook.md", "ace_processed.txt"}
    for src_file in SKILL_SRC.iterdir():
        if src_file.is_file():
            target = dest / src_file.name
            if target.exists() and src_file.name in generated:
                print(f"  SKIP (generated): {target}")
            elif target.exists():
                shutil.copy2(src_file, target)
                print(f"  UPDATED: {src_file.name} -> {target}")
            else:
                shutil.copy2(src_file, target)
                print(f"  COPIED: {src_file.name} -> {target}")

    return dest


def patch_agents_md(openclaw_home: Path) -> bool:
    """Append auto-learning instructions to AGENTS.md if not already present."""
    agents_md = openclaw_home / "workspace" / "AGENTS.md"

    if not AGENTS_SNIPPET.exists():
        print(f"  WARNING: snippet not found at {AGENTS_SNIPPET}")
        return False

    snippet_text = AGENTS_SNIPPET.read_text()

    # Strip the comment header from the snippet (lines starting with #)
    lines = snippet_text.splitlines()
    content_lines = []
    in_header = True
    for line in lines:
        if in_header and line.startswith("#") and not line.startswith("##"):
            continue
        in_header = False
        content_lines.append(line)
    snippet_body = "\n".join(content_lines).strip()

    if agents_md.exists():
        existing = agents_md.read_text()
        if AGENTS_MARKER in existing:
            print(f"  SKIP: AGENTS.md already contains '{AGENTS_MARKER}'")
            return False
        # Append
        with open(agents_md, "a") as f:
            f.write("\n\n" + snippet_body + "\n")
        print(f"  UPDATED: {agents_md}")
    else:
        agents_md.parent.mkdir(parents=True, exist_ok=True)
        agents_md.write_text(snippet_body + "\n")
        print(f"  CREATED: {agents_md}")

    return True


def section(name: str) -> None:
    print(f"\n{'=' * 50}\n  {name}\n{'=' * 50}")


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Set up the ACE skill for an OpenClaw agent."
    )
    parser.add_argument(
        "--openclaw-home",
        default=None,
        help="Path to the OpenClaw home directory (default: ~/.openclaw).",
    )
    parser.add_argument(
        "--no-agents",
        action="store_true",
        help="Skip patching AGENTS.md.",
    )
    args = parser.parse_args()

    section("Finding OpenClaw")
    openclaw_home = find_openclaw_home(args.openclaw_home)
    print(f"  Found: {openclaw_home}")

    section("Copying skill folder")
    skill_dest = copy_skill(openclaw_home)
    print(f"  Skill directory: {skill_dest}")

    if not args.no_agents:
        section("Patching AGENTS.md")
        patch_agents_md(openclaw_home)
    else:
        print("\n  Skipping AGENTS.md (--no-agents)")

    section("Done")
    print(f"""

  Skill installed at: {skill_dest}



  Next steps:

    1. Build the ACE Docker image (see Dockerfile.ace)

    2. Pass your LLM API key in docker-compose.yml

    3. Restart the gateway: docker compose down && docker compose up -d

    4. Send a message — the agent will run ace-learn automatically



  Full guide: https://kayba-ai.github.io/agentic-context-engine/integrations/openclaw/

""")


if __name__ == "__main__":
    main()