Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions platform-integrations/claude/plugins/evolve-lite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Use `/evolve-lite:publish` to share one or more of your local guidelines with ot

1. The skill lists files in `.evolve/entities/guideline/`
2. You pick which ones to publish
3. Each selected file is copied to `.evolve/public/`, stamped with your username as the owner, committed, and pushed to your `public_repo.remote`
3. Each selected file is moved into `.evolve/public/guideline/`, stamped with `visibility: public`, `published_at`, and your username as the owner, committed, and pushed to your `public_repo.remote`

Others can then subscribe using that remote URL.

Expand All @@ -97,6 +97,7 @@ Use `/evolve-lite:subscribe` to pull in guidelines from another user's public re
```

The repo is cloned to `.evolve/subscribed/alice/` and mirrored into `.evolve/entities/subscribed/alice/` so recall picks them up immediately.
The repo is cloned directly into `.evolve/entities/subscribed/alice/` so recall can pick it up immediately. Subscription names must use only letters, numbers, `.`, `_`, and `-`.

### Syncing Subscriptions

Expand All @@ -122,21 +123,22 @@ Use `/evolve-lite:unsubscribe` to remove a subscription and delete its locally c
> 2. bob
```

The skill confirms before deleting `.evolve/subscribed/{name}/` and its mirror under `.evolve/entities/subscribed/{name}/`.
The skill confirms before deleting `.evolve/entities/subscribed/{name}/`.

### Sharing Storage Layout

```text
.evolve/
public/ # git repo pushed to your public remote
guideline-name.md # owner-stamped guideline
subscribed/
alice/ # git clone of alice's public repo
her-guideline.md
public/
guideline/
guideline-name.md # owner-stamped published guideline
entities/
guideline/
private-guideline.md
subscribed/
alice/ # mirrored for recall
her-guideline.md
alice/ # git clone used directly by recall
guideline/
her-guideline.md
```

## Example Walkthrough
Expand Down
12 changes: 12 additions & 0 deletions platform-integrations/claude/plugins/evolve-lite/lib/entity_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ def find_entities_dir():
return c if c.is_dir() else None


def find_recall_entity_dirs():
"""Locate all directories that should be searched during recall.

Returns the existing recall roots in priority order:
``entities/`` first, then ``public/`` under the configured Evolve dir.
Missing directories are skipped.
"""
evolve_dir = get_evolve_dir()
candidates = [evolve_dir / "entities", evolve_dir / "public"]
return [path for path in candidates if path.is_dir()]


def get_default_entities_dir():
"""Return (and create) the default entities directory.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def main():
print(f"Error: invalid entity name: {args.entity!r}", file=sys.stderr)
sys.exit(1)

if not src_path.exists():
print(f"Error: entity file not found: {src_path}", file=sys.stderr)
if not src_path.is_file():
print(f"Error: entity file not found or is a directory: {src_path}", file=sys.stderr)
sys.exit(1)

# Parse entity
Expand Down Expand Up @@ -91,18 +91,26 @@ def main():
sys.exit(1)

content = entity_to_markdown(entity)
tmp_fd, tmp_path = tempfile.mkstemp(dir=dest_path.parent, suffix=".tmp")
tmp_path = None
try:
with os.fdopen(tmp_fd, "w", encoding="utf-8") as f:
f.write(content)
Path(tmp_path).replace(dest_path)
except Exception:
try:
os.unlink(tmp_path)
except OSError:
pass
raise
src_path.unlink()
with tempfile.NamedTemporaryFile(
"w",
encoding="utf-8",
dir=dest_path.parent,
prefix=f".{args.entity}.",
suffix=".tmp",
delete=False,
) as temp_file:
temp_file.write(content)
temp_file.flush()
os.fsync(temp_file.fileno())
tmp_path = Path(temp_file.name)

tmp_path.replace(dest_path)
src_path.unlink()
finally:
if tmp_path is not None and tmp_path.exists():
tmp_path.unlink()

try:
audit_append(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import argparse
import os
import re
import subprocess
import sys
from pathlib import Path
Expand All @@ -17,6 +18,8 @@
from config import load_config, save_config
from audit import append as audit_append

_SAFE_NAME = re.compile(r"^[A-Za-z0-9._-]+$")


def main():
parser = argparse.ArgumentParser()
Expand All @@ -28,6 +31,13 @@ def main():
evolve_dir = Path(os.environ.get("EVOLVE_DIR", ".evolve"))
project_root = str(evolve_dir.resolve().parent)

if not _SAFE_NAME.match(args.name):
print(
f"Error: invalid subscription name: {args.name!r} (only A-Z, a-z, 0-9, '.', '_', '-' allowed)",
file=sys.stderr,
)
sys.exit(1)

# Validate name: resolve and confirm it stays within the subscribed directory
subscribed_base = (evolve_dir / "entities" / "subscribed").resolve()
dest = (evolve_dir / "entities" / "subscribed" / args.name).resolve()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,19 @@ def main():
for sub in subscriptions:
if not isinstance(sub, dict):
continue
name = sub.get("name", "unknown")
name = sub.get("name")
branch = sub.get("branch", "main")

if not isinstance(name, str) or not name.strip():
summaries.append(f"{sub!r} (skipped — missing or non-string name)")
continue
name = name.strip()

if not isinstance(branch, str) or not branch.strip():
summaries.append(f"{name!r} (skipped — missing or non-string branch)")
continue
branch = branch.strip()

if not _SAFE_NAME.match(name):
summaries.append(f"{name!r} (skipped — invalid subscription name)")
continue
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "evolve-lite",
"version": "1.0.0",
"description": "Recall and save Evolve entities in Codex without MCP.",
"version": "1.1.0",
"description": "Recall, save, and share Evolve entities in Codex without MCP.",
"author": {
"name": "Vinod Muthusamy",
"url": "https://github.com/AgentToolkit/altk-evolve"
Expand All @@ -13,16 +13,18 @@
"skills": "./skills/",
"interface": {
"displayName": "Evolve Lite",
"shortDescription": "Recall and save reusable Evolve entities.",
"longDescription": "A lightweight Codex plugin that helps you save reusable entities from successful sessions and recall them automatically on new prompts.",
"shortDescription": "Recall, save, and share reusable Evolve entities.",
"longDescription": "A lightweight Codex plugin that helps you save reusable entities, publish selected guidance, subscribe to shared repos, and recall relevant memory automatically on new prompts.",
"developerName": "AgentToolkit",
"category": "Productivity",
"capabilities": ["Interactive", "Write"],
"websiteURL": "https://github.com/AgentToolkit/altk-evolve",
"defaultPrompt": [
"Recall Evolve entities for this task.",
"Save new Evolve learnings from this session.",
"Show me the entities stored for this repo."
"Show me the entities stored for this repo.",
"Publish one of my Evolve guidelines.",
"Subscribe to a teammate's Evolve guidelines repo."
],
"brandColor": "#2563EB"
}
Expand Down
Loading
Loading