Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
134447c
docs(adr): record compile-time plugin code generation decision
illeatmyhat Apr 29, 2026
e7b48e6
docs: drop the ADR draft (#219 PRD already captures the rationale)
illeatmyhat Apr 29, 2026
3181274
feat(build): introduce plugin-source/ and the render-equality build p…
illeatmyhat Apr 29, 2026
9b5e0aa
feat(build): migrate identical claude/claw-code skill scripts to plug…
illeatmyhat Apr 29, 2026
4c0b646
feat(build): add Jinja2 templating to the plugin source pipeline
illeatmyhat Apr 29, 2026
5ea1275
feat(build): template all claude/claw-code SKILL.md prose
illeatmyhat Apr 29, 2026
df5fec7
feat(build): add claude-only on_stop hooks as platform overlays
illeatmyhat Apr 29, 2026
07a171c
refactor(bob): drop colon-prefixed paths for Windows compatibility
illeatmyhat Apr 29, 2026
298413e
refactor(bob): decouple custom_modes.yaml from skill-path enumeration
illeatmyhat Apr 29, 2026
354f035
fix(sync): surface invalid subscription entries in stdout summary
illeatmyhat Apr 29, 2026
2363dc5
feat(build): unify SKILL.md prose across all four platforms
illeatmyhat Apr 29, 2026
9a573de
feat(learn): unify save_entities.py with codex's strict ownership sem…
illeatmyhat Apr 29, 2026
9f6537d
feat(recall): unify retrieve_entities.py across all four platforms
illeatmyhat Apr 29, 2026
def6e53
feat(sharing): unify publish/subscribe/sync/unsubscribe + bob save_tr…
illeatmyhat Apr 29, 2026
3f784f5
build(manifest): default target to source path
illeatmyhat Apr 29, 2026
5371e46
build(manifest): use Jinja default filter for forked_context
illeatmyhat Apr 29, 2026
e3f8fab
build(manifest): per-platform target_rewrites for bob folder rename
illeatmyhat Apr 29, 2026
9a241f1
build(manifest): default platforms to every declared platform
illeatmyhat Apr 29, 2026
e966506
build(lib): ship lib/ to all four platforms; drop claude fallback
illeatmyhat Apr 30, 2026
07aa430
build(manifest): fan out save / save-trajectory / on_stop to every pl…
illeatmyhat Apr 30, 2026
c30c845
build(structure): nest skills under skills/evolve-lite/<name>/
illeatmyhat Apr 30, 2026
1efee44
build(manifest): delete MANIFEST.toml; renderer walks plugin-source/
illeatmyhat Apr 30, 2026
60d0a8c
build(layout): move build_plugins.py into plugin-source/
illeatmyhat Apr 30, 2026
a090872
build(metadata): generate per-host plugin.json from plugin.toml
illeatmyhat Apr 30, 2026
afac65f
feat(bob): rename skills to colon form; tighten learn/recall workflow…
illeatmyhat Apr 30, 2026
fc94634
build(commands): render bob commands/ from plugin-source
illeatmyhat Apr 30, 2026
a0d7520
Merge branch 'main' into feature/unify-plugin-code
illeatmyhat May 1, 2026
f84f6b9
refactor(plugin-source): per-platform routing + auto-generate bob com…
illeatmyhat May 1, 2026
eed618e
test(plugin-source): redesign build pipeline tests + ship smoke harness
illeatmyhat May 1, 2026
1072272
test(smoke): clean up live output — sections only, no summary
illeatmyhat May 1, 2026
50d0fd2
test(smoke): let live-region lines wrap; size wipe by physical rows
illeatmyhat May 1, 2026
6994bca
fix(install): purge legacy evolve-prefixed bob artifacts on install/u…
illeatmyhat May 4, 2026
9ebcf33
fix(check_drift): detect orphan files under plugin roots
illeatmyhat May 4, 2026
73fd291
chore: ignore example-guidelines/
illeatmyhat May 4, 2026
9fe5f71
Merge branch 'main' into feature/unify-plugin-code
illeatmyhat May 4, 2026
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: 20 additions & 0 deletions .github/workflows/check-code.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ jobs:
- name: Check typing
run: uv run mypy .

check-plugins-rendered:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.12']
steps:
- uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
- name: Verify platform-integrations matches a fresh render of plugin-source
run: uv run python plugin-source/build_plugins.py check

ui-tests:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ dist
.secrets
event.json
site/
example-guidelines
.codex
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,15 @@ repos:
name: detect secrets
args: ['--baseline', '.secrets.baseline']
exclude: package.lock.json

# Plugin render-equality gate — fails if platform-integrations/ has drifted
# from plugin-source/. Runs whenever plugin-source/ or the rendered tree
# under platform-integrations/ has changed.
- repo: local
hooks:
- id: plugins-rendered
name: plugins-rendered
entry: uv run python plugin-source/build_plugins.py check
language: system
pass_filenames: false
files: ^(plugin-source/|platform-integrations/)
9 changes: 9 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,12 @@ codex-run:
# Smoke-test that Codex is installed and working
codex-test:
docker run --rm --env-file {{env_file}} {{codex_image}} codex exec --skip-git-repo-check "who are you"

# Render plugin-source/ into platform-integrations/. Edit plugin-source/, then run this.
compile-plugins:
uv run python plugin-source/build_plugins.py render

# Verify committed platform-integrations/ matches a fresh render of plugin-source/.
# CI and the pre-commit hook run this; nonzero exit means the source and output have drifted.
check-plugins-rendered:
uv run python plugin-source/build_plugins.py check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Must be used near the end of any non-trivial turn that produced potentially reusable tools, guidance, errors, workarounds, or workflows, so those lessons are saved for future turns.
---
Use the `evolve-lite-learn` skill on the current conversation. Follow the skill's instructions exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Publish a private guideline to a configured write-scope repo.
---
Use the `evolve-lite-publish` skill on the current conversation. Follow the skill's instructions exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Must be used at the start of any non-trivial task involving code changes, debugging, repo exploration, file inspection, or environment/tooling investigation to surface stored guidance before analysis or tool use.
---
Use the `evolve-lite-recall` skill on the current conversation. Follow the skill's instructions exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Save the current conversation as a trajectory JSON file in OpenAI chat completion format for analysis and fine-tuning
---
Use the `evolve-lite-save-trajectory` skill on the current conversation. Follow the skill's instructions exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Captures the current session's successful workflow and saves it as a reusable skill with SKILL.md and helper scripts
---
Use the `evolve-lite-save` skill on the current conversation. Follow the skill's instructions exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Add a shared guidelines repo (read-scope subscription or write-scope publish target) to the unified repos list.
---
Use the `evolve-lite-subscribe` skill on the current conversation. Follow the skill's instructions exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Pull the latest guidelines from every configured repo (read- and write-scope).
---
Use the `evolve-lite-sync` skill on the current conversation. Follow the skill's instructions exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
description: Remove a repo from the unified repos list and delete its local clone.
---
Use the `evolve-lite-unsubscribe` skill on the current conversation. Follow the skill's instructions exactly.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

31 changes: 13 additions & 18 deletions platform-integrations/bob/evolve-lite/custom_modes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,8 @@ customModes:

⚠️ CRITICAL: THE FOLLOWING WORKFLOW IS ABSOLUTELY MANDATORY AND CANNOT BE SKIPPED ⚠️

WORKFLOW (5 steps, strict order):
WORKFLOW (4 steps, strict order):

0. ⚠️ BEFORE FIRST USE (MANDATORY): Read the skill SKILL.md files from .bob/skills/ directory.
- Read .bob/skills/evolve-lite:recall/SKILL.md
- Read .bob/skills/evolve-lite:learn/SKILL.md
- Read .bob/skills/evolve-lite:save-trajectory/SKILL.md
- Do this ONCE per conversation before using the skills
1. ⚠️ FIRST ACTION (MANDATORY): Use the evolve-lite:recall skill to retrieve relevant guidelines.
- This MUST be your FIRST action in EVERY conversation
- NO exceptions - even for simple tasks like "echo hello world"
Expand All @@ -24,10 +19,14 @@ customModes:
- This MUST happen BEFORE attempt_completion
- NO exceptions - even if you think there's nothing to learn

Before using any skill for the first time in a conversation, read its
SKILL.md so you know the exact invocation syntax. Bob installs skills
under `.bob/skills/`; the runtime will resolve a skill by name.

⚠️ ENFORCEMENT RULES ⚠️
- Do not skip steps. Do not reorder steps.
- Step 0 must happen before using any skill for the first time in a conversation. read_file on .bob/skills/*/SKILL.md is allowed before Step 1.
- Step 1 must happen before any other tool use (except the SKILL.md reads in Step 0).
- Step 1 must happen before any other tool use (except a one-time
SKILL.md read for a skill you have not invoked yet).
- Step 3 must happen before Step 4.
- Step 4 must happen before attempt_completion.
- If you forget Step 1, STOP and call evolve-lite:recall immediately.
Expand All @@ -40,16 +39,12 @@ customModes:
- If no errors or non-obvious discoveries occurred, saving zero entities is correct — do not force low-quality entities.

MEMORY SHARING (Optional):
The following skills enable sharing guidelines with others:
- evolve-lite:publish — Publish your private guidelines to a public Git repo
- evolve-lite:subscribe — Subscribe to another user's public guidelines
- evolve-lite:unsubscribe — Remove a subscription
- evolve-lite:sync — Pull latest updates from all subscribed repos (manual invocation required)

These skills are OPTIONAL and do not affect the core workflow. Use them when you want to:
- Share your learnings with teammates
- Learn from others' guidelines
- Keep subscribed guidelines up to date
Additional evolve-lite skills handle sharing guidelines with others
(publish, subscribe, unsubscribe, sync). They are OPTIONAL and do not
affect the core workflow. Use them when you want to share your
learnings with teammates, learn from others' guidelines, or keep
subscribed guidelines up to date. Read the SKILL.md for any of these
before first use.

PRE-COMPLETION GATE:
Before calling attempt_completion, ask yourself:
Expand Down
Empty file.
33 changes: 33 additions & 0 deletions platform-integrations/bob/evolve-lite/lib/audit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Append-only audit log writer for .evolve/audit.log."""

import datetime
import json
import pathlib


def append(project_root=".", **fields):
"""Append a JSON audit entry to .evolve/audit.log.

Args:
project_root: Root directory that contains .evolve/.
**fields: Arbitrary key-value fields to include in the log entry.
"""
path = pathlib.Path(project_root) / ".evolve" / "audit.log"
path.parent.mkdir(parents=True, exist_ok=True)
entry = {**fields, "ts": datetime.datetime.now(datetime.UTC).isoformat().replace("+00:00", "Z")}
with path.open("a", encoding="utf-8") as f:
f.write(json.dumps(entry) + "\n")


if __name__ == "__main__":
import tempfile

with tempfile.TemporaryDirectory() as d:
append(project_root=d, action="test", actor="alice")
log_path = __import__("pathlib").Path(d) / ".evolve" / "audit.log"
line = log_path.read_text(encoding="utf-8").strip()
entry = __import__("json").loads(line)
assert entry["action"] == "test"
assert entry["actor"] == "alice"
assert "ts" in entry
print("audit.py ok")
Loading
Loading