Plugin harness for the img2 ecosystem: everything is a plugin. The harness ships no domain capability — it owns install/link plumbing, the plugin registry, the workspace state envelope, the gate runner, and the contract.
| Document | Its job |
|---|---|
| docs/PLUGIN_CONTRACT.md | Normative. The rules. Where anything else disagrees with it, it wins |
| docs/WRITING_A_PLUGIN.md | Your first plugin, start to finish. A linear tutorial — copy plugin-hello-cube and follow it |
| docs/plugin-wiki/ | Everything after that. Why the architecture is shaped this way, eleven worked scenarios — add a domain, insert a step before a base step, add a gate, what to do instead of overriding one — and a field-by-field reference |
| CONTRIBUTING.md | Changing the harness itself |
| SECURITY.md | The threat model, and how to review a plugin before installing it |
| CHANGELOG.md | What changed per release |
| docs/PLAN.md | Phase status and the decisions behind it |
docs/PLUGIN_ARCHITECTURE.md is a generated single-page build of the wiki — read the wiki instead,
unless you want the whole thing in one file.
Ecosystem discovery: img2 plugins reads the official catalog. GitHub topic
img2threejs-plugin can also surface community repositories outside that catalog.
You need Node.js ≥ 18, Git and Python ≥ 3.10. Configure at least one supported agent host first: Claude Code, Codex or OpenCode. See Prerequisites for detection paths. Use CLI 0.4.0 or newer for short plugin IDs and the branded table output.
npx --yes @img2threejs/img2@latest install --yesThis prepares the harness checkout and registry under ~/.img2, configures supported host
access, and links an img2 launcher into a writable directory on your PATH when possible.
Read the install output for the actual launcher location.
The first --yes belongs to npx and allows downloading the CLI package. The final
--yes belongs to img2 and skips its confirmation prompt; omit it if you want to confirm
interactively. Neither flag authorizes replacement of an existing plugin.
If the installed launcher is available, check it with:
img2 --version
img2 --helpIf img2 is not found, or its version is older than 0.4.0, use the latest CLI directly:
npx --yes @img2threejs/img2@latest plugins
npx --yes @img2threejs/img2@latest add hello-cube --yesFor short commands that always use the latest published CLI, set this bash/zsh alias:
alias img2='npx --yes @img2threejs/img2@latest'The alias takes precedence over an older launcher and lasts for the current shell session.
For zsh, add that line to ~/.zshrc and run source ~/.zshrc to keep it in future sessions.
Every img2 ... example below also works as npx --yes @img2threejs/img2@latest ....
Existing installations: install keeps the existing harness checkout; it does not
automatically upgrade an old launcher. Use the alias or the explicit npx ...@latest form
to get the new CLI without replacing your current registrations.
img2 plugins # official plugins available to install
img2 list # plugins already registered on this machineThese are different lists. plugins reads the live official catalog; it does not install
anything and can run before harness setup. list reads your local registry.
| Plugin / source README | What it provides | Git source | Install after harness setup |
|---|---|---|---|
| character | Character rigging, skin conditioning, animation and rig gates | Public | img2 add character --yes |
| cs2 | Counter-Strike 2 weapon/glove skin reconstruction and review gates | Public | img2 add cs2 --yes |
| environment | The environment plugin, distributed from a private repository | Private — authorized Git access required | img2 add environment --yes |
| hello-cube | A small deterministic image-to-Three.js cube example | Public | img2 add hello-cube --yes |
| img2glb | Image-to-GLB mesh generation through the hosted TRELLIS space | Public | img2 add img2glb --yes |
The table lists announced plugins; use img2 plugins for the current catalog. You do not
need to install all of them. For a small first installation:
img2 add hello-cube --yes
img2 list
img2 doctor
img2 sync --checkadd resolves a bare ID through the catalog, clones its Git source, validates the requested
manifest identity, pins the resolved commit and links the skill into detected hosts. It
does not run a reconstruction. doctor is a static installation audit, not a visual
quality or reconstruction acceptance check. sync --check verifies that generated files
match the registered manifests.
Start a new agent session, or restart the current one, so updated skills are loaded.
Open the plugin's linked README above for its inputs, setup and workflow; follow its
SKILL.md and tool instructions for the actual task. For example, ask your agent:
Use the hello-cube skill with ./reference.png. Follow the plugin's instructions,
produce the Three.js output, and report the output files and checks performed.
The harness manages installation and capability lookup. It does not generate a model just because a plugin was added, and it does not replace each plugin's usage instructions.
img2 update hello-cube --check # check this plugin without replacing it
img2 update hello-cube # install a newer available version
img2 doctor # audit again after changes
img2 remove hello-cube # unlink owned host links and back up the managed checkoutOmit the ID from img2 update to inspect/update the harness checkout and all registered
plugins; img2 update harness handles only the harness. The harness checkout moves to the newest
tag only when its origin is the official repository, it has no local changes, and it is behind
that tag. update --check
exits 1 when an update is pending, and 0 when none is pending; no checkout is replaced.
npx --yes @img2threejs/img2@latest install --yes --auto-install additionally records your
opt-in in $IMG2_HOME/auto-install. Afterwards an agent skill may run
img2 ensure <plugin-id>, which only adds a missing public official plugin, never updates
or replaces one, and accepts no options. Without the marker, or with IMG2_AUTO_INSTALL=0,
ensure exits 3 and changes nothing. Delete the marker to opt out. The marker records your
choice; it is not a security boundary. Agent skills are instructed never to write it, but an
agent that can write files under $IMG2_HOME could create it, which is why ensure is limited
to public official catalog plugins.
Git updates follow the newest reachable semver tag. Local --link checkouts are live and
are not fetched by update. Removal drops the registry row and its user-authored overrides;
it does not delete the original source checkout of a local link.
The standalone installers are public packages in the
@img2threejs npm organization:
| Plugin | npm package | Install, including harness setup if missing |
|---|---|---|
| character | @img2threejs/character | npx --yes @img2threejs/character install --yes |
| cs2 | @img2threejs/cs2 | npx --yes @img2threejs/cs2 install --yes |
| environment | @img2threejs/environment | npx --yes @img2threejs/environment install --yes |
Run the same command with --dry-run instead of --yes to preview without writes.
Use doctor to audit the installation, update --check to check plugin releases, and
remove --yes to remove an installed plugin. Installer packages contain only the CLI;
plugin source is fetched separately at the package's immutable commit pin. They use
@img2threejs/img2@0.4.0 and do not run a reconstruction.
The npm package name and executable differ: a global installation of
@img2threejs/cs2 provides img2-cs2, and likewise for character/environment.
img2 add <id> still resolves the plugin's Git source, not its npm installer.
The environment source requires a
GitHub account with repository access and working Git credentials. A private GitHub link
may appear as 404 when you are signed out or lack permission. The public
@img2threejs/environment npm installer does
not grant access to that source.
With the harness installed and Git access configured:
img2 add environment --yes
img2 doctorAlternatively, the standalone installer can preview an installation without changes and bootstrap the harness when installing:
npx --yes @img2threejs/environment install --dry-run
npx --yes @img2threejs/environment install --yesThe organization installer pins SDK 0.4.0 and plugin source commit c19f5f2.
Newer local environment development is not included in this installer.
| Situation / option | What to do |
|---|---|
| No harness checkout | Run img2 install --yes before img2 add <id> |
| Unknown plugin ID | Check img2 plugins; bare IDs must be in the official catalog |
| Catalog/network unavailable | Retry when reachable, or use a known explicit Git/npm/local source |
| Git access denied for a private source | Obtain repository permission and configure Git authentication |
| Plugin already registered | Use img2 list and img2 update <id>; adding it again does not silently replace it |
| Registered as a local link | Keep using that checkout, or explicitly choose replacement with --force |
--ref <ref> |
Select an existing Git tag, branch or full commit SHA instead of automatic tag selection |
--home <absolute-path> |
Select the same harness home for every related command; the default is ~/.img2 |
--yes |
Skip consent prompts; it is not permission to overwrite a registration |
For a ref-pinned install, use a real ref from that plugin's repository:
img2 add hello-cube --ref main --yesOnly when you deliberately want to replace an existing registration:
img2 add environment --force --yes--force backs up the previous managed checkout or mount before replacement. If the
registration is a local link, the original linked checkout remains intact. Without
--force, a local registration is refused before catalog access or cloning.
Explicit org/repo, Git URLs, npm:<name> and --link continue to work without catalog
access. Sources outside the default GitHub org/npm scope need --allow-any-source;
review them before authorizing installation. Unknown catalog IDs and identity mismatches
exit 2; catalog/network failures exit 1 without changing install state.
npx --yes @img2threejs/img2@latest plugins
npx --yes @img2threejs/img2@latest plugins --jsonNo harness installation, GitHub credentials or local registry is needed to list the catalog;
only Node.js ≥ 18 and internet access. Each call fetches the maintained
catalog.json from GitHub
with a ten-second request/body timeout. Catalog updates do not require a new npm release.
The count covers officially announced plugins, including private sources with public installers;
it is not npm search, a census of hidden repositories, or the locally registered set (img2 list).
Each entry includes its description, GitHub source, source visibility, optional npm CLI and a
copyable install command. Private-source installation still requires authorized repository access.
Listing never installs plugins, reads registrations or creates a harness home.
--json returns counts (total, public, private, npmClis) and plugins, with each
install represented as an argv array. Fetch/validation errors exit 1 with status: "error" and
counts: null, never a false zero. Invalid command arguments exit 2 before fetching.
Maintainers add or update announced entries in the root catalog.json (schema 1); unpublished
private repositories must remain outside the public catalog.
Human output includes the img2threejs wordmark, a colored pixel-to-cube terminal logo, and the brand slogan: “Rebuild the object in a reference image as a code-only, procedural Three.js model.” Catalogs, registrations, audit findings, metadata, help and update outcomes use bounded-width tables; narrow multi-column records become Field/Value tables.
install and add show numbered phases, actual host-link outcomes, the selected ref/pin,
backup locations when replacing an installation, and a copyable doctor command on success.
Plugin CLI wrappers using SDK 0.4.0 use the same presentation for offline previews and
already-registered installs. Wrappers pinned to an older SDK retain their existing UI.
Registration alone is not a health check; run doctor for that.
Color is enabled only on the corresponding terminal stream. Set NO_COLOR=1 to disable it;
TERM=dumb is also plain. Redirected/piped output stays uncolored even with FORCE_COLOR.
Narrow terminals stack fields and wrap messages without truncating paths or refs.
Machine-readable --json output, implicitly-JSON capabilities, and exit codes are unchanged;
they never include a banner or table.
Use npx @img2threejs/img2@latest <command> to pick up installer updates.
install keeps an existing harness checkout; it does not automatically upgrade an older
img2 launcher pointing into that checkout.
The harness publishes to npm as the scoped package @img2threejs/img2. There is no unscoped
fallback: a wrapper that depends on img2 resolves a different package on npm and will not
satisfy the wrapper's @img2threejs/img2/plugin-cli import.
A plugin's author can ship a CLI wrapper alongside the plugin repo. The wrapper lives in the
plugin's cli/ subdirectory, is published to npm under a name like img2-<plugin-id>, and lets
end users install/update/remove the plugin without the harness on their machine first:
npx --package=@img2threejs/img2 create-img2-plugin-cli \
--directory ~/src/plugin-foo \
--name img2-foo \
--source img2threejs/plugin-foo \
--workflow-ref 7ef63107b39f77df3a11062a1a0a2245f500921cThe generator creates an installer-only cli/ package, isolated-home regression tests,
public CLI README/license, .gitignore, a SHA-pinned .github/workflows/cli-publish.yml,
and a complete root CLI_QUICKSTART.md. For a private GitHub source, add --private-repo.
End users run npx img2-foo install --dry-run, then npx img2-foo install --yes.
The package ships only its executable, package metadata and public README/license—not the
plugin repository's README, tools or assets. The shared runtime is pinned to the generator's
exact harness version.
See docs/WRITING_A_PLUGIN.md and the
generated quick-start template.
- Node.js ≥ 18
- Git (the harness uses it for clone/checkout)
- Python 3.10+ (the bundled
img2_core/is stdlib-only Python)
Detected agent hosts: Claude Code (~/.claude), Codex (~/.codex), and
OpenCode ($XDG_CONFIG_HOME/opencode or ~/.config/opencode). The harness does not
support any other host; check img2 doctor --json for the current host detection on your
machine.
img2 capabilities answers "which installed plugin turns X into Y" for a caller — a base pipeline
consulting it at a step, or you at a prompt. It is read-only, takes no lock and writes nothing.
img2 capabilities --from-kind image --to-kind glb --json{ "version": 1, "contract": 14,
"query": { "from": "image", "to": "glb" },
"status": "answered",
"providers": [{ "plugin": "img2glb", "version": "0.1.0", "resolvedSha": "353a8ea…",
"dir": "/Users/you/.img2/plugins/img2glb",
"steps": [{ "id": "generate-glb", "argv": ["python3", "…/tools/img2glb.py",
"--image", "{image}", "--workspace", "{workspace}"] }],
"gateRunner": { "argv": ["python3", "-m", "img2_core.gate_runner", "…"] } }],
"problems": [] }Four properties a caller can rely on:
- One JSON envelope on stdout for every outcome it owns, including failures. Branch on
status(answered|ambiguous|data-fault) — never on stderr text. - Step commands come back as
argvarrays, already tokenised, with{plugin_dir}resolved and{workspace}/{image}left as single elements to replace by value. You never build a shell string, so a path containing a space cannot split and a plugin cannot smuggle in a second command. - Exit codes reuse the harness table:
0answered (zero providers is a normal answer),1a data fault,3an ambiguous edge — resolve it with--plugin <id>. This subcommand never exits2, so2still means "this harness does not understand you". - One broken plugin cannot deny an unrelated answer. A plugin whose manifest fails to read lands
in
problems[]whileproviders[]still answers.
To detect whether a harness supports this at all, without parsing prose:
img2 --version --json # {"harness":"0.4.0","maxPluginSchema":1,"coreApi":1,
# "contract":14,"commands":[…,"capabilities"]}An unrecognised --json, or capabilities missing from commands, means the feature is absent —
treat that as "no provider" and take your built-in path, rather than as an error.
Developing a plugin locally:
img2 add --link ~/src/plugin-hello-cube # symlink, no clone; ref/sha recorded as "local"A plugin can also be distributed as an npm package instead of a git repo — img2 add npm:<name>
or img2 add npm:<name>@<version> (default source trust is the @img2threejs scope, same rule as
the img2threejs/* git org: anything else needs --allow-any-source). ref records the resolved
version and resolvedSha records npm's own dist.integrity (a sha512-… string) rather than a git
SHA — the harness's own notion of "pinned content hash" for a package it fetched instead of cloned.
img2 update [<id>] [--check] re-checks every row's source (npm version, git's newest semver tag) for
something newer and fetches it in place; --check reports what is pending without changing anything.
Layout under $IMG2_HOME (default ~/.img2; the deprecated IMG2THREEJS_HOME is honoured
for one release with a warning):
harness/ canonical harness checkout (img2_core lives here)
plugins/<id>/ one clone (or --link symlink, or npm fetch) per registry row
generated/ index.md + routes.json, regenerated by `img2 sync`
plugins.json the registry: flat rows {id, repo, ref, resolvedSha, addedAt}
repo is "org/repo", a git URL, "npm:<name>", or "link:<path>"
receipts.json what was linked where
backups/ displaced directories; nothing is ever deleted in place
Plugin tools reach the Python core through the generated _img2_local.py (gitignored by
contract) or $IMG2_HOME; img2_core.require_core_api(1) asserts the core API at runtime.
Gates run through python3 -m img2_core.gate_runner --plugin-dir <dir> --workspace <dir>,
which topo-sorts gates.json, expects one img2.gate-verdict JSON envelope per gate on
stdout, and stops on a blocking fail.
node --test tests/*.test.mjs
python3 -m unittest discover -s tests/python -p 'test_*.py'Both suites are dependency-free (node:test and unittest only). Run them from the repo root.