Skip to content

Commit 08c6672

Browse files
TMHSDigitalclaude
andcommitted
feat(distribution): install the plugin from a slim, bot-built plugin-dist branch
The marketplace plugin source was "./", so `/plugin marketplace add` and every install fetched the whole repo (~30 MB tracked, ~83 MB pack) to deliver ~0.3 MB of skills. - scripts/build_plugin_dist.py assembles only what the plugin loads (skills/, claude/ incl. the blender-rules skill, rules/, snippets/, templates/, LICENSE files, .claude-plugin/plugin.json) plus a self-contained marketplace.json (source "./") and a short README. --check builds to a temp dir and verifies every skills dir has SKILL.md files, the rules skill is present and the tree stays under 2 MB (now 67 files, 0.27 MB). - release.yml: after the GitHub release, the built tree is force-pushed as a single orphan commit to `plugin-dist`, so the branch never accumulates history. The main-integrity ruleset targets only the default branch. - .claude-plugin/marketplace.json on main points its plugin source at {"source": "github", "repo": ..., "ref": "plugin-dist"}, so even users who added main install the slim build. validate-claude-packaging asserts that source and runs build_plugin_dist.py --check. - README Quick start and the landing install step use `/plugin marketplace add TMHSDigital/Blender-Developer-Tools@plugin-dist`. plugin-dist was bootstrapped from this tree before this commit (5a2622e), so the marketplace source resolves immediately. Proof (live, isolated CLAUDE_CONFIG_DIR so no real config was touched): `claude plugin marketplace add TMHSDigital/Blender-Developer-Tools@plugin-dist` then `claude plugin install blender-developer-tools@blender-developer-tools`: both succeed; marketplace clone 635 KB, plugin cache 443 KB, 17 SKILL.md (16 skills + blender-rules). Closes #340 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent fe0fcef commit 08c6672

6 files changed

Lines changed: 134 additions & 5 deletions

File tree

