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
99 changes: 99 additions & 0 deletions .github/actions/validate/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Validate a Butler skill
description: >-
Checks out the calling repository, fetches the Butler Skill Hub's validator and offline
replay harness (Virtual-Protocol/butler-skills at main) into the runner's temp directory
— never inside the skill tree — and runs `validate.py` and, when the skill has a duty.py,
`replay.py` on it. A skill repo's whole CI is one step:
`uses: Virtual-Protocol/butler-skills/.github/actions/validate@main`.

inputs:
path:
description: Directory of the skill inside the calling repository (where SKILL.md lives).
required: false
default: "."
standalone:
description: >-
"true" (default) validates a skill repository — the name comes from the frontmatter.
"false" is registry mode: the path is skills/<name> inside a butler-skills checkout.
required: false
default: "true"
maintainer:
description: >-
"true" allows the maintainer-only `butler-` name prefix (the registry PR check decides
for real; this only affects the skill repo's own run). `bevo-` names are always refused.
required: false
default: "false"
fixture:
description: Replay fixture for duty.py (basename under the hub's tests/fixtures/).
required: false
default: trade-activity-page

runs:
using: composite
steps:
- name: Check out the skill
uses: actions/checkout@v4

# actions/checkout can only place a repository under $GITHUB_WORKSPACE, and anything
# inside the skill tree would count against its 50-file / 1 MB rules — so land the hub
# in a scratch path, then move it to $RUNNER_TEMP before validating.
- name: Fetch the hub tooling (Virtual-Protocol/butler-skills at main)
uses: actions/checkout@v4
with:
repository: Virtual-Protocol/butler-skills
ref: main
path: .butler-skills-hub
persist-credentials: false

- name: Move the hub tooling out of the skill tree
shell: bash
run: |
set -eu
HUB="$RUNNER_TEMP/butler-skills"
rm -rf "$HUB"
mv .butler-skills-hub "$HUB"
echo "BUTLER_SKILLS_HUB=$HUB" >> "$GITHUB_ENV"

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install viem for the selector check
# scripts/check_selectors.mjs is an ES module: `import "viem"` resolves from its own
# directory upward, so viem is installed at the hub checkout's root.
shell: bash
run: npm i --no-save --no-audit --no-fund viem@2 >/dev/null
working-directory: ${{ env.BUTLER_SKILLS_HUB }}

- name: Validate the skill
shell: bash
env:
SKILL_PATH: ${{ inputs.path }}
STANDALONE: ${{ inputs.standalone }}
MAINTAINER_FLAG: ${{ inputs.maintainer == 'true' && '--maintainer' || '' }}
run: |
set -eu
FLAG=""
if [ "$STANDALONE" = "true" ]; then FLAG="--standalone"; fi
python3 "$BUTLER_SKILLS_HUB/scripts/validate.py" $FLAG $MAINTAINER_FLAG "$SKILL_PATH"

- name: Offline replay of duty.py
shell: bash
env:
SKILL_PATH: ${{ inputs.path }}
STANDALONE: ${{ inputs.standalone }}
FIXTURE: ${{ inputs.fixture }}
run: |
set -eu
if [ ! -f "$SKILL_PATH/duty.py" ]; then
echo "no duty.py in $SKILL_PATH — replay skipped (one-off-only skill)"
exit 0
fi
FLAG=""
if [ "$STANDALONE" = "true" ]; then FLAG="--standalone"; fi
# The hub checkout carries stub_bevo.py and every fixture, so nothing is downloaded.
python3 "$BUTLER_SKILLS_HUB/tests/replay.py" $FLAG "$SKILL_PATH" --fixture "$FIXTURE" --no-download
8 changes: 8 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ jobs:
- name: Build canary (this commit)
run: python3 scripts/build_index.py --channel canary --repo-tag "main-${{ github.sha }}"

# The standalone developer tools, served next to the channels so a skill repo never
# has to clone this registry to validate:
# tools/validate.py, tools/replay.py, tools/stub_bevo.py, tools/check_selectors.mjs,
# tools/fixtures/<every file under tests/fixtures/>
# replay.py downloads stub_bevo.py + fixtures from here when they are not beside it.
- name: Publish the standalone tools
run: python3 scripts/publish_tools.py

- uses: actions/configure-pages@v5

- uses: actions/upload-pages-artifact@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
run: python3 scripts/check_pins.py

- name: Validate all skills
# The bevo- prefix is maintainer-only. author_association is a trusted GitHub
# The butler- prefix is maintainer-only (bevo- is always refused). author_association is a trusted GitHub
# Actions context value (OWNER/MEMBER/COLLABORATOR for people with write access to
# this repo) — external, non-maintainer PRs run the strict, non-maintainer check.
env:
Expand Down
8 changes: 4 additions & 4 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "skills/bevo-copytrade"]
path = skills/bevo-copytrade
[submodule "skills/butler-copytrade"]
path = skills/butler-copytrade
url = https://github.com/Virtual-Protocol/butler-skill-copytrade
[submodule "skills/bevo-contract-call"]
path = skills/bevo-contract-call
[submodule "skills/butler-contract-call"]
path = skills/butler-contract-call
url = https://github.com/Virtual-Protocol/butler-skill-contract-call
4 changes: 2 additions & 2 deletions CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ Each skill is its own git repository, pinned here as a submodule at the commit o

| Skill | Version | Repo | Tier | Modes | Money-moving | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `bevo-contract-call` | 1.0.0 | [Virtual-Protocol/butler-skill-contract-call](https://github.com/Virtual-Protocol/butler-skill-contract-call/tree/v1.0.0) | on-demand | one-off, duty | yes | Build, dry-run and file any named contract call (approve/deposit/stake/claim) as one approval transaction. Use for "call/approve/interact with <contract>". |
| `bevo-copytrade` | 1.0.0 | [Virtual-Protocol/butler-skill-copytrade](https://github.com/Virtual-Protocol/butler-skill-copytrade/tree/v1.0.0) | on-demand | one-off, duty | yes | Copy another member's buys once or as a standing duty, one trade per leader event, never twice. Use for "copy/mirror/follow <@handle or wallet>". |
| `butler-contract-call` | 1.0.1 | [Virtual-Protocol/butler-skill-contract-call](https://github.com/Virtual-Protocol/butler-skill-contract-call/tree/v1.0.1) | on-demand | one-off | yes | Build, dry-run and file any named contract call (approve/deposit/stake/claim) as one approval transaction. Use for "call/approve/interact with <contract>". |
| `butler-copytrade` | 1.0.1 | [Virtual-Protocol/butler-skill-copytrade](https://github.com/Virtual-Protocol/butler-skill-copytrade/tree/v1.0.1) | on-demand | one-off, duty | yes | Copy another member's buys once or as a standing duty, one trade per leader event, never twice. Use for "copy/mirror/follow <@handle or wallet>". |
12 changes: 10 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,13 @@ submodule pointer (`.gitmodules` + the gitlink).
- Before every commit here: `python3 scripts/validate.py --all --maintainer`,
`python3 scripts/check_pins.py`, `python3 -m pytest tests -q`,
`python3 scripts/sync_readme.py --check`.
- Inside a skill repo: `python3 <butler-skills>/scripts/validate.py --standalone .` and
`python3 <butler-skills>/tests/replay.py --standalone . --fixture trade-activity-page`.
- Inside a skill repo (no registry checkout — the tools are published standalone):
`curl -sSLO https://virtual-protocol.github.io/butler-skills/tools/validate.py`,
`curl -sSLO https://virtual-protocol.github.io/butler-skills/tools/replay.py`, then
`python3 validate.py --standalone .` and
`python3 replay.py --standalone . --fixture trade-activity-page`. Skill-repo CI is the
composite action `.github/actions/validate` (`uses: Virtual-Protocol/butler-skills/.github/actions/validate@main`);
`scripts/publish_tools.py` lays the tools out under `dist/tools/` for Pages.
- Name prefixes: `butler-` is maintainer-only; `bevo-` is the container's bundled-skill
namespace and is refused. A skill is the delta over AGENTS.md — never restate what the
container already teaches.
20 changes: 13 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ checks. If the two ever disagree, the validator is right and `SKILL_STANDARD.md`
| Community skills | the author's own GitHub repo |
| The pin | this repo: `.gitmodules` + the `skills/<name>` gitlink, always at a tagged commit |
| The published index | GitHub Pages, built by `scripts/build_index.py` from the pinned checkouts |
| The standalone tools (`validate.py`, `replay.py`, `stub_bevo.py`, `check_selectors.mjs`, fixtures) | GitHub Pages `tools/`, laid out by `scripts/publish_tools.py`; skill-repo CI is the composite action `.github/actions/validate` |

Nothing under `skills/` is ever edited in a PR to this repo — a change to a skill is a
commit and a tag in the skill repo, then a PR here that moves the pointer.
Expand Down Expand Up @@ -55,16 +56,21 @@ commit and a tag in the skill repo, then a PR here that moves the pointer.

## Before you open a PR

In your skill repo (the template's CI runs exactly these):
In your skill repo — no registry checkout; the hub publishes its validator and replay
harness as standalone files, and the template's CI runs the same two checks through
`uses: Virtual-Protocol/butler-skills/.github/actions/validate@main`:

```bash
git clone --depth 1 https://github.com/Virtual-Protocol/butler-skills /tmp/butler-skills
python3 /tmp/butler-skills/scripts/validate.py --standalone .
python3 /tmp/butler-skills/tests/replay.py --standalone . --fixture trade-activity-page
curl -sSLO https://virtual-protocol.github.io/butler-skills/tools/validate.py
curl -sSLO https://virtual-protocol.github.io/butler-skills/tools/replay.py
python3 validate.py --standalone .
python3 replay.py --standalone . --fixture trade-activity-page
```

Both must exit 0 with no infrastructure and no Bevo account — see README.md §7 for what
each checks. Then tag: `git tag v<version> && git push origin main --tags`.
Both must exit 0 with no infrastructure and no Butler account — see README.md §7 for what
each checks (`replay.py` fetches `stub_bevo.py` and fixtures from the same site when they
are not beside it; keep the downloaded files out of the commit). Then tag:
`git tag v<version> && git push origin main --tags`.

In your fork of this registry:

Expand All @@ -74,7 +80,7 @@ git -C skills/<name> fetch --tags && git -C skills/<name> checkout v<version>
git add .gitmodules skills/<name>
git commit -s -m "skills: <name> <version>"
python3 scripts/check_pins.py # the pin rules CI will run
python3 scripts/validate.py --all # add --maintainer for a bevo- skill
python3 scripts/validate.py --all # add --maintainer for a butler- skill (bevo- is refused)
python3 -m pytest tests -q # the full local suite
```

Expand Down
Loading
Loading