loomcli
loomcli is the Python package. weave is the binary it installs. This doc is for the cases where you care about the package itself — install paths, version pinning, building from source, the schema-source-of-truth role.
For the operator command surface (plan / apply / diff / get / describe), see /docs/cli.
What ships in the package
pip install loomcli puts three things on your machine:
- The
weaveconsole script — what you actually run from a terminal. Wraps the package's CLI module via the standard setuptools entry-point mechanism. Always installs asweave, regardless of how you installed the package. - The Python module
loomcli— usable from your own scripts if you want to reach the same primitives the CLI does without shelling out.from loomcli.client import PowerloomClientis the typical import. - The bundled v2 schema — every Powerloom Kind, in JSON Schema form, under
loomcli._bundled_schema.v2. The Powerloom api container pins aloomcliversion specifically to ride this schema in lock-step.
The split (package / binary / schema) is intentional. It means a server-side Powerloom build, a CI runner that lints manifests, and an operator on a laptop all consume the same authoritative type definitions.
Install paths
From PyPI (the default)
pip install loomcli
Requires Python 3.11+. Works on macOS, Linux, and Windows. The binary lands wherever your pip puts console scripts (~/.local/bin for user installs; <venv>/bin for venvs).
Pin a specific version when scripting:
pip install 'loomcli==0.6.2'
Inside a uv-managed project
uv add loomcli
uv run weave --version
Inside a Docker image
RUN pip install --no-cache-dir loomcli==0.6.2
ENTRYPOINT ["weave"]
Common in CI runners that just need weave plan / weave apply against a manifest tree.
From source
The CLI lives at github.com/shanerlevy-debug/loomcli (separate from the Powerloom monorepo as of v034). Clone + editable install:
git clone https://github.com/shanerlevy-debug/loomcli
cd loomcli
pip install -e .
weave --version
Required for: contributing to the CLI itself, testing pre-release schema changes, or running CLI tests locally (pytest).
Versioning
loomcli follows semver. The major + minor track the schema generation; the patch tracks bug-fixes and CLI ergonomics that don't shift the schema.
| Component | Bumps when |
|---|---|
Major (1.x.x) | Breaking change to the schema or CLI invocation contract |
Minor (x.7.x) | New Kind, new field, new flag, new behavior — backward-compatible |
Patch (x.x.3) | Bug fix, error-message change, dependency bump — invisible to manifests |
The Powerloom api container pins loomcli with both an upper and lower bound to keep the schema lock-step honest. The api/pyproject.toml line looks like:
"loomcli>=0.6.2rc1,<0.7.0",
When the schema bumps a minor (e.g., 0.7.0 ships a new Kind), the api gets upgraded in the same PR that lifts that pin. Operators running the prior CLI minor see a clean 426 Upgrade Required response from the schema-version gate; the api refuses to honor an older client's manifests against newer schemas, by design.
To check what version your weave is talking to:
weave --version # client side
curl https://api.powerloom.org/healthz # server reports its loomcli pin
The schema source of truth
Every Powerloom Kind has a JSON Schema definition. These live in the loomcli package, not in the Powerloom monorepo. The api consumes them at runtime via loomcli._bundled_schema.v2.<Kind> — there is no parallel definition on the server.
This shape has three implications:
- Schema bumps land in the loomcli repo first. A new Kind, a new required field, a renamed property — all of those are PR'd into loomcli, released as a new version, then picked up by the api in a follow-up PR that bumps the pin. Two repos, two releases, one source of truth.
- The CLI validates locally.
weave plan -f file.yamlruns the manifest through the same JSON Schema the api would use. Errors caught here never reach the wire.weave validate -f file.yamlis the no-side-effects version — schema-only, no network call. - Custom kinds aren't a thing today. Every Kind ships in the bundled schema. If you want to register a custom resource, the path is: PR it into loomcli, get it released, then reference it. There is no operator-side "register a new Kind" affordance.
Compose / extends resolution
Some Kinds (notably MCPDeployment and WorkflowDefinition) compose against stdlib templates via the extends: field. The CLI resolves these client-side using the bundled schema before sending the apply to the api — that's why weave plan can show you the fully-merged effective shape before any network call.
apiVersion: powerloom/v1
kind: MCPDeployment
metadata:
name: my-postgres
extends: postgres # resolved against loomcli._bundled_schema.v2.stdlib.postgres
parameters:
connection_string_credential_ref: cred:/acme/db-readonly
The x-override mechanism for collisions, the cycle detection, the recursive extends: <Composed> — all of that runs in the CLI. The api receives a fully-resolved manifest and treats the result as authoritative.
This isn't accidental. The CLI being the resolver means every plan you run produces a deterministic, copy-pasteable view of what's about to land. No hidden server-side magic.
Where the package lives in your codebase
If you're shipping Powerloom-managed agents from your own repo:
- Manifests live under
manifests/(or wherever your team puts them). Plain YAML. Version-controlled. Reviewed in PR. - Loomcli is a dev dependency (
requirements-dev.txtorpyproject.toml [tool.uv] dev-dependencies). Pinned to the same version your CI uses. - CI runs
weave planon every PR that touchesmanifests/and posts the diff back as a PR comment. The standardloomcli-on-CI workflow lives in github.com/shanerlevy-debug/loomcli/blob/main/.github/workflows/example-ci.yml. - CI applies on merge to main, scoped to a deploy environment via env var.
This is the same shape Terraform takes — manifests next to code, plan in PR, apply on merge. The CLI is built to drop into that workflow without inventing a new one.
What loomcli is not
loomcli is not a runtime SDK for invoking agents from your application. For that, use the REST API directly (see /docs/api-reference) or generate a client from the OpenAPI spec at /api/openapi.json. The CLI is for governance — declaring what exists, who can do what, what gets approved. Invoking an agent at runtime is an HTTP call to POST /agents/{id}/invoke; the CLI doesn't sit in that path.