# 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](/docs/cli).

---

## What ships in the package

`pip install loomcli` puts three things on your machine:

1. **The `weave` console script** — what you actually run from a terminal. Wraps the package's CLI module via the standard setuptools entry-point mechanism. Always installs as `weave`, regardless of how you installed the package.
2. **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 PowerloomClient` is the typical import.
3. **The bundled v2 schema** — every Powerloom Kind, in JSON Schema form, under `loomcli._bundled_schema.v2`. The Powerloom api container pins a `loomcli` version 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)

```bash
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:

```bash
pip install 'loomcli==0.6.2'
```

### Inside a uv-managed project

```bash
uv add loomcli
uv run weave --version
```

### Inside a Docker image

```dockerfile
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](https://github.com/shanerlevy-debug/loomcli) (separate from the Powerloom monorepo as of v034). Clone + editable install:

```bash
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:

```toml
"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:

```bash
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.yaml` runs the manifest through the same JSON Schema the api would use. Errors caught here never reach the wire. `weave validate -f file.yaml` is 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.

```yaml
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.txt` or `pyproject.toml [tool.uv] dev-dependencies`). Pinned to the same version your CI uses.
- **CI runs `weave plan`** on every PR that touches `manifests/` and posts the diff back as a PR comment. The standard `loomcli`-on-CI workflow lives in [github.com/shanerlevy-debug/loomcli/blob/main/.github/workflows/example-ci.yml](https://github.com/shanerlevy-debug/loomcli/tree/main/.github/workflows).
- **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](/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.