‎.claude-plugin/marketplace.json‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
"plugins": [
88
{
99
"name": "blender-developer-tools",
10-
"source": "./",
10+
"source": {
11+
"source": "github",
12+
"repo": "TMHSDigital/Blender-Developer-Tools",
13+
"ref": "plugin-dist"
14+
},
1115
"description": "Cursor and Claude Code skills, rules, snippets, and templates for Blender Python add-on and scripting development",
1216
"version": "0.142.4"
1317
}

‎.github/workflows/release.yml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,26 @@ jobs:
248248
--title "v${{ steps.new.outputs.version }}" \
249249
--notes-file "$RUNNER_TEMP/release-notes.md"
250250
251+
- name: Publish the slim plugin build to plugin-dist
252+
# Claude Code installs from this branch (marketplace.json on main points
253+
# its plugin source here). One orphan commit, force-pushed, so the
254+
# branch never grows history; only main is protected by the ruleset.
255+
if: steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' && steps.commit.outputs.stale != 'true'
256+
env:
257+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
258+
NEW_VERSION: ${{ steps.new.outputs.version }}
259+
run: |
260+
set -euo pipefail
261+
dist="$RUNNER_TEMP/plugin-dist"
262+
python3 scripts/build_plugin_dist.py --out "$dist"
263+
cd "$dist"
264+
git init -q -b plugin-dist
265+
git config user.name "github-actions[bot]"
266+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
267+
git add -A
268+
git commit -q -s -m "plugin build v$NEW_VERSION from $GITHUB_SHA"
269+
git push -q --force "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:plugin-dist
270+
251271
- name: Dispatch Pages
252272
if: steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' && steps.commit.outputs.stale != 'true'
253273
env:

‎.github/workflows/validate.yml‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,9 @@ jobs:
245245
errors.append('marketplace.json must list exactly the one plugin from plugin.json')
246246
elif entries[0].get('version') != version:
247247
errors.append(f"marketplace.json plugin version {entries[0].get('version')!r} != VERSION {version!r}")
248-
elif not os.path.isdir(os.path.join(entries[0].get('source', ''), 'skills')):
249-
errors.append('marketplace.json source must point at a directory containing skills/')
248+
elif entries[0].get('source') != {'source': 'github', 'repo': 'TMHSDigital/Blender-Developer-Tools', 'ref': 'plugin-dist'}:
249+
errors.append('marketplace.json plugin source must be the plugin-dist branch '
250+
'(github TMHSDigital/Blender-Developer-Tools, ref plugin-dist)')
250251
251252
# Claude Code discovers skills/<name>/SKILL.md plus every extra
252253
# directory in plugin.json "skills" (the generated blender-rules
@@ -294,6 +295,9 @@ jobs:
294295
- name: Check claude/blender-rules.md and the rules skill are regenerated from rules/*.mdc
295296
run: python3 scripts/build_claude_rules.py --check
296297

298+
- name: Check the plugin-dist build assembles, carries every skill, and stays slim
299+
run: python3 scripts/build_plugin_dist.py --check
300+
297301
- name: Check skill file references resolve outside a checkout
298302
run: python3 tests/check_skill_refs.py
299303

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,12 @@ git clone https://github.com/TMHSDigital/Blender-Developer-Tools.git
6464
- **Claude Code** — install as a plugin, then run `/skills` to see all 16 skills plus `blender-rules`:
6565

6666
```text
67-
/plugin marketplace add TMHSDigital/Blender-Developer-Tools
67+
/plugin marketplace add TMHSDigital/Blender-Developer-Tools@plugin-dist
6868
/plugin install blender-developer-tools@blender-developer-tools
6969
```
7070

71+
`@plugin-dist` is a generated branch holding only what the plugin loads (about 0.3 MB, rebuilt on every release), so the add does not clone the gallery renders, showcase props and history on `main`. Adding the repo without `@plugin-dist` still works; the plugin itself installs from `plugin-dist` either way.
72+
7173
Claude Code does not read Cursor `.mdc` rules, so the plugin ships them as the `blender-rules` skill (generated from `rules/`), which Claude loads when it writes or reviews bpy code. No second clone is needed. To keep the rules in context all the time instead, add `@/path/to/Blender-Developer-Tools/claude/blender-rules.md` to your project's `CLAUDE.md` from a checkout. Without the plugin, copy `skills/*` and `claude/skills/*` into your project's `.claude/skills/`.
7274
- **Get Blender** — download **5.2 LTS** (primary target) or **4.5 LTS** (supported fallback) from [blender.org/download/lts](https://www.blender.org/download/lts/); current stable lives at [blender.org/download](https://www.blender.org/download/). The `blender` command below is that binary — on macOS it is inside the app bundle at `/Applications/Blender.app/Contents/MacOS/Blender`.
7375
- **Run an example** — every example is a self-checking headless script (exit non-zero on failure, no GPU needed for the check):

‎scripts/build_plugin_dist.py‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env python3
2+
"""Assemble the slim Claude Code plugin tree published on the `plugin-dist` branch.
3+
4+
The repo root is ~30 MB of gallery renders, showcase props and history; the
5+
plugin needs ~1 MB of it. This copies only what the plugin uses into --out,
6+
as a self-contained marketplace (its marketplace.json points at "./"):
7+
8+
skills/ claude/ rules/ snippets/ templates/ LICENSE files
9+
.claude-plugin/plugin.json + marketplace.json, and a short README
10+
11+
python scripts/build_plugin_dist.py --out DIR # build
12+
python scripts/build_plugin_dist.py --check # build to a temp dir, verify, report size
13+
14+
release.yml force-pushes the built tree as a single orphan commit to
15+
`plugin-dist` on every release, so the branch never accumulates history.
16+
Users add it with `/plugin marketplace add TMHSDigital/Blender-Developer-Tools@plugin-dist`.
17+
"""
18+
from __future__ import annotations
19+
20+
import argparse
21+
import json
22+
import shutil
23+
import sys
24+
import tempfile
25+
from pathlib import Path
26+
27+
ROOT = Path(__file__).resolve().parent.parent
28+
DIRS = ("skills", "claude", "rules", "snippets", "templates")
29+
FILES = ("LICENSE", ".claude-plugin/plugin.json")
30+
MAX_BYTES = 2_000_000
31+
IGNORE = shutil.ignore_patterns("__pycache__", "*.pyc")
32+
33+
DIST_README = """# Blender Developer Tools (Claude Code plugin build)
34+
35+
Generated from https://github.com/TMHSDigital/Blender-Developer-Tools by
36+
`scripts/build_plugin_dist.py` on every release. Do not edit this branch; it
37+
is force-pushed. It carries only what the plugin loads: skills, the
38+
`blender-rules` skill generated from `rules/`, snippets, templates and licenses.
39+
Examples, the showcase and the gallery live on `main`.
40+
41+
```text
42+
/plugin marketplace add TMHSDigital/Blender-Developer-Tools@plugin-dist
43+
/plugin install blender-developer-tools@blender-developer-tools
44+
```
45+
"""
46+
47+
48+
def build(out: Path) -> int:
49+
if out.exists():
50+
shutil.rmtree(out)
51+
out.mkdir(parents=True)
52+
for d in DIRS:
53+
shutil.copytree(ROOT / d, out / d, ignore=IGNORE)
54+
for f in FILES:
55+
(out / f).parent.mkdir(parents=True, exist_ok=True)
56+
shutil.copy2(ROOT / f, out / f)
57+
market = json.loads((ROOT / ".claude-plugin" / "marketplace.json").read_text(encoding="utf-8"))
58+
for entry in market["plugins"]:
59+
entry["source"] = "./"
60+
(out / ".claude-plugin" / "marketplace.json").write_text(
61+
json.dumps(market, indent=2) + "\n", encoding="utf-8", newline="\n")
62+
(out / "README.md").write_text(DIST_README, encoding="utf-8", newline="\n")
63+
return sum(p.stat().st_size for p in out.rglob("*") if p.is_file())
64+
65+
66+
def verify(out: Path, size: int) -> list[str]:
67+
errors = []
68+
plugin = json.loads((out / ".claude-plugin" / "plugin.json").read_text(encoding="utf-8"))
69+
skill_dirs = [out / "skills"] + [out / s for s in plugin.get("skills", [])]
70+
for d in skill_dirs:
71+
if not any(d.glob("*/SKILL.md")):
72+
errors.append(f"{d.relative_to(out)} has no SKILL.md")
73+
if not (out / "claude" / "skills" / "blender-rules" / "SKILL.md").is_file():
74+
errors.append("blender-rules skill missing")
75+
if size > MAX_BYTES:
76+
errors.append(f"dist is {size} bytes, over the {MAX_BYTES} budget")
77+
return errors
78+
79+
80+
def main(argv=None) -> int:
81+
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
82+
ap.add_argument("--out", type=Path)
83+
ap.add_argument("--check", action="store_true")
84+
a = ap.parse_args(argv)
85+
if not a.out and not a.check:
86+
ap.error("give --out DIR or --check")
87+
with tempfile.TemporaryDirectory() as td:
88+
out = a.out or Path(td) / "dist"
89+
size = build(out)
90+
errors = verify(out, size)
91+
n = sum(1 for p in out.rglob("*") if p.is_file())
92+
for e in errors:
93+
print(f"::error::{e}", file=sys.stderr)
94+
print(f"plugin dist: {n} files, {size / 1e6:.2f} MB" + ("" if errors else " ok"))
95+
return 1 if errors else 0
96+
97+
98+
if __name__ == "__main__":
99+
sys.exit(main())

‎site.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"installSteps": [
2121
"Clone the repo: <code>git clone https://github.com/TMHSDigital/Blender-Developer-Tools</code>",
2222
"<strong>Cursor:</strong> copy <code>rules/*.mdc</code> into your project's <code>.cursor/rules/</code> — they auto-apply via scope globs; reference skills by name in chat",
23-
"<strong>Claude Code:</strong> run <code>/plugin marketplace add TMHSDigital/Blender-Developer-Tools</code> then <code>/plugin install blender-developer-tools@blender-developer-tools</code>; the rules come with it as the <code>blender-rules</code> skill",
23+
"<strong>Claude Code:</strong> run <code>/plugin marketplace add TMHSDigital/Blender-Developer-Tools@plugin-dist</code> then <code>/plugin install blender-developer-tools@blender-developer-tools</code>; the rules come with it as the <code>blender-rules</code> skill",
2424
"Grab <code>snippets/</code> and <code>templates/</code> as starting points for add-ons and headless batch jobs"
2525
]
2626
}

0 commit comments

Comments
 (0)