{"id":48880010,"url":"https://github.com/binary-core-llc/bowerbot","last_synced_at":"2026-05-27T07:02:43.964Z","repository":{"id":349574453,"uuid":"1183588107","full_name":"binary-core-llc/bowerbot","owner":"binary-core-llc","description":"🐦 AI agent that assembles production-ready OpenUSD scenes from natural language, so you focus on creative decisions, not pipeline work","archived":false,"fork":false,"pushed_at":"2026-05-24T00:46:22.000Z","size":1470,"stargazers_count":21,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-24T02:29:27.507Z","etag":null,"topics":["3d","ai","ai-agents","digital-twin","openusd","pipeline","python","scene-assembly","usd","usdz"],"latest_commit_sha":null,"homepage":"https://binarycore.us/bowerbot","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/binary-core-llc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"binary-core-llc"}},"created_at":"2026-03-16T18:58:39.000Z","updated_at":"2026-05-24T00:46:17.000Z","dependencies_parsed_at":"2026-04-26T01:00:59.592Z","dependency_job_id":null,"html_url":"https://github.com/binary-core-llc/bowerbot","commit_stats":null,"previous_names":["binary-core-llc/bowerbot"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/binary-core-llc/bowerbot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binary-core-llc%2Fbowerbot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binary-core-llc%2Fbowerbot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binary-core-llc%2Fbowerbot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binary-core-llc%2Fbowerbot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/binary-core-llc","download_url":"https://codeload.github.com/binary-core-llc/bowerbot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binary-core-llc%2Fbowerbot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33554780,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","ai","ai-agents","digital-twin","openusd","pipeline","python","scene-assembly","usd","usdz"],"created_at":"2026-04-16T02:07:36.947Z","updated_at":"2026-05-27T07:02:43.913Z","avatar_url":"https://github.com/binary-core-llc.png","language":"Python","funding_links":["https://github.com/sponsors/binary-core-llc"],"categories":["Libraries \u0026 Tools"],"sub_categories":["Technical Explanation"],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"docs/mascot.png\" alt=\"BowerBot\" width=\"200\"\u003e\n\n# BowerBot\n\n**AI agent for OpenUSD.**\n\n**From empty scene to a production-ready OpenUSD stage.**\n\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Python](https://img.shields.io/badge/Python-3.12+-blue)](https://www.python.org/)\n[![OpenUSD](https://img.shields.io/badge/OpenUSD-26.x-green)](https://openusd.org)\n[![YouTube](https://img.shields.io/badge/YouTube-Tutorials-red?logo=youtube)](https://www.youtube.com/playlist?list=PLhNtBS4KXazZk_LSZfMHlzmNQPqHc4CMb)\n[![Built by Binary Core LLC](https://img.shields.io/badge/Built%20by-Binary%20Core%20LLC-black)](https://binarycore.us)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)\n\n\u003c/div\u003e\n\n---\n\n## 🐦 Meet BowerBot\n\nIn the rainforests of Australia and New Guinea lives one of nature's most remarkable architects: the **bowerbird**.\n\nInstead of relying on appearance, the bowerbird **collects, curates, and arranges objects** from its environment into a carefully constructed 3D composition. Every object is chosen. Every placement is intentional.\n\n**BowerBot brings that same idea to OpenUSD.**\n\nBowerBot is an **AI agent for OpenUSD**, a conversational interface that helps any team using OpenUSD go from an empty scene to a production-ready stage by:\n- finding assets from any connected source (Sketchfab, local disk, company DAM, or any custom provider)\n- placing them with spatial awareness\n- authoring materials inline in ASWF-compliant asset folders\n- setting up native USD lighting (sun, dome, point, area, disk, tube)\n- validating technical correctness (units, hierarchy, references, bindings)\n- packaging the result for any USD-compatible runtime\n\n---\n\n## 🎯 What BowerBot Is (and Is Not)\n\n**BowerBot is:**\n- A USD authoring agent for any OpenUSD pipeline: VFX, AEC, simulation, spatial computing, digital twins, robotics, e-commerce 3D\n- A conversational interface for the full authoring surface: assets, lighting, materials, validation, packaging\n- A fast way to go from 0 → production-ready scene\n- A pipeline assistant that handles technical correctness (units, hierarchy, references, bindings)\n- A pipeline guardian that catches asset issues **before** they reach production\n- Extensible by design: new asset sources, DCCs, and domains plug in as skills\n\n**BowerBot is NOT:**\n- A final scene generator\n- A replacement for DCC tools like Maya or Omniverse\n- A system that produces perfect composition or artistic layouts\n\nScenes generated by BowerBot are meant to be **opened, reviewed, and refined** in your DCC.\n\nThink of it as:\n\u003e **\"Block out the scene instantly, then refine like a pro.\"**\n\n### Pipeline Quality Built In\n\nBowerBot enforces [ASWF USD standards](https://github.com/usd-wg/assets/blob/main/docs/asset-structure-guidelines.md) at every step, not just placing assets. Fixable mismatches (non-canonical folder names, external dependencies) are auto-normalized on intake so the project copy is always self-contained. Production-required invariants are validated at intake too: assets with non-identity root transforms (Maya pivot dance, unfrozen DCC exports) are refused with a clear message and the option to bake transforms into vertex data on the project copy without touching the source. Unfixable violations (wrong root prim type, missing `defaultPrim`, incorrect `metersPerUnit`, circular references, missing dependencies) are caught **at assembly time** with a clear message about what's wrong and how to fix it.\n\n\u003e **\"The cheapest bug to fix is the one you catch before it enters the pipeline.\"**\n\n---\n\n## ✨ What It Does\n\nWatch BowerBot build real scenes end-to-end on the **[demo playlist on YouTube](https://www.youtube.com/playlist?list=PLhNtBS4KXaza-3Sn4ggJLH-6ujRZ3Iapd)**. Each video walks through a project across asset discovery, placement, materials, lighting, validation, and packaging.\n\nOpen the resulting `.usda` in Maya, usdview, Omniverse, Isaac Sim, or any USD-compatible tool to refine composition, lighting, materials, or downstream pipeline steps.\n\nProjects are persistent. Close the session, come back later, and continue where you left off.\n\n---\n\n## ✨ Features\n\n- 📦 **OpenUSD native**: references, `defaultPrim`, `metersPerUnit`, `upAxis`, all correct out of the box. BowerBot authors a single `scene.usda` as the live working layer; `save_scene_snapshot(name)` writes a flattened, DCC-stripped `\u003cname\u003e.usda` alongside whenever you want to publish a frozen version\n- 🎭 **USD variant sets**: asset-level (material, geometry/LOD, configuration, attribute) live in the asset's `variants.usda`; scene-level (lighting moods, light-type swap, model selection at a placement) live inline in `scene.usda`. Architectural invariants protect every mutation: auto-promote existing references into a model-selection variant on first add, auto-demote back to a direct ref when the set is removed, cascading orphan-opinion cleanup on prim delete/rename, automatic texture-asset staging for Asset-typed attribute values, and suspect-set detection that flags variants that collapse to a single choice\n- 🏗️ **ASWF-compliant asset folders**: geometry, materials, and lighting split into a root + layer files, per the [USD Working Group guidelines](https://github.com/usd-wg/assets/blob/main/docs/asset-structure-guidelines.md). Heavy `geo.usda` composes via a **payload arc** for lazy-load (city-scale digital twins, robot fleets, large layouts open instantly); `mtl.usda` / `lgt.usda` / `contents.usda` use references\n- 🧳 **Self-contained intake**: non-canonical source folders are detected via USD composition, canonicalized (`root.usd` → `\u003cfolder\u003e.usda`), and external dependencies (textures, sublayers) are localized into the asset folder so the project copy is always portable\n- 🎨 **Material binding**: apply MaterialX or existing `.usda` materials to specific mesh parts; procedural materials author hybrid MaterialX + UsdPreviewSurface outputs so they render across studio renderers (Renderman, Arnold), Hydra Storm, Apple RealityKit / AR Quick Look, and Isaac Sim\n- 💡 **Native USD lighting**: sun, dome, point, area, disk, and tube lights at scene or asset level, with optional UsdLux `light:link` collections so a rim light, kicker, or product-shot key only illuminates the prims you target\n- 🧩 **Automatic unit handling**: assets in cm, mm, or inches are scaled correctly at reference time\n- 📐 **Geometry-aware placement**: bounding-box resolved positions for surface, above, below, or nested placements\n- 🔌 **Pluggable skills**: connect any asset source (Sketchfab, PolyHaven, company DAM, or build your own)\n- 🧠 **Multi-LLM support**: OpenAI, Anthropic, and any provider via [litellm](https://docs.litellm.ai/)\n- 📁 **Project-based workflow**: one folder per scene, resumable across sessions\n- ✅ **Scene validation**: `defaultPrim`, units, up-axis, reference resolution, and material binding checks plus USD's modern `UsdValidation` framework (the same validators behind `usdchecker`) run automatically on intake and on `validate_scene`\n- 📦 **USDZ packaging**: standard USDZ for Omniverse, Isaac Sim, Unreal, Unity, web viewers, and any USD consumer; opt-in Apple AR Quick Look strict-subset pre-validation when shipping to iOS Files / Safari / iMessage / macOS Quick Look / Vision Pro\n- 🏗️ **Onboarding wizard**: zero-config setup in 60 seconds\n\nBuilt on [OpenUSD](https://openusd.org), the [ASWF USD Working Group](https://wiki.aswf.io/display/WGUSD) standards, and the [Alliance for OpenUSD (AOUSD)](https://aousd.org/) core spec driven by Pixar, Apple, NVIDIA, and others.\n\n---\n\n## 🚀 Quick Start\n\n### Install\n\nThere are two paths for end users (pick whichever fits your environment), plus a separate path for contributors who want to modify BowerBot itself.\n\n#### End users, Option A: uv (recommended)\n\n[uv](https://docs.astral.sh/uv/) manages Python and isolated tool environments for you, so you do not need to install or pin Python yourself.\n\n```bash\nuv tool install bowerbot\n```\n\n#### End users, Option B: pip\n\nIf you already maintain a Python 3.12+ environment, plain `pip` works:\n\n```bash\npip install bowerbot\n```\n\n#### Contributors: developer install\n\nTo modify BowerBot itself, clone the repo and let uv manage the dev environment:\n\n```bash\ngit clone https://github.com/binary-core-llc/bowerbot.git\ncd bowerbot\nuv sync\nuv run bowerbot onboard\n```\n\n### First-time setup\n\n```bash\nbowerbot onboard\n```\n\nThe wizard asks for your LLM API key, your asset library directory, and your projects directory, then writes `~/.bowerbot/config.json`. One file, one place, no `.env`.\n\n### Create a project and start building\n\n```bash\nbowerbot new \"Coffee Shop\"\nbowerbot open coffee_shop\n```\n\nTo plug in asset providers like Sketchfab, see [Skills](#-skills) below.\n\n---\n\n## 📺 Tutorials\n\nNew to BowerBot? Watch the **[tutorial playlist on YouTube](https://www.youtube.com/playlist?list=PLhNtBS4KXazZk_LSZfMHlzmNQPqHc4CMb)** for setup walkthroughs, scene building demos, and tips for working with USD pipelines.\n\n---\n\n## 🩹 Troubleshooting\n\nStuck on something? See **[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** for common issues: working alongside a DCC, skill installation, CLI rendering on Windows, and LLM tool-calling pitfalls.\n\n---\n\n## 🛠️ CLI Commands\n\n| Command | Description |\n|---------|-------------|\n| `bowerbot new \"name\"` | Create a new project |\n| `bowerbot open name` | Open a project and start chatting |\n| `bowerbot list` | Show all projects |\n| `bowerbot chat` | Auto-detect project in current directory |\n| `bowerbot build \"prompt\"` | Single-shot build (auto-creates project) |\n| `bowerbot skills` | List scene builder tools and enabled skills |\n| `bowerbot info` | Show current configuration |\n| `bowerbot onboard` | First-time setup wizard |\n\n---\n\n## 📁 Projects\n\nEach project is a self-contained folder with metadata, scene, assets, and packaged output in one place:\n\n```\nscenes/coffee_shop/\n  project.json    # Metadata: name, created_at, updated_at, scene_file\n  scene.usda      # The USD stage (references only, clean and readable)\n  scene.usdz      # Packaged output (Apple Vision Pro, Omniverse, etc.)\n  assets/         # ASWF folders + self-contained USDZs used by this scene\n  textures/       # Scene-level textures (HDRI maps for DomeLights, etc.)\n```\n\nProjects are resumable. Close the session, come back later, and continue where you left off:\n\n```\n$ bowerbot open coffee_shop\n# Project: Coffee Shop\n# Scene: scene.usda (5 object(s))\n\nYou: Show me the scene structure\nBowerBot: Scene has 5 objects...\n\nYou: Remove Table_03\nBowerBot: Removed /Scene/Furniture/Table_03\n```\n\n---\n\n## 🔄 How It Works\n\nBowerBot is conversational: you tell it what you want and it uses the right tools to build your scene. Behind the scenes, it manages asset discovery, USD composition, materials, lighting, and more.\n\n### Asset Discovery\n\nBowerBot searches for assets across all connected sources, prioritizing what's already available:\n\n1. **Local assets first**: BowerBot checks your local asset directory (`assets_dir` in config.json) for USD files (`.usd`, `.usda`, `.usdc`, `.usdz`). This includes anything you've exported from Maya, Houdini, Blender, or any DCC tool, as well as assets previously downloaded from cloud providers.\n\n2. **Cloud providers if needed**: If the asset isn't found locally, BowerBot searches connected providers (any installed skill, e.g. Sketchfab) and downloads the asset to your local directory.\n\n3. **All downloads are cached locally**: Once an asset is downloaded from any source, it lives in your `assets_dir` and is available for all future projects without re-downloading.\n\n### Scene Assembly\n\nWhen you ask BowerBot to place an asset, it routes by what the source looks like and always produces a self-contained ASWF folder in the project:\n\n- **Folder with a detectable root** (canonical `wall/wall.usda`, or non-canonical `wall/root.usd` + `wall/geo.usd` + `wall/mtl.usd`): the root is identified via USD composition (the file no sibling depends on), the folder is copied into the project, the root is canonicalized to `\u003cfolder\u003e.usda`, sibling references are rewritten, and any externally-referenced textures or layers are localized into the folder so the output is portable.\n- **Loose USD geometry** (`.usd`, `.usda`, `.usdc` from your DCC exports): wrapped in a fresh ASWF folder named after the file stem, producing `\u003cstem\u003e/\u003cstem\u003e.usda` + `geo.usda`.\n- **USDZ files** (from Sketchfab, DAMs, etc.): placed as-is since they're already self-contained.\n\nWhen an asset can't be safely intaken (missing external dependencies, or a folder with multiple independent USDs and no clear root), BowerBot refuses with a message naming the conflict instead of guessing.\n\n### Material Workflow\n\nWhen you apply materials to an asset, BowerBot writes them into the asset folder's `mtl.usda`, not the scene file. The scene stays clean with only references:\n\n```\nYou: Apply wood material to the table top\nBowerBot: [searches local assets for \"wood\" materials]\n         [discovers mesh parts: table top, legs, frame]\n         [writes material definition + binding into assets/table/mtl.usda]\n         Bound /table/mtl/wood_varnished to table top\n```\n\nThe result is a production-ready asset folder:\n```\nassets/single_table/\n  single_table.usda   \u003c- root (references geo + mtl)\n  geo.usda            \u003c- geometry (untouched from source)\n  mtl.usda            \u003c- materials inline + bindings\n```\n\n### Scene Output\n\nThe scene file (`scene.usda`) contains only references and lights: no material data, no geometry copies, no sublayers. Clean and readable:\n\n```usda\ndef Xform \"Scene\" (kind = \"assembly\") {\n    def Xform \"Furniture\" {\n        def Xform \"Table_01\" {\n            xformOp:translate = (5, 0, 4)\n            xformOp:scale = (0.01, 0.01, 0.01)\n            def Xform \"asset\" (\n                references = @assets/single_table/single_table.usda@\n            ) { }\n        }\n    }\n    def Xform \"Lighting\" {\n        def DistantLight \"Sun_01\" { ... }\n        def DomeLight \"Environment_01\" { ... }\n    }\n}\n```\n\nOpen it in Maya, usdview, Omniverse, Isaac Sim, or any USD-compatible tool to refine.\n\n---\n\n## 🔌 Skills\n\nSkills extend BowerBot with **external** asset providers, DCC connectors, and simulation runtimes. Each skill is a separate Python package, discovered at runtime through Python entry points (`bowerbot.skills`). The skill SDK lives in `bowerbot.skills`; skills themselves ship and version on their own. See [Installing a skill](#installing-a-skill) below for the walkthrough.\n\n### Scene Builder Tools\n\nBowerBot's core tools for building USD scenes:\n\n| Tool | Description |\n|------|-------------|\n| `create_stage` | Initialize a new USD scene with standard hierarchy |\n| `place_asset` | Add an asset (auto-creates ASWF folder for loose geometry) |\n| `place_asset_inside` | Nest an asset inside an ASWF container's `contents.usda` |\n| `move_asset` | Reposition an existing object without creating duplicates |\n| `compute_grid_layout` | Calculate evenly spaced positions |\n| `list_scene` | Show current scene with positions and bounding boxes |\n| `rename_prim` | Move/rename objects in the hierarchy |\n| `remove_prim` | Delete objects from the scene |\n| `create_light` | Add native USD lights (sun, dome, point, area, disk, tube) |\n| `update_light` | Modify an existing light's properties |\n| `remove_light` | Delete a light from the scene or asset |\n| `create_material` | Author a procedural MaterialX material and bind it to a prim |\n| `bind_material` | Apply a material to a specific mesh part (writes into asset mtl.usda) |\n| `remove_material` | Clear material binding from a prim |\n| `list_materials` | Show all materials and their bindings |\n| `cleanup_unused_materials` | Prune material definitions no prim binds to (per asset or project-wide) |\n| `cleanup_unused_contents` | Prune empty `contents.usda` scopes left after removing nested assets |\n| `freeze_asset` | Bake non-identity root transforms (Maya/Houdini unfrozen exports) into vertex data, per asset or project-wide |\n| `list_prim_children` | Discover mesh parts inside a referenced asset |\n| `list_project_assets` | Show asset folders with scene usage status |\n| `delete_project_asset` | Remove an asset folder (checks references first) |\n| `delete_project_texture` | Remove a texture file (checks references first) |\n| `search_assets` | Find USD assets in the user's library by keyword (geo, mtl, package) |\n| `list_assets` | List every USD asset in the user's library, classified by category |\n| `search_textures` | Find HDRIs and material maps in the asset library by keyword |\n| `list_textures` | List every HDRI and material map in the asset library |\n| `validate_scene` | Check for USD errors |\n| `package_scene` | Bundle as `.usdz` |\n\n### Extension Skills\n\n#### First-party skills\n\nMaintained by Binary Core LLC alongside the BowerBot core.\n\n| Skill | Install | What it does |\n|-------|---------|--------------|\n| [bowerbot-skill-sketchfab](https://github.com/binary-core-llc/bowerbot-skill-sketchfab) | `pip install bowerbot-skill-sketchfab` | Searches and downloads models from your own Sketchfab account in USDZ format. |\n\n#### Community skills\n\nBuilt by external contributors, published to PyPI under each author's namespace, and listed here for discoverability. To add yours, open a PR on this README adding a row to the table below. The skill must be open source, installable via `pip` from public PyPI, and follow the contract in [CONTRIBUTING.md](CONTRIBUTING.md).\n\n| Skill | Author | Install | What it does |\n|-------|--------|---------|--------------|\n| _be the first_ | | | |\n\nWhen this list grows large enough to warrant tooling, it becomes the [BowerHub](#-roadmap) skill registry.\n\n#### Installing a skill\n\nThree steps. Sketchfab as the worked example.\n\n**1. Install the skill alongside BowerBot.** With uv, add it to the same tool environment:\n\n```bash\nuv tool install bowerbot --with bowerbot-skill-sketchfab\n```\n\nTo add more skills later, rerun with every `--with` you want and `--reinstall`:\n\n```bash\nuv tool install bowerbot --with bowerbot-skill-sketchfab --with bowerbot-skill-polyhaven --reinstall\n```\n\nIf you used plain `pip` to install BowerBot, install the skill in the same Python environment:\n\n```bash\npip install bowerbot-skill-sketchfab\n```\n\n**2. Get any credentials the skill needs.** Sketchfab requires an API token from https://sketchfab.com/settings/password. Each skill's README documents what credentials (if any) it needs.\n\n**3. Add the skill's config block to `~/.bowerbot/config.json`:**\n\n```json\n\"skills\": {\n  \"sketchfab\": {\n    \"enabled\": true,\n    \"config\": { \"token\": \"your-sketchfab-token\" }\n  }\n}\n```\n\nThat's it. BowerBot auto-discovers the skill via Python entry points the next time you run it. The exact shape of `config` is per-skill; consult the skill's README.\n\n#### Verifying a skill is installed\n\nThree commands, in increasing depth. All work on Windows, macOS, and Linux.\n\n**1. Ask BowerBot what it sees:**\n\n```bash\nbowerbot skills\n```\n\nLists the core scene-builder tools plus every extension skill the registry has loaded successfully. If your skill shows under \"Extension skills\" with its tools, you are done.\n\n**2. If it does not appear, check the package is installed:**\n\n```bash\npip show bowerbot-skill-sketchfab\n```\n\nIf the package is installed, this prints its name, version, and location. If not, it prints `Package(s) not found` and exits non-zero. Install it (see [Installing a skill](#installing-a-skill) above). Replace `bowerbot-skill-sketchfab` with whichever skill you are checking.\n\n**3. If the package is installed but BowerBot still does not see it, inspect the entry-point registration directly:**\n\n```bash\npython -c \"from importlib.metadata import entry_points; print('\\n'.join(f'{ep.name} -\u003e {ep.value}' for ep in entry_points(group='bowerbot.skills')))\"\n```\n\nIf your skill does not appear in this output despite being pip-installed, the skill's `pyproject.toml` is missing or broken. File an issue on the skill's repo. If the skill does appear here but `bowerbot skills` still does not show it, the gap is in your `~/.bowerbot/config.json`: the skill's block is missing, `enabled: false`, or the credentials fail `validate_config()`.\n\n#### Private and in-house skills\n\nSkills do not have to be public. Install from a private PyPI index, a git URL, or a local path:\n\n```bash\n# Private PyPI\npip install bowerbot-skill-acme --index-url https://pypi.acme.internal/\n\n# Direct git URL (any host)\npip install git+ssh://git@github.com/acme/bowerbot-skill-acme.git\n\n# Local path during development\npip install -e /path/to/skill\n```\n\nEntry-point discovery works the same in all three cases.\n\n#### Trust\n\nA skill's `SKILL.md` is injected into the LLM's system prompt, and its tools run with the same access as core tools. Only install skills you trust. Open-source skills are auditable; closed-source skills should come from a vendor you have a relationship with. The first-party table above is the only set Binary Core has audited end-to-end.\n\n---\n\n## ⚙️ Configuration\n\nAll settings live in `~/.bowerbot/config.json`. The `skills` block holds the config for any skill packages you've installed; the example below shows what it looks like once you've installed `bowerbot-skill-sketchfab` (see [Skills](#-skills)). A fresh install starts with `\"skills\": {}`.\n\n```json\n{\n  \"llm\": {\n    \"model\": \"gpt-4.1\",\n    \"api_key\": \"sk-...\",\n    \"temperature\": 0.1,\n    \"max_tokens\": 4096,\n    \"context_window\": null,\n    \"summarization_threshold\": 0.75,\n    \"num_retries\": 3,\n    \"request_timeout\": 120.0,\n    \"max_tool_rounds\": 25\n  },\n  \"scene_defaults\": {\n    \"meters_per_unit\": 1.0,\n    \"up_axis\": \"Y\",\n    \"default_room_bounds\": [10.0, 3.0, 8.0]\n  },\n  \"skills\": {\n    \"sketchfab\": {\n      \"enabled\": true,\n      \"config\": { \"token\": \"your-sketchfab-token\" }\n    }\n  },\n  \"assets_dir\": \"./assets\",\n  \"projects_dir\": \"./scenes\"\n}\n```\n\nSwitch models by changing one line:\n\n```json\n{ \"model\": \"gpt-4.1\" }\n{ \"model\": \"anthropic/claude-sonnet-4-6\" }\n{ \"model\": \"deepseek/deepseek-chat\" }\n```\n\n### Tested Models\n\n| Model | Tool Calling | Instruction Following | Recommended |\n|-------|-------------|----------------------|-------------|\n| `gpt-4.1` | Excellent | Excellent | **Yes** (default) |\n| `gpt-4.1-mini` | Good | Good | Yes (budget) |\n| `gpt-4o` | Poor | Poor | No (skips tool calls, ignores SKILL.md) |\n| `anthropic/claude-sonnet-4-6` | Excellent | Excellent | Yes |\n\nBowerBot relies heavily on tool calling and SKILL.md instructions. Models that don't follow tool-calling patterns reliably will produce poor results.\n\n### Token Management\n\nBowerBot automatically manages conversation context to stay within model limits. Two settings control this:\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `context_window` | `null` | Context window size in tokens. `null` = auto-detect from the model. |\n| `summarization_threshold` | `0.75` | Fraction of context budget that triggers history summarization. |\n\nAdditional tuning options (usually don't need changing):\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `tool_result_age_threshold` | `2` | User turns before old tool results are compressed. |\n| `min_keep_recent` | `6` | Minimum recent messages always kept verbatim. |\n| `summary_max_tokens` | `512` | Max tokens for the summarization LLM call. |\n\n### Tool-Calling Loop\n\nBowerBot runs a loop where the LLM requests tool calls, BowerBot executes them, and the results are fed back. Complex requests (e.g. binding materials to many mesh parts at once) can require many rounds.\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `max_tool_rounds` | `25` | Maximum LLM ↔ tool exchange rounds per request. Increase if BowerBot stops with \"Reached maximum tool-calling rounds\" on legitimate workflows. |\n\n### Error Recovery\n\nBowerBot automatically handles transient API errors:\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `num_retries` | `3` | Retries for rate limits and transient errors (429, 500, 503). |\n| `request_timeout` | `120.0` | Seconds before a request times out. |\n\n- **Rate limits and transient errors** are retried automatically with exponential backoff.\n- **Validation errors** are fed back to the LLM so it can auto-fix issues and re-validate.\n- **Permanent errors** (bad API key, unknown model) show a clear message without crashing.\n\n---\n\n## 🏗️ Architecture\n\nBowerBot is organized FastAPI-style:\n\n- **schemas/** describe data (pydantic models + enums)\n- **utils/** are pure-function primitives (no `SceneState`, no orchestration)\n- **services/** are state-aware orchestrators, one function per tool, signature `(state, params)`, calls utils and other services freely, raises on errors\n- **tools/** are the LLM-facing surface, thin adapters that guard preconditions, call ONE service, wrap the result in `ToolResult`\n\nAdding a feature is the same three-file change every time: schema, service, tool.\n\n```\nsrc/bowerbot/\n  agent.py            # LLM tool-calling loop and prompt assembly\n  cli.py              # Click CLI\n  config.py           # Settings from ~/.bowerbot/config.json\n  project.py          # Project lifecycle (create / load / resume)\n  state.py            # SceneState: the context threaded through every tool handler\n  dispatcher.py       # Aggregates tool defs + routes tool calls to handlers\n  token_manager.py    # Conversation compression and summarization\n\n  prompts/            # LLM instructions as markdown (editable without code changes)\n    core.md\n    scene_building.md\n    library.md\n    textures.md\n    variants.md\n\n  schemas/            # Pydantic models and enums, grouped by domain\n    assets.py         #   Asset formats, categories, ASWF layer names, metadata\n    transforms.py     #   TransformParams, PositionMode, SceneObject\n    lights.py         #   LightType, LightParams\n    materials.py      #   MaterialXShaders, ProceduralMaterialParams\n    textures.py       #   HDRI / image / texture-category enums\n    validation.py     #   Severity, ValidationIssue, ValidationResult\n    variants.py       #   VariantCategory, AddVariant params, VariantsSummary\n\n  services/           # State-aware orchestrators, one per tools module\n    stage_service.py       #   create_stage, list_scene, rename_prim, move_asset, ...\n    asset_service.py       #   place_asset, place_asset_inside, list/delete_project_*\n    library_service.py     #   list_assets, search_assets, find_package_for\n    light_service.py       #   create_light, update_light, remove_light\n    material_service.py    #   create_material, bind_material, list/remove/cleanup\n    texture_service.py     #   list_textures, search_textures\n    validation_service.py  #   validate_scene, package_scene\n    variant_service.py     #   add_asset_(material|geometry|attribute|configuration)_variant,\n                           #   add_scene_lighting_(attribute|selection)_variant,\n                           #   list_variants, select/remove_asset_variant(_set|_for_instance),\n                           #   select/remove_scene_variant(_set)\n\n  tools/              # LLM-facing API layer (tool defs + thin handlers)\n    _helpers.py            #   Precondition guards (require_stage / project / library)\n    stage_tools.py         #   create_stage, list_scene, rename_prim, move_asset, ...\n    asset_tools.py         #   place_asset, place_asset_inside, list/delete_project_*\n    library_tools.py       #   search_assets, list_assets\n    light_tools.py         #   create_light, update_light, remove_light\n    material_tools.py      #   create_material, bind_material, list/remove_material\n    texture_tools.py       #   search_textures, list_textures\n    validation_tools.py    #   validate_scene, package_scene\n    variant_tools.py       #   variant authoring + selection (asset + scene-instance)\n\n  skills/             # Skill SDK. The contract every skill implements.\n                      # Skills themselves ship as separate pip packages.\n    base.py                #   Skill, SkillContext, SkillConfigError,\n                           #   SkillCategory, Tool, ToolResult\n    registry.py            #   Entry-point discovery and tool routing\n\n  utils/              # Pure-function primitives shared by services\n    stage_utils.py           #   Stage create/open/save, references, transforms, prims,\n                             #   ref-path scanning, LIGHT_CLASSES\n    asset_intake_utils.py    #   intake_folder, intake_usdz, create_asset_folder, ASWF\n    asset_folder_utils.py    #   ASWF folder primitives (detect root, layer scopes,\n                             #   resolve_asset_dir_for_prim)\n    library_utils.py         #   scan_library, find_package_for\n    light_utils.py           #   light_in_folder primitives, HDRI staging\n    material_utils.py        #   material_in_folder primitives, find_first_material\n    texture_utils.py         #   find_textures, copy_texture_to_project\n    validation_utils.py      #   validate_stage, package_to_usdz, validate_asset_variants\n    variant_utils.py         #   variants.usda lifecycle, author_in_variant keystone,\n                             #   apply_variant, set/clear_default, removal + cleanup\n    geometry_utils.py        #   Bounds, unit conversion, layout math\n    dependency_utils.py      #   USD dependency tree walker\n    naming_utils.py          #   Name sanitization for files, prims, projects\n  gateway/            # Future: FastAPI + MCP server\n```\n\n**Design principles**\n\n- **One tools module, one service**: every service module backs exactly one tool surface, with one orchestrator per tool. Shared primitives live in `utils/`, called freely by any service.\n- **Functions only in tools / services / utils**: classes live in `schemas/` (pydantic models, enums) and a small set of state objects (`SceneState`, `Project`).\n- **Tools are thin**: guard preconditions, call ONE service, wrap in `ToolResult`. No business logic, no util calls, no cross-service routing.\n- **Services own orchestration**: take `(state, params)`, do the cross-service and multi-util work, mutate state, raise on errors.\n- **Utils are pure primitives**: no `SceneState`, no other services. Composable building blocks.\n- **State lives in one place**: `SceneState` holds the open stage, the project binding, the asset library path, and the object counter; tool handlers thread it into service calls.\n- **All `pxr` is in `services/` and `utils/`**: the rest of the codebase never imports `pxr` directly.\n- **Prompts are content**: editable `.md` files, not Python constants.\n- **Skills are external integrations**: new asset providers ship as Python packages discovered via entry points.\n- **One config file**: `~/.bowerbot/config.json`, no `.env`.\n\n---\n\n## 📐 USD Compliance\n\nEvery scene follows [OpenUSD](https://openusd.org) best practices and the [ASWF asset structure guidelines](https://github.com/usd-wg/assets/blob/main/docs/asset-structure-guidelines.md):\n\n**Scene level**\n- `metersPerUnit = 1.0`, `upAxis = \"Y\"`, `defaultPrim` always set\n- Standard hierarchy: `/Scene/Architecture`, `/Scene/Furniture`, `/Scene/Products`, `/Scene/Lighting`, `/Scene/Props`\n- References only: no inline geometry, no scattered material sublayers\n- Wrapper-prim pattern isolates scene-level transforms from asset-internal ones, so DCC export transforms (Maya pivots, rotations) stay untouched\n- Pre-packaging validator checks `defaultPrim`, units, up-axis, reference resolution, and material bindings\n\n**Asset level**\n- References (not sublayers) per ASWF guidelines, for predictable opinion strength\n- Materials inline in `mtl.usda`, lights inline in `lgt.usda`, nested references in `contents.usda`\n- Automatic `metersPerUnit` conversion across composition boundaries\n- Identity root transforms enforced on intake: pivot dances, baked rotations, and other unfrozen DCC export ops are rejected (or baked into vertex data with explicit user consent), so nested placements compose predictably\n- Nested placements mirror the scene-level wrapper convention (a wrapper `Xform` holds the per-instance transform, an inner `/asset` child holds the reference arc), and `move_asset` / `remove_prim` on a nested path route writes to `contents.usda` instead of authoring per-instance overrides at scene level\n- Asset roots carry the canonical ASWF identity: `kind = \"component\"` for terminal assets and an `assetInfo` dictionary (`identifier`, `name`, `version`) so DCC outliners, asset browsers, and pipeline asset-management systems recognise BowerBot output as production-grade\n\n**Variant sets**\n\nTwo layers of authority. The naming convention makes routing explicit.\n\n- **Asset-level** variants live in `\u003casset\u003e/variants.usda`, referenced (not sublayered) into the asset root. Four orchestrators: material bindings, geometry/LOD payloads, configuration activations, and attribute overrides. The asset's \"ship default\" lives on the root prim in `\u003casset\u003e.usda`, never inside `variants.usda`.\n- **Scene-level** variants live inline in `scene.usda` on a carrier prim. Three orchestrators: lighting attribute swaps and lighting selection on `/Scene/Lighting`, plus model selection on the placement wrapper. Lighting selection swaps which UsdLux is active across pre-placed siblings (DiskLight vs RectLight). Model selection swaps which asset reference loads at a placement (chair vs stool).\n- Tool names carry an explicit `asset_` or `scene_` prefix so the LLM never has to guess which layer of authority a call writes to.\n- Foundation: `utils/variant_utils.author_in_variant(stage, prim_path, set, name, author_fn)` runs any caller function inside the variant's edit context. Asset and scene orchestrators are thin wrappers. Adding a new variant category is a pure addition, never a util change.\n- Per-instance overrides: any placement can author `variants = { \"set\" = \"value\" }` inline in `scene.usda` to pick a different variant from the asset's default.\n- Validation runs on `validate_scene` before packaging (referenced not sublayered, default selection present, no orphan reference, naming).\n\n**Architectural invariants (apply at every prim mutation):**\n\n1. **Orphan opinion cleanup cascade.** When a prim is removed, every variant body spec authored at the same path is dropped. Empty intermediate `over` specs are pruned. Empty variant bodies remove via `Sdf.VariantSetSpec.RemoveVariant`. Empty variant sets drop along with their `variantSetNames` and `variantSelections` metadata. When `variants.usda` becomes empty, the file is auto-deleted and the root reference scrubbed.\n2. **Rename invariant.** Renaming a prim follows the rename through every variant body opinion, preserving authored values.\n3. **Asset-staging for `Sdf.ValueTypeNames.Asset` attributes.** Variant bodies that author texture or HDRI paths automatically stage the source file into `\u003cproject\u003e/textures/` and write the project-relative path. Refuses if the source cannot be resolved (no silent broken paths).\n4. **Suspect-set detection.** After a removal, variant sets that have collapsed to a single model-selection variant (or 2+ variants converging on one prim with active-only opinions) are flagged via `suspect_variant_sets` on the result. BowerBot surfaces the suspect to the user and asks before deleting the set.\n5. **Model-selection symmetry.** `add_scene_model_selection_variant`'s first call auto-promotes the placement's existing direct reference into a variant body (named after the source asset folder). Removing the entire set auto-demotes the active variant's reference back to a direct reference on `/asset`. No data loss, no dead-slot placements.\n6. **Layer-level reference scanning.** `delete_project_asset`'s safety check scans variant bodies in any layer, not just the composed stage view. An asset referenced only by a non-active variant body still blocks deletion.\n\n**Removal scope**\n- Removal operations are scoped to one carrier. Removing a variant set from one asset never affects other assets, even when they reference each other.\n- When multiple assets are in scope, BowerBot asks which asset before calling the removal tool. It never guesses.\n- Variants composed in via referenced assets stay visible after removal because they are authored elsewhere. Navigate to that asset and remove them there.\n\n---\n\n## 🗺️ Roadmap\n\nWhat's next for BowerBot. Contributions welcome:\n\n- [ ] **More scene-level variant categories**: layout variants (atomic furniture arrangement swap on a group prim) and camera variants (active camera + render settings) on the `/Scene/Cameras` group. Infrastructure is in place via `apply_scene_variant`; the orchestrators are pure additions when use cases land\n- [ ] **Animation variants (asset-level)**: each variant body references a different animation clip (idle, walk, etc.), production-canonical for articulated state cycling\n- [ ] **More asset providers**: Fab, PolyHaven, Objaverse, CGTrader skills\n- [ ] **MCP Gateway**: FastAPI server for web UI and external AI clients\n- [ ] **Web UI**: chat panel + live 3D viewport\n- [ ] **BowerHub**: community skill registry\n\n---\n\n## 🤝 Contributing\n\nBowerBot is open source and welcomes contributions. The best way to start is writing a new **skill** for an asset provider, DCC, or simulation runtime you use. Skills ship as separate pip packages discovered through the `bowerbot.skills` entry-point group.\n\nRead [CONTRIBUTING.md](CONTRIBUTING.md) for the skill contract, the required FastAPI internal layout, and a worked `pyproject.toml` example for a stand-alone skill package.\n\nFor a complete reference, see [bowerbot-skill-sketchfab](https://github.com/binary-core-llc/bowerbot-skill-sketchfab): a real first-party skill on PyPI, with the production layout, entry-point registration, validation, and release pipeline you can mirror for your own.\n\n---\n\n## 💖 Sponsors\n\nBowerBot is open source and built by a small team at [Binary Core LLC](https://binarycore.us). Sponsorship funds new asset providers (PolyHaven, Fab, CGTrader), USD compliance work, scene templates, the MCP gateway, documentation, and community support.\n\n[**Become a sponsor on GitHub**](https://github.com/sponsors/binary-core-llc). Three monthly tiers (Egg, Nest, Bower) plus one-time options.\n\n### Backers\n\n_Be the first._\n\n---\n\n## 📄 License\n\n```\nCopyright 2026 Binary Core LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n```\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\nBuilt with 🐦 by [Binary Core LLC](https://binarycore.us)\n\n*\"The bowerbird doesn't have the flashiest feathers. It just builds the most compelling world.\"*\n\n\u003c/div\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinary-core-llc%2Fbowerbot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbinary-core-llc%2Fbowerbot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinary-core-llc%2Fbowerbot/lists"}