Skip to content

Commit cd4204c

Browse files
authored
feat(codex): add lite sharing skills and session-start sync (#196)
* fix(codex): rebase sharing changes onto claude branch * fix(codex): tighten sharing script safeguards * fix(codex): harden sharing scripts and tests * fix(evolve-lite): align codex publish config root * test(evolve-lite): format sync regression tests
1 parent b670d92 commit cd4204c

31 files changed

Lines changed: 2068 additions & 97 deletions

File tree

platform-integrations/claude/plugins/evolve-lite/README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Use `/evolve-lite:publish` to share one or more of your local guidelines with ot
8282

8383
1. The skill lists files in `.evolve/entities/guideline/`
8484
2. You pick which ones to publish
85-
3. Each selected file is copied to `.evolve/public/`, stamped with your username as the owner, committed, and pushed to your `public_repo.remote`
85+
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`
8686

8787
Others can then subscribe using that remote URL.
8888

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

9999
The repo is cloned to `.evolve/subscribed/alice/` and mirrored into `.evolve/entities/subscribed/alice/` so recall picks them up immediately.
100+
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 `-`.
100101

101102
### Syncing Subscriptions
102103

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

125-
The skill confirms before deleting `.evolve/subscribed/{name}/` and its mirror under `.evolve/entities/subscribed/{name}/`.
126+
The skill confirms before deleting `.evolve/entities/subscribed/{name}/`.
126127

127128
### Sharing Storage Layout
128129

129130
```text
130131
.evolve/
131-
public/ # git repo pushed to your public remote
132-
guideline-name.md # owner-stamped guideline
133-
subscribed/
134-
alice/ # git clone of alice's public repo
135-
her-guideline.md
132+
public/
133+
guideline/
134+
guideline-name.md # owner-stamped published guideline
136135
entities/
136+
guideline/
137+
private-guideline.md
137138
subscribed/
138-
alice/ # mirrored for recall
139-
her-guideline.md
139+
alice/ # git clone used directly by recall
140+
guideline/
141+
her-guideline.md
140142
```
141143

142144
## Example Walkthrough

platform-integrations/claude/plugins/evolve-lite/lib/entity_io.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ def find_entities_dir():
7575
return c if c.is_dir() else None
7676

7777

78+
def find_recall_entity_dirs():
79+
"""Locate all directories that should be searched during recall.
80+
81+
Returns the existing recall roots in priority order:
82+
``entities/`` first, then ``public/`` under the configured Evolve dir.
83+
Missing directories are skipped.
84+
"""
85+
evolve_dir = get_evolve_dir()
86+
candidates = [evolve_dir / "entities", evolve_dir / "public"]
87+
return [path for path in candidates if path.is_dir()]
88+
89+
7890
def get_default_entities_dir():
7991
"""Return (and create) the default entities directory.
8092

platform-integrations/claude/plugins/evolve-lite/skills/publish/scripts/publish.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ def main():
5757
print(f"Error: invalid entity name: {args.entity!r}", file=sys.stderr)
5858
sys.exit(1)
5959

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

6464
# Parse entity
@@ -91,18 +91,26 @@ def main():
9191
sys.exit(1)
9292

9393
content = entity_to_markdown(entity)
94-
tmp_fd, tmp_path = tempfile.mkstemp(dir=dest_path.parent, suffix=".tmp")
94+
tmp_path = None
9595
try:
96-
with os.fdopen(tmp_fd, "w", encoding="utf-8") as f:
97-
f.write(content)
98-
Path(tmp_path).replace(dest_path)
99-
except Exception:
100-
try:
101-
os.unlink(tmp_path)
102-
except OSError:
103-
pass
104-
raise
105-
src_path.unlink()
96+
with tempfile.NamedTemporaryFile(
97+
"w",
98+
encoding="utf-8",
99+
dir=dest_path.parent,
100+
prefix=f".{args.entity}.",
101+
suffix=".tmp",
102+
delete=False,
103+
) as temp_file:
104+
temp_file.write(content)
105+
temp_file.flush()
106+
os.fsync(temp_file.fileno())
107+
tmp_path = Path(temp_file.name)
108+
109+
tmp_path.replace(dest_path)
110+
src_path.unlink()
111+
finally:
112+
if tmp_path is not None and tmp_path.exists():
113+
tmp_path.unlink()
106114

107115
try:
108116
audit_append(

platform-integrations/claude/plugins/evolve-lite/skills/subscribe/scripts/subscribe.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import argparse
1010
import os
11+
import re
1112
import subprocess
1213
import sys
1314
from pathlib import Path
@@ -17,6 +18,8 @@
1718
from config import load_config, save_config
1819
from audit import append as audit_append
1920

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

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

34+
if not _SAFE_NAME.match(args.name):
35+
print(
36+
f"Error: invalid subscription name: {args.name!r} (only A-Z, a-z, 0-9, '.', '_', '-' allowed)",
37+
file=sys.stderr,
38+
)
39+
sys.exit(1)
40+
3141
# Validate name: resolve and confirm it stays within the subscribed directory
3242
subscribed_base = (evolve_dir / "entities" / "subscribed").resolve()
3343
dest = (evolve_dir / "entities" / "subscribed" / args.name).resolve()

platform-integrations/claude/plugins/evolve-lite/skills/sync/scripts/sync.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,19 @@ def main():
133133
for sub in subscriptions:
134134
if not isinstance(sub, dict):
135135
continue
136-
name = sub.get("name", "unknown")
136+
name = sub.get("name")
137137
branch = sub.get("branch", "main")
138138

139+
if not isinstance(name, str) or not name.strip():
140+
summaries.append(f"{sub!r} (skipped — missing or non-string name)")
141+
continue
142+
name = name.strip()
143+
144+
if not isinstance(branch, str) or not branch.strip():
145+
summaries.append(f"{name!r} (skipped — missing or non-string branch)")
146+
continue
147+
branch = branch.strip()
148+
139149
if not _SAFE_NAME.match(name):
140150
summaries.append(f"{name!r} (skipped — invalid subscription name)")
141151
continue

platform-integrations/codex/plugins/evolve-lite/.codex-plugin/plugin.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "evolve-lite",
3-
"version": "1.0.0",
4-
"description": "Recall and save Evolve entities in Codex without MCP.",
3+
"version": "1.1.0",
4+
"description": "Recall, save, and share Evolve entities in Codex without MCP.",
55
"author": {
66
"name": "Vinod Muthusamy",
77
"url": "https://github.com/AgentToolkit/altk-evolve"
@@ -13,16 +13,18 @@
1313
"skills": "./skills/",
1414
"interface": {
1515
"displayName": "Evolve Lite",
16-
"shortDescription": "Recall and save reusable Evolve entities.",
17-
"longDescription": "A lightweight Codex plugin that helps you save reusable entities from successful sessions and recall them automatically on new prompts.",
16+
"shortDescription": "Recall, save, and share reusable Evolve entities.",
17+
"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.",
1818
"developerName": "AgentToolkit",
1919
"category": "Productivity",
2020
"capabilities": ["Interactive", "Write"],
2121
"websiteURL": "https://github.com/AgentToolkit/altk-evolve",
2222
"defaultPrompt": [
2323
"Recall Evolve entities for this task.",
2424
"Save new Evolve learnings from this session.",
25-
"Show me the entities stored for this repo."
25+
"Show me the entities stored for this repo.",
26+
"Publish one of my Evolve guidelines.",
27+
"Subscribe to a teammate's Evolve guidelines repo."
2628
],
2729
"brandColor": "#2563EB"
2830
}

0 commit comments

Comments
 (0)