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
1 change: 1 addition & 0 deletions .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"skills/ai-mesh-cleanup/SKILL.md",
"skills/bake-high-to-low/SKILL.md",
"skills/engine-export-presets/SKILL.md",
"skills/extension-runtime-and-packaging/SKILL.md",
"skills/operators/SKILL.md",
"skills/ui-panels/SKILL.md",
"skills/custom-properties/SKILL.md",
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ a `.cursor-plugin/plugin.json` manifest so the ecosystem drift checker
classifies it as a `cursor-plugin`. This is content the AI loads when the user
asks Blender questions or works on Blender add-ons in Cursor or Claude Code.

The content base is 16 skills, 9 rules, 3 templates, 27 snippets, 64
The content base is 17 skills, 9 rules, 3 templates, 28 snippets, 65
examples, and 76 showcase pieces (counts are CI-enforced against README.md)
and the manifest). The full inventory tables and per-item purposes live in
`CLAUDE.md`. Example anatomy and authoring rules: copy `examples/bmesh-gear/`;
Expand All @@ -32,7 +32,7 @@ in `docs/VISUAL-STYLE.md`; the canonical run prompt is

```
Blender-Developer-Tools/
skills/<skill-name>/SKILL.md # 16 skill files
skills/<skill-name>/SKILL.md # 17 skill files
rules/<rule-name>.mdc # 9 rule files
templates/<template-name>/ # 3 starter templates
snippets/<snippet-name>.py # 27 standalone Python snippets
Expand Down
19 changes: 11 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ The **Blender Developer Tools** repository is at **v0.144.3**. It packages skill
## Repository Architecture

```
skills/<skill-name>/SKILL.md - AI workflow definitions, 16 total
skills/<skill-name>/SKILL.md - AI workflow definitions, 17 total
rules/<rule-name>.mdc - Anti-pattern rules, 9 total
templates/<template-name>/ - Starter projects, 3 total
snippets/<snippet-name>.py - Standalone code patterns, 27 total
examples/<name>/ - Runnable smoke-gated examples, 64 total (+ gallery.json)
snippets/<snippet-name>.py - Standalone code patterns, 28 total
examples/<name>/ - Runnable smoke-gated examples, 65 total (+ gallery.json)
showcase/<name>/ - Budget-conformance props, 76 pieces, each with a gallery `category` (sibling of examples/; see showcase/README.md § Categories)
scripts/build_gallery.py - Regenerates docs/gallery/ from examples/gallery.json + showcase/gallery.json
scripts/site/ - Vendored landing-page build (Jinja2); tokens.css is the shared palette
Expand All @@ -30,7 +30,7 @@ docs/gallery/ - Committed generated gallery pages + hero render
VERSION - Source of truth for the repo version
```

## Skills (16)
## Skills (17)

| Skill | Purpose |
| --- | --- |
Expand All @@ -50,6 +50,7 @@ VERSION - Source of truth for the repo version
| drivers-and-app-handlers | Driver expressions, `driver_namespace`, application handlers including the new 5.1 `exit_pre` |
| bl-info-migration | Three-step migration from legacy `bl_info` to Extensions Platform, dual-format pattern |
| vse-python | VSE timeline from Python: `.strips` vs `.sequences`, `new_effect` kwargs, 5.2 COLOR `width`/`height` bake |
| extension-runtime-and-packaging | Bundled wheels, `extension_path_user` data that survives upgrades, `online_access`, `bl_ext` names, `--command extension` build in CI |

## Rules (9)

Expand Down Expand Up @@ -95,7 +96,7 @@ matching its `globs` does. Changing a glob changes when the rule fires.
- Unity / Godot / Unreal glTF export via the engine-export-presets contract
- Explicit exit codes matching `headless-batch-script-template` (0, then 2+)

## Snippets (27)
## Snippets (28)

Small standalone `.py` files at `snippets/<name>.py`, each 5 to 75 lines.

Expand All @@ -105,13 +106,15 @@ v0.2.0: Principled BSDF material, driver-with-custom-function via `driver_namesp

AI asset pipeline track: `decimate_to_budget.py`, `convex_hull_collider.py`, `lod_chain.py` (helper duplicated, not imported), `gltf_draco_export.py`, `export_preset_unity.py`, `export_preset_godot.py`, `export_preset_unreal.py`, `setup_bake_target_image.py`, `bake_normal_high_to_low.py`, `save_baked_image.py`.

## Examples (64)
Extensions runtime: `extension-user-data.py` (`extension_path_user` settings file and an `online_access` guard).

## Examples (65)

Runnable scripts at `examples/<name>/`, each asserting a real API contract with
deterministic checks (exit non-zero on failure) and optionally rendering a still via
`--output`. All of them run headless on Blender 5.2 LTS and 4.5 LTS in `blender-smoke.yml` (5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch);
**56 of the 64 ship a render in the site gallery** at `docs/gallery/`. The other
eight are **check-only**: they carry no `--output` path, no gallery entry, and no
**56 of the 65 ship a render in the site gallery** at `docs/gallery/`. The other
nine are **check-only**: they carry no `--output` path, no gallery entry, and no
hero asset. The criterion is whether the contract is expressible in pixels. An
example is check-only when its witness is a data or state fact that no scene
redesign can make visible — a datablock name, an RNA attribute's presence, a
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ The drift-check workflow enforces these on every push and PR.

## Aggregate Counts

`README.md` declares aggregate counts (e.g. "16 skills, 9 rules, 3 templates, 27 snippets, 64 examples, and 76 showcase pieces"). The `validate-counts` job in `.github/workflows/validate.yml` enforces these substrings against the filesystem on every push and PR. Showcase pieces are counted separately from examples. When you add or remove content, update the README counts in the same commit.
`README.md` declares aggregate counts (e.g. "17 skills, 9 rules, 3 templates, 28 snippets, 65 examples, and 76 showcase pieces"). The `validate-counts` job in `.github/workflows/validate.yml` enforces these substrings against the filesystem on every push and PR. Showcase pieces are counted separately from examples. When you add or remove content, update the README counts in the same commit.

## Pull Request Process

Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
</p>

<p align="center">
<strong>16 skills</strong> &nbsp;&bull;&nbsp; <strong>9 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>27 snippets</strong> &nbsp;&bull;&nbsp; <strong>64 examples</strong> &nbsp;&bull;&nbsp; <strong>76 showcase pieces</strong>
<strong>17 skills</strong> &nbsp;&bull;&nbsp; <strong>9 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>28 snippets</strong> &nbsp;&bull;&nbsp; <strong>65 examples</strong> &nbsp;&bull;&nbsp; <strong>76 showcase pieces</strong>
</p>

<p align="center">
Expand All @@ -44,7 +44,7 @@

## Overview

This repository ships **16 skills, 9 rules, 3 templates, 27 snippets, 64 examples, and 76 showcase pieces** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
This repository ships **17 skills, 9 rules, 3 templates, 28 snippets, 65 examples, and 76 showcase pieces** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.

The content is consumed by AI coding agents reading these files directly from a checkout — **there is no MCP server in this repository, and none is required**. Both agents install it as a plugin (see [Quick start](#quick-start)): Cursor loads the skills and applies `rules/*.mdc` wherever their scope globs match; Claude Code loads the skills and gets the rules through a generated `blender-rules` skill, since it does not read Cursor `.mdc` files. An agent picks a skill up when its description matches the task. Any agent that can read files in a workspace can use it the same way. There is no build step for the content — edit the Markdown and Python files directly.

Expand All @@ -63,14 +63,14 @@ The content is consumed by AI coding agents reading these files directly from a
git clone https://github.com/TMHSDigital/Blender-Developer-Tools.git
```

- **Cursor** — install it as a local plugin from the slim `plugin-dist` branch, then run **Developer: Reload Window**. **Customize** lists the 16 skills and 9 rules; the rules apply by glob scope and the agent loads a skill when its description matches the task.
- **Cursor** — install it as a local plugin from the slim `plugin-dist` branch, then run **Developer: Reload Window**. **Customize** lists the 17 skills and 9 rules; the rules apply by glob scope and the agent loads a skill when its description matches the task.

```bash
git clone --branch plugin-dist --single-branch https://github.com/TMHSDigital/Blender-Developer-Tools.git ~/.cursor/plugins/local/blender-developer-tools
```

(On Windows the folder is `%USERPROFILE%\.cursor\plugins\local\`. Cursor skips symlinks that point outside that folder, so clone or copy rather than link.) Teams can import the repository as a team marketplace instead; it carries `.cursor-plugin/marketplace.json`. For one project only, copy `skills/*` into the project's `.cursor/skills/` and `rules/*.mdc` into `.cursor/rules/` from a checkout. Copying only the rules gives you no skills.
- **Claude Code** — install as a plugin, then run `/skills` to see all 16 skills plus `blender-rules`:
- **Claude Code** — install as a plugin, then run `/skills` to see all 17 skills plus `blender-rules`:

```text
/plugin marketplace add TMHSDigital/Blender-Developer-Tools@plugin-dist
Expand Down Expand Up @@ -105,7 +105,7 @@ Releases ship often; [CHANGELOG.md](CHANGELOG.md) lists what each one changed.

## Falsifiers

Every one of the 64 examples carries a **falsifier**: a flag that changes the
Every one of the 65 examples carries a **falsifier**: a flag that changes the
input so a real assertion fails. It never disables the assertion, skips the
check, or short-circuits to an error — it feeds the script something the
contract says must not pass, and the same check that guards the happy path
Expand Down Expand Up @@ -133,7 +133,7 @@ per-script exit-code model, are in

## Examples and showcase

**64 examples** in [`examples/`](examples/) are runnable, self-checking scripts: each asserts one API contract, carries a falsifier, and runs headless on Blender 5.2 LTS and 4.5 LTS in the `blender-smoke` workflow (5.1 on the weekly cron or by manual dispatch). Those whose contract is visible also render a still. **76 showcase pieces** in [`showcase/`](showcase/) exist to show the skills working together at production scale: each is a single headless script that models a game prop, then runs it through the same cleanup, bake, LOD, collider and export steps the skills teach, and fails if a measured budget (triangle counts, colliders, materials, real-world size) is missed. They are not API examples. Conventions: [`showcase/README.md`](showcase/README.md).
**65 examples** in [`examples/`](examples/) are runnable, self-checking scripts: each asserts one API contract, carries a falsifier, and runs headless on Blender 5.2 LTS and 4.5 LTS in the `blender-smoke` workflow (5.1 on the weekly cron or by manual dispatch). Those whose contract is visible also render a still. **76 showcase pieces** in [`showcase/`](showcase/) exist to show the skills working together at production scale: each is a single headless script that models a game prop, then runs it through the same cleanup, bake, LOD, collider and export steps the skills teach, and fails if a measured budget (triangle counts, colliders, materials, real-world size) is missed. They are not API examples. Conventions: [`showcase/README.md`](showcase/README.md).

Browse both, with filters, full-size renders and each script's README, in the **[gallery](https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/)**.

Expand All @@ -151,11 +151,11 @@ Browse both, with filters, full-size renders and each script's README, in the **
## How content is organized

```
skills/<name>/SKILL.md - 16 skill files, YAML frontmatter, one canonical pattern each
skills/<name>/SKILL.md - 17 skill files, YAML frontmatter, one canonical pattern each
rules/<name>.mdc - 9 rule files, anti-pattern + correction
templates/<name>/ - 3 template directories (extension-addon-template, headless-batch-script-template, ai-asset-pipeline-template)
snippets/<name>.py - 27 standalone Python snippets, 5 to 75 lines each
examples/<name>/ - 64 example directories: script, README with exit codes and falsifier
snippets/<name>.py - 28 standalone Python snippets, 5 to 75 lines each
examples/<name>/ - 65 example directories: script, README with exit codes and falsifier
showcase/<name>/ - 76 showcase pieces: budget-gated game props, script and README
claude/ - generated Claude Code copies of the rules (blender-rules skill + import file)
```
Expand Down
2 changes: 1 addition & 1 deletion docs/new-example-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ metadata — so the render would be identical whether the API held or broke. The
"redesign the scene until failure would be visible" instruction above is the test:
attempt it first, and only when it cannot succeed in principle does the example
become check-only. The exception is for contracts that are invisible, not for
renders that are hard. Eight of the 64 examples currently qualify, and `CLAUDE.md`
renders that are hard. Nine of the 65 examples currently qualify, and `CLAUDE.md`
carries the same rule. A check-only example is otherwise a full example: it still
asserts a real contract, still exits non-zero on failure, still carries a falsifier,
and still takes a `tests/smoke/catalog.json` row so it runs on every PR.
Expand Down
100 changes: 100 additions & 0 deletions examples/extension-package-lifecycle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Extension package lifecycle

Takes [`templates/extension-addon-template`](../../templates/extension-addon-template/)
through what an extension meets after its first run: validate, build, a
repository listing, install, an upgrade over the top, removal, and the
online-access gate. Every step runs a child Blender against a throwaway user
directory (`BLENDER_USER_RESOURCES`), so the real install is never touched.
Check-only: the witnesses are exit codes, zip members and paths, so a render
would look the same whether the contract held or broke.

Follows [`extension-runtime-and-packaging`](../../skills/extension-runtime-and-packaging/SKILL.md),
whose snippet is [`extension-user-data.py`](../../snippets/extension-user-data.py).
Scaffolding matches [`eval-mesh-datablock-name`](../eval-mesh-datablock-name/)
(`check()` steps return codes, argparse falsifier flags, FATAL wrapper).

**What it witnesses:**

- `blender --command extension validate` and `build` accept the template, and
the zip carries `blender_manifest.toml` and `__init__.py` at its root.
- **`validate` is not a release gate.** With `wheels = ["./wheels/not_shipped-1.0-py3-none-any.whl"]`
appended and no such file, `validate` exits 0 and `build` exits 1 with no
zip. Only the build opens the files the manifest names.
- `extension server-generate --repo-dir` writes `index.json` listing the
package with `archive_url` `./example_addon-0.1.0.zip`.
- Installed with `extension install-file -r user_default`, the add-on imports
as `bl_ext.user_default.example_addon`, and
`bpy.utils.extension_path_user(__package__)` names
`extensions/.user/user_default/example_addon`, beside the install tree
`extensions/user_default/example_addon`, not inside it.
- Installing 0.2.0 over 0.1.0 replaces the install tree (a file written
beside `__file__` is gone) and keeps the user directory (`settings.json`
survives). `extension remove` deletes the user directory too.
- `bpy.app.online_access` is `False` in a factory-startup child and `True`
under `--online-mode`.

**What failure each check would catch:**

- exit 3 — the template stopped validating or building, or the zip lost its manifest
- exit 4 — a package whose wheel does not exist passed the release gate
(`--validate-only` lands here: it gates on `validate` alone); also exit 4 if
`validate` starts rejecting the missing wheel, because the trap this example
names would then be gone
- exit 5 — `server-generate` wrote no listing, or the listing does not name the package
- exit 6 — install, import, upgrade or remove failed, or `__package__` is not
`bl_ext.user_default.example_addon`
- exit 7 — user data lives inside the install tree, did not survive the
upgrade, or survived removal (`--data-next-to-file` lands here: it stores
data beside `__file__`)
- exit 8 — `online_access` is not `False` by default and `True` under `--online-mode`

## Re-verified

| Measurement | 4.5.11 | 5.1.2 | 5.2.1 |
| --- | --- | --- | --- |
| validate / build, template | 0 / 0 | 0 / 0 | 0 / 0 |
| validate / build, missing wheel | 0 / 1 | 0 / 1 | 0 / 1 |
| `__package__` | `bl_ext.user_default.example_addon` | same | same |
| user data kept by 0.2.0 upgrade | yes | yes | yes |
| file beside `__file__` kept by upgrade | no | no | no |
| user data after `remove` | deleted | deleted | deleted |
| `online_access` default / `--online-mode` | False / True | False / True | False / True |
| default exit | 0 | 0 | 0 |
| `--validate-only` / `--data-next-to-file` exit | 4 / 7 | 4 / 7 | 4 / 7 |

## API reference

- [`bpy.utils.extension_path_user`](https://docs.blender.org/api/current/bpy.utils.html#bpy.utils.extension_path_user)
([4.5 LTS](https://docs.blender.org/api/4.5/bpy.utils.html#bpy.utils.extension_path_user))
- [`bpy.app.online_access`](https://docs.blender.org/api/current/bpy.app.html#bpy.app.online_access)
- [Extensions command line arguments](https://docs.blender.org/manual/en/latest/advanced/command_line/extension_arguments.html)

## Run

```bash
blender --background --python extension_package_lifecycle.py --
blender --background --python extension_package_lifecycle.py -- --validate-only
blender --background --python extension_package_lifecycle.py -- --data-next-to-file
```

The script starts about a dozen child Blender processes, so it takes longer
than most examples.

## Exit codes

| Code | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Uncaught exception (FATAL wrapper) |
| 2 | argparse / usage |
| 3 | Template does not validate or build, or the zip lacks its manifest |
| 4 | Missing-wheel package passed the release gate (`--validate-only` lands here) |
| 5 | `server-generate` listing missing or wrong |
| 6 | Install, import, upgrade or remove failed, or `__package__` wrong |
| 7 | User data inside the install tree, lost on upgrade, or kept after removal (`--data-next-to-file` lands here) |
| 8 | `online_access` default or `--online-mode` value wrong |

The `blender-smoke` workflow runs the check on Blender 5.2 LTS and 4.5 LTS
(5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch).
Smoke passes no extra flags on the happy path. Its catalog falsifiers are
`--validate-only` (expects exit 4) and `--data-next-to-file` (expects exit 7).
Loading
Loading