Metadata-Version: 2.4
Name: kanon-cli
Version: 2.0.1
Summary: Kanon (Kanon Package Manager) CLI tool
Project-URL: Homepage, https://github.com/caylent-solutions/kanon
Project-URL: Issues, https://github.com/caylent-solutions/kanon/issues
Author-email: Caylent <solutions-owners@caylent.com>
License: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.11
Requires-Dist: defusedxml>=0.7.1
Requires-Dist: packaging>=23.0
Requires-Dist: shtab>=1.7.0
Description-Content-Type: text/markdown

# Kanon (Kanon Package Manager)

A standalone Python CLI for managing versioned DevOps automation packages
via declarative manifests.

**License:** Apache 2.0

---

## Table of Contents

- [Quick Start: Find and Add Dependencies](#quick-start-find-and-add-dependencies)
- [Tab Completion](#tab-completion)
- [Subcommands](#subcommands)
- [Git Authentication](#git-authentication)
- [Migration from kanon bootstrap](#migration-from-kanon-bootstrap)
- [What is Kanon?](#what-is-kanon)
  - [Fully customizable](#fully-customizable)
  - [Core Purpose](#core-purpose)
- [Use Cases](#use-cases)
  - [Unify Disparate Automation](#unify-disparate-automation)
  - [Platform Engineering](#platform-engineering)
  - [Multi-Project Consistency](#multi-project-consistency)
- [Quick Start](#quick-start)
  - [Prerequisites](#prerequisites)
  - [Install the Kanon CLI](#install-the-kanon-cli)
  - [Standalone Usage (No Task Runner Required)](#standalone-usage-no-task-runner-required)
  - [Integrating with Task Runners (Optional)](#integrating-with-task-runners-optional)
- [CLI Reference](#cli-reference)
  - [kanon list](#kanon-list)
  - [kanon add](#kanon-add)
  - [kanon remove](#kanon-remove)
  - [kanon install](#kanon-install)
  - [kanon clean](#kanon-clean)
  - [kanon outdated](#kanon-outdated)
  - [kanon why](#kanon-why)
  - [kanon doctor](#kanon-doctor)
  - [kanon validate](#kanon-validate)
  - [kanon catalog audit](#kanon-catalog-audit)
  - [kanon repo](#kanon-repo)
  - [kanon completion](#kanon-completion)
  - [kanon bootstrap (deprecated)](#kanon-bootstrap-deprecated)
- [.kanon Variable Reference](#kanon-variable-reference)
  - [Core Variables](#core-variables)
  - [Source Variables](#source-variables)
  - [Environment Variables](#environment-variables)
  - [Example .kanon](#example-kanon)
- [Architecture](#architecture)
  - [How It Works](#how-it-works)
  - [Directory Structure After Install](#directory-structure-after-install)
  - [Multi-Source Isolation](#multi-source-isolation)
  - [Environment Variable Portability (envsubst)](#environment-variable-portability-envsubst)
- [Creating a Manifest Repository](#creating-a-manifest-repository)
  - [Structure](#structure)
  - [Catalog entry (-marketplace.xml)](#catalog-entry--marketplacexml)
  - [remote.xml -- Git Remote Definition](#remotexml----git-remote-definition)
  - [packages.xml -- Package Declarations](#packagesxml----package-declarations)
  - [Entry-point manifest](#entry-point-manifest)
  - [Include Chains for Hierarchy](#include-chains-for-hierarchy)
  - [Updating Package Versions](#updating-package-versions)
- [Creating Packages](#creating-packages)
  - [Package Structure](#package-structure)
  - [Versioning](#versioning)
  - [Registering a Package](#registering-a-package)
  - [Symlinks via linkfile](#symlinks-via-linkfile)
- [Creating Marketplace Packages](#creating-marketplace-packages)
  - [Marketplace Manifest Structure](#marketplace-manifest-structure)
  - [Key Requirements](#key-requirements)
  - [Cascading Includes](#cascading-includes)
  - [Validation](#validation)
- [Manifest Features (PEP 440 Constraints)](#manifest-features-pep-440-constraints)
  - [PEP 440 Version Constraints in Manifests](#pep-440-version-constraints-in-manifests)
  - [PEP 440 Version Resolution in .kanon](#pep-440-version-resolution-in-kanon)
  - [Absolute Linkfile Destinations](#absolute-linkfile-destinations)
- [SSH Authentication Setup](#ssh-authentication-setup)
- [Developer Setup](#developer-setup)
  - [Prerequisites](#prerequisites-1)
  - [Install from Source](#install-from-source)
  - [Set Up Git Hooks](#set-up-git-hooks)
  - [Run Tests](#run-tests)
  - [Build](#build)
  - [Project Structure](#project-structure)
  - [Contributing](#contributing)
  - [CI/CD Pipeline](#cicd-pipeline)
- [Documentation](#documentation)
- [License](#license)

---

## Quick Start: Find and Add Dependencies

The following five-step workflow shows how to discover, inspect, add, and
install a dependency from a remote manifest catalog -- using the placeholder
URL `https://example.com/org/manifest-repo.git@main` throughout.

**Step 1: Discover available packages.**

```bash
kanon list --catalog-source 'https://example.com/org/manifest-repo.git@main'
```

Lists every package declared in the remote catalog so you can see what is
available.

**Step 2: Inspect a package.**

```bash
kanon list my-package \
  --catalog-source 'https://example.com/org/manifest-repo.git@main' \
  --detail
```

Shows the full metadata for `my-package` -- version history, description,
and source URL.

**Step 3: Add the package at a pinned version.**

```bash
kanon add 'my-package@==1.2.3' \
  --catalog-source 'https://example.com/org/manifest-repo.git@main'
```

Writes `my-package@==1.2.3` into your `.kanon` manifest file. The `==`
prefix pins to an exact version; PEP 440 range constraints (e.g., `~=1.2.0`,
`>=1.0.0,<2.0.0`) are also accepted.

**Step 4: Install (first run writes `.kanon.lock`).**

```bash
kanon install
```

Resolves all declared packages against the catalog, clones them into
`.kanon-data/sources/`, aggregates symlinks under `.packages/`, and writes
`.kanon.lock` with exact resolved versions so every subsequent install is
reproducible.

**Step 5: Commit both `.kanon` and `.kanon.lock`.**

```bash
git add .kanon .kanon.lock
git commit -m "feat: add my-package 1.2.3"
```

Committing both files ensures the entire team installs the same resolved
package versions. Never commit `.packages/` or `.kanon-data/` -- these are
ephemeral and are gitignored automatically by `kanon install`.

---

## Tab Completion

Kanon ships built-in shell completion for bash and zsh via the
`kanon completion <shell>` subcommand. Run `eval "$(kanon completion bash)"`
(or `zsh`) once in your shell session, or add it to your shell RC file, to
enable tab-completion of subcommand names, flags, and catalog entries. For
persistent installation and advanced options including fish support and
system-wide setup, see [docs/shell-completion.md](docs/shell-completion.md).

---

## Subcommands

| Subcommand | Summary | Doc |
| --- | --- | --- |
| `kanon list` | List packages available in a catalog or show detail for one | [docs/list-and-add.md](docs/list-and-add.md) |
| `kanon add` | Add a package (with optional version constraint) to `.kanon` | [docs/list-and-add.md](docs/list-and-add.md) |
| `kanon remove` | Remove a package from `.kanon` | [docs/list-and-add.md](docs/list-and-add.md) |
| `kanon outdated` | Show packages in `.kanon` that have newer versions available | [docs/outdated-and-why.md](docs/outdated-and-why.md) |
| `kanon why` | Explain why a specific package version was resolved | [docs/outdated-and-why.md](docs/outdated-and-why.md) |
| `kanon install` | Resolve, clone, and symlink all packages; writes `.kanon.lock` | [docs/lockfile.md](docs/lockfile.md) |
| `kanon doctor` | Diagnose the local Kanon installation and report problems | [docs/doctor.md](docs/doctor.md) |
| `kanon catalog audit` | Audit a catalog for missing or malformed entries | [docs/catalog-author-guide.md](docs/catalog-author-guide.md) |
| `kanon validate xml` | Validate XML manifests under `repo-specs/` | [docs/repo/manifest-format.md](docs/repo/manifest-format.md) |
| `kanon validate marketplace` | Validate marketplace XML manifests under `repo-specs/` | [docs/repo/manifest-format.md](docs/repo/manifest-format.md) |
| `kanon validate metadata` | Validate catalog entry metadata | [docs/catalog-author-guide.md](docs/catalog-author-guide.md) |
| `kanon clean` | Remove synced packages and Kanon state (`--orphans` also prunes unreferenced marketplaces) | [docs/lifecycle.md](docs/lifecycle.md) |
| `kanon repo` | Low-level manifest-driven repo sync subsystem | [docs/repo/README.md](docs/repo/README.md) |
| `kanon completion` | Emit a shell completion script for bash or zsh | [docs/shell-completion.md](docs/shell-completion.md) |
| `kanon bootstrap` | **deprecated (removed in 2.0; exits 3)** -- use `kanon list` / `kanon add` instead | [docs/migration-bootstrap-to-add.md](docs/migration-bootstrap-to-add.md) |

---

## Git Authentication

Kanon uses the `git` binary for all remote operations and never prompts for
credentials or caches them itself -- authentication is delegated entirely to
the operator's git client (SSH keys, credential helpers, `GIT_TOKEN`, etc.).
For setup instructions covering SSH key forwarding, HTTPS token helpers, and
URL rewriting for private Git hosts, see
[docs/git-auth-setup.md](docs/git-auth-setup.md).

---

## Migration from kanon bootstrap

The `kanon bootstrap` subcommand is deprecated. Its catalog-discovery and
project-scaffolding responsibilities have been replaced by `kanon list`
(discover and inspect packages) and `kanon add` (add a pinned dependency to
`.kanon`). If your workflow currently uses `kanon bootstrap <entry>`, the
[docs/migration-bootstrap-to-add.md](docs/migration-bootstrap-to-add.md)
guide walks through the equivalent `kanon list` + `kanon add` + `kanon
install` steps and explains the lockfile model that replaces hand-editing
`.kanon`.

---

## What is Kanon?

Kanon is a **DevOps Platform Dependency Manager** that brings
version-controlled, reproducible automation to your projects through
declarative manifests. Kanon enables you to centralize, version, and share
automation across your organization without replacing your existing tools.

**Solves a common problem:** Organizations have quality automation and operational knowledge scattered across teams -- build conventions, linting rules, security scanning, test frameworks, local dev tooling, and shared markdown documentation that work well but are not widely adopted because they are hard to discover, version, test, and distribute. Kanon enables you to package this automation and share it across projects in a tested, reproducible way.

### Fully customizable

* **Public or Private** -- Use public repositories or host everything privately within your organization
* **Your Infrastructure** -- Point to your own Git repositories and package sources
* **Your Standards** -- Define your own manifests, packages, and automation
* **Portable** -- Teams retain access to automation even after external partnerships end

### Core Purpose

* **Platform Dependency Management** -- Centralize and version your DevOps automation, shared knowledge, dependencies, and standards
* **Flexible Overlay** -- Works alongside your preferred build tools and dependency managers, or standalone with no task runner at all
* **Team Standards** -- Share tested, versioned automation, tasks, and approaches across teams dynamically
* **Tool Agnostic** -- Adapts to your workflow, not the other way around

## Use Cases

### Unify Disparate Automation

Your organization has quality automation scattered across teams -- testing frameworks, linting configs, deployment scripts, security scans -- but they are not widely adopted because they are hard to find, version, and integrate. Kanon lets you package this automation, version it, and make it available to all teams through simple manifests.

### Platform Engineering

Provide golden paths and paved roads to development teams. Package your organization's standards, policies, automation, and shared operational knowledge as versioned dependencies that teams can pull into their projects.

This can include CI/CD workflows, security policies, deployment automation, coding standards, architecture guidance, operational runbooks, and shared markdown knowledge bases used by both developers and AI coding agents.

### Multi-Project Consistency

Ensure the same testing, linting, security scanning, and deployment automation across projects without copy-pasting or manual synchronization.

---

## Quick Start

### Prerequisites

- Python 3.11+
- [pipx](https://pipx.pypa.io/) on PATH
  (`python3 -m pip install --user pipx && pipx ensurepath`)
- Git
- If authenticating with Git via SSH, see
  [SSH Authentication Setup](#ssh-authentication-setup)

### Install the Kanon CLI

`kanon-cli` is published to [PyPI](https://pypi.org/project/kanon-cli/). The
recommended install method depends on the use case:

**Production / general use** -- isolated CLI install via pipx:

```bash
pipx install kanon-cli
```

**Local development on this repository** -- editable install into the
project's virtualenv:

```bash
pip install -e .
```

(Editable mode lets local source edits take effect immediately without
reinstalling. CI uses `pip install kanon-cli` for ephemeral runners; see
`docs/pipeline-integration.md`.)

### Standalone Usage (No Task Runner Required)

Kanon works directly from the command line. No task runner is needed. The
workflow is declarative: you discover entries in a remote catalog, add the
ones you want to `.kanon`, install them, and (optionally) clean up. Every
command that resolves a catalog needs a catalog source -- either the
`--catalog-source <url>@<ref>` flag or the `KANON_CATALOG_SOURCE`
environment variable.

```bash
# Set once in your shell rc file -- pin to the current major version
export KANON_CATALOG_SOURCE='https://github.com/your-org/your-catalog-repo.git@>=2.0.0,<3.0.0'
```

**1. Discover entries in the catalog:**

```bash
kanon list                   # all entry names, one per line
kanon list --detail          # human-readable record per entry
kanon list my-tool --detail  # narrow to entries matching a substring
```

`kanon list` reads the `*-marketplace.xml` files under `repo-specs/` in the
manifest repo and prints one catalog entry name per line.

**2. Add entries to `.kanon`:**

```bash
kanon add my-tool                       # pin to the highest available version
kanon add 'my-tool@>=1.0.0,<2.0.0'      # pin with a PEP 440 constraint
kanon add my-tool --marketplace-install # also enable the marketplace lifecycle
```

`kanon add` resolves each entry against the catalog and writes the
`KANON_SOURCE_<name>_{URL,REVISION,PATH}` triple into `.kanon`, creating the
file with a standard header when it does not yet exist.

**3. Install (sync all packages, write `.kanon.lock`):**

```bash
kanon install
```

This reconciles `.kanon` against `.kanon.lock`, runs the repo
init/envsubst/sync lifecycle for every source, aggregates packages into
`.packages/` via symlinks, creates source workspaces under
`.kanon-data/sources/`, writes `.kanon.lock` with the exact resolved SHAs,
and adds `.packages/` and `.kanon-data/` to `.gitignore`.

**4. Clean (full teardown):**

```bash
kanon clean              # remove .packages/, .kanon-data/, marketplace dir
kanon clean --orphans    # also prune kanon-owned marketplaces no longer referenced
```

`kanon clean` removes all synced packages and Kanon state directories, and
(when `KANON_MARKETPLACE_INSTALL=true`) uninstalls marketplace plugins.

**Important:** All synced files in `.packages/` and `.kanon-data/` are
ephemeral and should not be committed. Commit only `.kanon` and `.kanon.lock`
to your repository.

The `@<ref>` portion of a catalog source accepts a branch name, a tag, the
special value `latest` (which resolves to the highest semver tag), or a PEP
440 version constraint (e.g., `~=2.0.0`, `>=2.0.0,<3.0.0`). Version
constraints are resolved against the repository's git tags via
`git ls-remote`. The manifest repo IS the catalog: every `*-marketplace.xml`
file under `repo-specs/` carrying a `<catalog-metadata>` block is one catalog
entry. There is no separate `catalog/` directory.

Manifest repositories should use [semantic versioning](https://semver.org/)
for git tags. Pinning to a major version range (e.g., `>=2.0.0,<3.0.0`)
allows automatic pickup of minor and patch releases while preventing
unexpected breaking changes.

### Integrating with Task Runners (Optional)

Kanon works standalone via `kanon install` and `kanon clean`. You can wrap
these commands in any build tool or task runner by creating targets that
delegate to the CLI.

### Tab Completion

Kanon ships with built-in shell completion for bash and zsh via the
`kanon completion <shell>` subcommand. The generated script enables
tab-completion of subcommand names and flags in your shell session.

**Quick setup:**

```bash
# bash -- add to ~/.bashrc or source once in your current session
eval "$(kanon completion bash)"

# zsh -- add to ~/.zshrc
eval "$(kanon completion zsh)"
```

For persistent installation and advanced options (fish, system-wide install,
oh-my-zsh), see `docs/shell-completion.md`.

---

## CLI Reference

```bash
kanon --help                              # Top-level help
kanon --version                           # Show version
```

Run `kanon <command> --help` for the full option list of any command. The
sections below summarise each 2.0 command. A catalog source (the
`--catalog-source <url>@<ref>` flag or the `KANON_CATALOG_SOURCE`
environment variable) is required by `list`, `add`, `outdated`, `why`, and
`catalog audit`; for `install` and `doctor` the `.kanon.lock` `[catalog]`
source is used as a fallback when present and consistent.

### kanon list

Discovers catalog entries. Prints one entry name per line to stdout, sorted
lexicographically, by reading the `*-marketplace.xml` files under
`repo-specs/` in the catalog source.

```bash
kanon list                       # all entry names
kanon list foo                   # substring filter (name/desc/keywords)
kanon list --regex '^foo'        # regex filter
kanon list --detail              # human-readable record per entry
kanon list --format json         # structured JSON array
kanon list --tree                # three-layer ASCII dependency tree
kanon list --all-versions        # walk historical tagged versions
```

Key options: `--format {names,json}`, `--detail`, `--tree` (with
`--max-depth N`, `--no-filter-required`), `--all-versions` (with `--limit N`,
`--no-limit`, `--since-version <spec>`), `--regex <pattern>`,
`--match-fields <csv>`. A positional `<substring>` and `--regex` are mutually
exclusive; `--format json` is incompatible with `--tree`.

### kanon add

Resolves catalog entries from the catalog source and appends the
`KANON_SOURCE_<name>_{URL,REVISION,PATH}` triple to `.kanon`, creating the
file with a standard header when absent.

```bash
kanon add my-tool                       # pin to highest PEP 440 tag
kanon add 'my-tool@>=1.0.0,<2.0.0'      # pin with a PEP 440 constraint
kanon add my-tool --marketplace-install # enable the marketplace lifecycle
kanon add my-tool --dry-run             # print the diff without writing
```

Each entry is `<name>` or `<name>@<spec>` (PEP 440 constraint). Key options:
`--kanon-file <path>` (default `./.kanon`, env `KANON_KANON_FILE`), `--force`
(overwrite an existing block), `--dry-run`, and the mutually-exclusive
`--marketplace-install` / `--no-marketplace-install` (applied only when the
file is created).

### kanon remove

Removes the three `KANON_SOURCE_<name>_{URL,REVISION,PATH}` lines for one or
more entries from `.kanon`.

```bash
kanon remove my-tool                      # canonical source OR entry name
kanon remove my-tool --dry-run            # preview removed lines
kanon remove my-tool --force              # skip not-fully-present sources
```

Each `<name>` may be the canonical source name (e.g. `foo_bar`) or the
original entry name (e.g. `Foo-Bar`); both normalise to the same keys.
Removal is atomic: if any requested name is not fully present (fewer than
three matching keys) and `--force` is not set, the command exits non-zero and
the file is unchanged.

### kanon install

Executes the full install lifecycle and reconciles `.kanon` against
`.kanon.lock`.

```bash
kanon install                     # auto-discover .kanon by walking up from cwd
kanon install .kanon              # explicit path to .kanon file
kanon install --strict-lock       # error when an orphaned lock entry is present
kanon install --strict-drift      # error when a branch source has drifted
kanon install --refresh-lock      # re-resolve every transitive version from scratch
kanon install --refresh-lock-source NAME  # re-resolve one source's chain only
```

**Behavior:**

- Parses `.kanon`, then runs the repo init/envsubst/sync lifecycle for each
  source (alphabetical order).
- Aggregates packages into `.packages/` via symlinks; detects cross-source
  name collisions (fail-fast); updates `.gitignore`.
- Reconciles against `.kanon.lock`: a plain `install` prunes orphaned lock
  entries (a source removed from `.kanon`) with an info-line; `--strict-lock`
  promotes that to an error. Branch drift (a locked SHA differing from the
  branch's current tip) reuses the locked SHA with an info-line; `--strict-drift`
  promotes that to an error.
- **Auto-prune:** when a source is removed from `.kanon`, its registered
  marketplace is unregistered on the next install.
- If `KANON_MARKETPLACE_INSTALL=true`: runs the marketplace install lifecycle.

`--refresh-lock` and `--refresh-lock-source NAME` require a CLI-supplied or
`KANON_CATALOG_SOURCE` catalog source; the lockfile fallback is disabled on
those paths.

### kanon clean

Executes the full teardown lifecycle.

```bash
kanon clean                       # auto-discover .kanon by walking up from cwd
kanon clean .kanon                # explicit path to .kanon file
kanon clean --orphans             # also unregister orphaned marketplaces
```

**Behavior:**

1. If `KANON_MARKETPLACE_INSTALL=true`: uninstalls plugins and removes the
   marketplace directory.
2. Removes the `.packages/` and `.kanon-data/` directories.

With `--orphans`, before the normal teardown kanon also unregisters any
kanon-owned marketplaces recorded in `.kanon.lock` that are no longer
referenced by `.kanon`, pruning them from `~/.claude`.

### kanon outdated

Compares each source in `.kanon` against the catalog and emits a table of
`name | current | latest-matching-spec | latest-available | upgrade-type`.

```bash
kanon outdated                    # table output, always exits 0
kanon outdated --format json      # JSON array, one object per source
kanon outdated --fail-on-upgrade  # exit 1 when any source has an upgrade (CI gate)
```

The `current` column comes from `.kanon.lock` when present, or is
live-resolved against the catalog when absent. Key options:
`--fail-on-upgrade`, `--format {table,json}`, `--kanon-file`, `--lock-file`.

### kanon why

Explains why a transitive dependency is in the tree. Reads `.kanon`, resolves
the full dependency tree (from `.kanon.lock` when present, else live-resolves
against the catalog), and prints every chain reaching the requested node.

```bash
kanon why my-project              # by source name, repo URL, or XML path
kanon why https://example.com/org/project.git
kanon why --format json my-project
```

The argument is matched three ways: a `<project>` repo URL (canonicalized), a
transitive XML manifest path (exact-string equality), or a top-level source
name (normalized via `derive_source_name`). A catalog source is required only
on the live-resolve path (when `.kanon.lock` is absent).

### kanon doctor

Diagnoses `.kanon` / `.kanon.lock` health against the current project
directory.

```bash
kanon doctor                            # run all health checks
kanon doctor --strict-drift             # promote branch-drift findings to errors
kanon doctor --refresh-completion-cache # invalidate the shell completion cache
kanon doctor --prune-cache              # prune stale cache files (age-based)
```

Reports findings including `.kanon`/`.kanon.lock` consistency (via
`kanon_hash`), hand-edit detection, orphaned lock entries, branch drift,
dangling-SHA detection, a `NO_SOURCES` finding for a zero-source `.kanon`, and
a remote-reachability sanity check (warning only). See
[docs/doctor.md](docs/doctor.md) for the full subcheck reference.

### kanon validate

Validates manifest XML files. Subcommands:

```bash
kanon validate xml          # well-formedness, attributes, include chains
kanon validate marketplace  # linkfile dest, includes, uniqueness, tag format
kanon validate metadata     # catalog-metadata soft-spots (no network access)
```

- **`validate xml`** -- checks well-formed XML, required attributes on
  `<project>` and `<remote>`, and that `<include>` names point to existing
  files.
- **`validate marketplace`** -- checks `<linkfile dest>` attributes, include
  chain integrity, project path uniqueness, and revision tag format.
- **`validate metadata`** -- checks the `<catalog-metadata>` blocks for
  required/recommended fields, source-name derivation, and entry-name
  uniqueness, without cloning or calling git. Supports `--format {text,json}`.

All three accept `--repo-root REPO_ROOT` (default: auto-detect via
`git rev-parse`).

### kanon catalog audit

Audits a manifest repo against the catalog standards contract (the five
soft-spot rules).

```bash
kanon catalog audit                       # audit the current directory
kanon catalog audit ./scratch --strict    # promote warnings to errors
kanon catalog audit https://example.com/org/repo.git@main  # audit a remote source
kanon catalog audit --check metadata,tag-format            # run a subset of checks
```

`<dir-or-source>` is a local directory (must contain `repo-specs/`) or a
remote `<git_url>@<ref>` source; defaults to `.`. Options: `--check <subset>`
(valid values: `all`, `entry-name-uniqueness`, `metadata`, `remote-url`,
`source-name-derivation`, `tag-format`), `--format {text,json}`, `--strict`.
See [docs/catalog-author-guide.md](docs/catalog-author-guide.md).

### kanon repo

Catalog-author / low-level subcommand: runs kanon's `repo` dispatcher.
All trailing arguments after `kanon repo` are forwarded verbatim to it.

```bash
kanon repo init -u <url> -b <branch> -m <manifest>
kanon repo sync --jobs=4
kanon repo help
```

`--repo-dir REPO_DIR` sets the `.repo` directory (default: `${KANON_REPO_DIR}`
or `.repo`). See [docs/repo/README.md](docs/repo/README.md).

### kanon completion

Emits the shell completion script for kanon to stdout.

```bash
kanon completion bash > /etc/bash_completion.d/kanon
kanon completion zsh  > "${fpath[1]}/_kanon"
```

Target shell choices: `bash`, `zsh`. See
[docs/shell-completion.md](docs/shell-completion.md).

### kanon bootstrap (deprecated)

`kanon bootstrap` was removed in 2.0 (a breaking change). It no longer
performs any work and exits with code `3`, directing you to `kanon add` /
`kanon list`. The catalog model changed: a manifest repo no longer has a
separate `catalog/<name>/` location and the kanon wheel no longer bundles a
catalog. Use `kanon list` to discover entries and `kanon add` to add them.
See [docs/migration-bootstrap-to-add.md](docs/migration-bootstrap-to-add.md).

---

## .kanon Variable Reference

The `.kanon` file is a shell-compatible `KEY=VALUE` configuration file that
drives the Kanon lifecycle. Lines starting with `#` are comments. Values can
reference environment variables using `${VAR}` syntax (e.g.,
`${HOME}/.claude-marketplaces`). Every `.kanon` variable can be overridden
by an environment variable of the same name, enabling CI/CD pipelines to
customize behavior without modifying the file.

### Core Variables

**`GITBASE`** (Required)
Base Git URL for `kanon repo envsubst`
(e.g., `https://github.com/your-org/`).

**`CLAUDE_MARKETPLACES_DIR`** (Conditional)
Directory for marketplace symlinks. Required when
`KANON_MARKETPLACE_INSTALL=true`.

**`KANON_MARKETPLACE_INSTALL`** (Optional, default: `false`)
Boolean toggle for marketplace lifecycle.

### Source Variables

Sources are auto-discovered from `KANON_SOURCE_<name>_URL` variable patterns
and processed in alphabetical order by name. Each source requires three
variables:

**`KANON_SOURCE_<name>_URL`** (Required)
Git URL for the named source's manifest repository.

**`KANON_SOURCE_<name>_REVISION`** (Required)
Branch, exact tag, or PEP 440 constraint (e.g. `refs/tags/~=1.1.0`) for the
named source.

**`KANON_SOURCE_<name>_PATH`** (Required)
Path to the entry-point manifest XML for the named source.

### Environment Variables

**`KANON_CATALOG_SOURCE`**
Remote catalog source as `<git_url>@<ref>` where ref is a branch, tag,
`latest`, or PEP 440 constraint (e.g., `>=2.0.0,<3.0.0`). One of this env var
or the `--catalog-source` flag is **required** by `kanon list`, `kanon add`,
`kanon outdated`, `kanon why`, and `kanon catalog audit`. For `kanon install`
and `kanon doctor`, the `.kanon.lock` `[catalog].source` is used as a fallback
when present and consistent. Resolution precedence is:
`--catalog-source` flag > `KANON_CATALOG_SOURCE` env var > lock `[catalog]`
source > the `[catalog]` block in `.kanon`.

### Example .kanon

```properties
# Shared env vars for envsubst
GITBASE=https://github.com/your-org/
CLAUDE_MARKETPLACES_DIR=${HOME}/.claude-marketplaces

# Marketplace install toggle
KANON_MARKETPLACE_INSTALL=true

# Source: build -- build tooling packages
KANON_SOURCE_build_URL=https://github.com/your-org/kanon-manifests.git
KANON_SOURCE_build_REVISION=main
KANON_SOURCE_build_PATH=repo-specs/build/meta.xml

# Source: marketplaces -- plugin marketplaces
KANON_SOURCE_marketplaces_URL=https://github.com/your-org/kanon-manifests.git
KANON_SOURCE_marketplaces_REVISION=main
KANON_SOURCE_marketplaces_PATH=repo-specs/marketplaces/meta.xml
```

---

## Architecture

```text
                    ┌─────────────────────────┐
                    │     Kanon CLI           │
                    │  (list / add / install /│
                    │   clean / validate)     │
                    └───────────┬─────────────┘
                                │
               defines          │            uses
                                v
              ┌────────────────────────────────────────┐
              │       Manifest Repository              │
              │  - Top-level dependency manifests      │
              │  - Declares relationships between      │
              │    domain and automation repos         │
              └──────────────────┬─────────────────────┘
                                 │
        references               │                references
                                 │
             v                                       v
┌───────────────────────┐                ┌────────────────────────┐
│  Package Repositories │                │ Automation Repositories│
│ (build conventions,   │                │ (shared tasks,         │
│  linting, security)   │                │  validation, scanning) │
└────────────┬──────────┘                └───────────┬────────────┘
             │                                       │
             └───────────────────┬───────────────────┘
                                 │
                                 v
                   ┌────────────────────────────┐
                   │   kanon repo subsystem     │
                   │ (manifest-driven sync with │
                   │  envsubst + PEP 440)       │
                   │ Executes manifests, syncs  │
                   │ repos, manages workspace   │
                   └────────────────────────────┘
```

### How It Works

Kanon's `kanon repo` subsystem orchestrates dependencies across Git
repositories via XML manifests. Manifests define what to clone, where to
place it, and how to wire it together.

The install lifecycle follows three steps per source:

1. **`kanon repo init`** -- Clones the manifest repository. `${VARIABLE}`
   placeholders remain as-is in the XML.
2. **`kanon repo envsubst`** -- Reads variables from `.kanon` (e.g.,
   `GITBASE`) and replaces `${VARIABLE}` placeholders in all manifest XML
   files.
3. **`kanon repo sync`** -- Clones packages using the now-resolved URLs into
   `.packages/`.

After all sources are synced, Kanon aggregates their packages into a single
`.packages/` directory using symlinks, giving consumers a unified view
regardless of which source provided each package.

### Directory Structure After Install

```text
project/
  .kanon                            # Configuration (committed)
  ...                               # Other catalog entry files (committed)
  .kanon-data/                      # Kanon state (gitignored)
    sources/
      build/                        # Isolated source workspace
        .repo/
        .packages/
          my-build-conventions/
      marketplaces/                 # Isolated source workspace
        .repo/
        .packages/
          my-marketplace-plugin/
  .packages/                        # Aggregated symlinks (gitignored)
    my-build-conventions -> \
      ../.kanon-data/sources/build/.packages/my-build-conventions
    my-marketplace-plugin -> \
      ../.kanon-data/sources/marketplaces/.packages/my-marketplace-plugin
```

### Multi-Source Isolation

Each source is initialized and synced in its own isolated directory under
`.kanon-data/sources/<name>/`. Sources cannot interfere with each other --
each gets its own `kanon repo init` / `kanon repo sync` cycle. If two sources
produce a package with the same name, Kanon detects the collision and fails
immediately with an actionable error message.

### Environment Variable Portability (envsubst)

The `envsubst` feature makes manifests portable across organizations. Instead
of hard-coding Git URLs in manifest XML, you use `${GITBASE}` placeholders:

```xml
<!-- Portable -- resolved from .kanon at install time -->
<remote name="origin" fetch="${GITBASE}"/>
```

Adopting Kanon for a different organization means changing one line in
`.kanon`:

```properties
GITBASE=https://github.com/your-company/
```

CI/CD pipelines can override this via environment variables without modifying
`.kanon`:

```bash
GITBASE=https://git.internal.company.com/ kanon install
```

For full documentation, see [docs/how-it-works.md](docs/how-it-works.md).

---

## Creating a Manifest Repository

A manifest repository contains `repo-specs/` with XML manifests that define
what packages to sync, from which repositories, and at which versions. **The
manifest repo IS the catalog** -- there is no separate `catalog/` directory.
Each catalog entry is a single `*-marketplace.xml` file under `repo-specs/`
that carries a nested `<catalog-metadata>` block; the
`<catalog-metadata><name>` child is the entry name consumers pass to
`kanon add <name>`. See
[docs/creating-manifest-repos.md](docs/creating-manifest-repos.md) for the
full catalog-author guide and
[docs/repo/manifest-format.md](docs/repo/manifest-format.md) for the
underlying XML schema.

### Structure

```text
my-manifest-repo/
  repo-specs/
    git-connection/
      remote.xml             # Git remotes with ${GITBASE} placeholders
    my-archetype/
      my-archetype-marketplace.xml  # Catalog entry (carries <catalog-metadata>)
      packages.xml           # Package repos with pinned versions
```

### Catalog entry (-marketplace.xml)

Each catalog entry is a `*-marketplace.xml` file containing exactly one
nested `<catalog-metadata>` block. Required fields are `name`,
`display-name`, `description`, and `version`; recommended fields are `type`,
`owner-name`, `owner-email`, and `keywords` (comma-separated). The legacy
flat-attribute scheme (metadata as XML attributes) is rejected.

```xml
<package>
  <catalog-metadata>
    <name>my-archetype</name>
    <display-name>My Archetype</display-name>
    <description>Build conventions and lint config for service repos.</description>
    <version>1.0.0</version>
    <type>library</type>
    <owner-name>Platform Team</owner-name>
    <owner-email>platform@example.com</owner-email>
    <keywords>build,lint,conventions</keywords>
  </catalog-metadata>
  <include name="repo-specs/my-archetype/packages.xml" />
</package>
```

`kanon validate metadata` and `kanon catalog audit` enforce the
`<catalog-metadata>` contract.

### remote.xml -- Git Remote Definition

Defines where packages are hosted using `${GITBASE}` for portability:

```xml
<manifest>
  <remote name="origin" fetch="${GITBASE}" />
  <default remote="origin" revision="refs/tags/1.0.0" />
</manifest>
```

### packages.xml -- Package Declarations

Lists each package repository, its local path, and the pinned version:

```xml
<manifest>
  <include name="repo-specs/git-connection/remote.xml" />

  <project name="my-build-conventions"
           path=".packages/my-build-conventions"
           remote="origin"
           revision="refs/tags/1.0.0" />

  <project name="my-lint-config"
           path=".packages/my-lint-config"
           remote="origin"
           revision="refs/tags/2.1.0" />
</manifest>
```

### Entry-point manifest

The `*-marketplace.xml` catalog entry is the entry point referenced by the
`KANON_SOURCE_<name>_PATH` value that `kanon add` writes into `.kanon`. It
pulls in the package declarations via `<include>`:

```xml
<package>
  <catalog-metadata>
    <!-- ... required + recommended fields ... -->
  </catalog-metadata>
  <include name="repo-specs/my-archetype/packages.xml" />
</package>
```

### Include Chains for Hierarchy

Manifests can include other manifests via `<include>` tags, forming a
hierarchy. This enables cascading configurations where common packages are
defined once and specialized packages are layered on top:

```text
my-archetype-marketplace.xml
  └── packages.xml (leaf -- e.g., specific project type)
        └── packages.xml (framework level)
              └── packages.xml (language level)
                    └── packages.xml (common/base)
```

Each level includes its parent and adds its own package entries. The
`kanon repo` subsystem recursively resolves all includes, accumulating a
unified set of packages.

### Updating Package Versions

1. Tag the package repository with the new semver version
2. Update the `revision` attribute in the corresponding `packages.xml`
3. Run `kanon validate xml` to verify manifests remain valid
4. Tag and push the manifest repository

Projects pick up the new versions on next `kanon install`.

For more details, see [CONTRIBUTING.md](CONTRIBUTING.md).

---

## Creating Packages

A package is a Git repository containing automation scripts (configuration
files, shell scripts, etc.) tagged with semver versions. Kanon syncs packages
to `.packages/` where build tools can discover and apply them.

### Package Structure

```text
my-package/
  automation-script.sh        # Shell scripts, config files, etc.
  config/                     # Optional: configuration files
  README.md                   # Package documentation
  CHANGELOG.md                # Version history
```

### Versioning

Use [semantic versioning](https://semver.org/) with Git tags:

- **MAJOR** -- Breaking changes (renamed tasks, removed config, changed
  behavior)
- **MINOR** -- New features (new tasks, new config options)
- **PATCH** -- Bug fixes (corrected config, fixed task behavior)

```bash
git tag -a 1.0.0 -m "Release 1.0.0"
git push origin 1.0.0
```

### Registering a Package

Add the package to a manifest's `packages.xml`:

```xml
<project name="my-package"
         path=".packages/my-package"
         remote="origin"
         revision="refs/tags/1.0.0" />
```

### Symlinks via linkfile

Some packages contain assets (configuration files, templates) that tools
expect at conventional paths. The `<linkfile>` element creates symlinks from
the package directory to the project root:

```xml
<project name="my-lint-config"
         path=".packages/my-lint-config"
         remote="origin"
         revision="refs/tags/1.0.0">
  <linkfile src="config/checkstyle/checkstyle.xml"
            dest="config/checkstyle/checkstyle.xml" />
</project>
```

After `kanon repo sync`, the project has `config/checkstyle/checkstyle.xml`
as a symlink pointing into `.packages/`. These symlinked paths should be
gitignored since they are regenerated by `kanon install`.

---

## Creating Marketplace Packages

Marketplace packages use `<linkfile>` symlinks to expose plugins to Claude
Code. They follow a cascading manifest hierarchy where each level includes
its parent, enabling shared tools across project types while adding
specialized plugins at each level.

### Marketplace Manifest Structure

```xml
<manifest>
  <!-- Include shared remote definitions -->
  <include name="repo-specs/git-connection/remote.xml" />

  <!-- Add this level's marketplace project -->
  <project name="my-marketplace-packages"
           path=".packages/my-marketplace-dev-lint"
           remote="origin"
           revision="refs/tags/development/dev-lint/1.0.0">
    <linkfile src="development/dev-lint"
              dest="${CLAUDE_MARKETPLACES_DIR}/my-marketplace-dev-lint" />
  </project>
</manifest>
```

### Key Requirements

- All `<linkfile dest>` attributes must start with
  `${CLAUDE_MARKETPLACES_DIR}/`
- Each `<project path>` must be unique across all manifests
- The `KANON_MARKETPLACE_INSTALL` flag in `.kanon` must be set to `true`
- `CLAUDE_MARKETPLACES_DIR` must be defined in `.kanon`

### Naming Convention

Marketplace manifest files must be named `*-marketplace.xml` (e.g.,
`claude-history-marketplace.xml`,
`immutable-audit-trail-marketplace.xml`).
The `kanon validate marketplace` command discovers files matching this
pattern under `repo-specs/`.

### Cascading Includes

Manifests support cascading `<include>` chains where each level includes its
parent. This enables shared remote definitions, common project entries, and
layered composition across project types. Currently marketplace manifests use
a flat structure (each manifest includes `remote.xml` directly), but
cascading hierarchies are fully supported when needed.

### Validation

```bash
kanon validate marketplace
```

This checks linkfile destination prefixes, include chain integrity, project
path uniqueness, and revision format validity.

For full documentation, see
[docs/claude-marketplaces-guide.md](docs/claude-marketplaces-guide.md).

---

## Manifest Features (PEP 440 Constraints)

Kanon adds the following capabilities to manifest-driven sync:

### PEP 440 Version Constraints in Manifests

`<project revision>` accepts [PEP 440](https://peps.python.org/pep-0440/)
version constraint syntax in addition to a branch, tag, or commit SHA.
Constraints resolve to the best matching tag at sync time.

#### How It Works

The resolver splits the `revision` attribute at the last `/` into a tag-path
prefix and a constraint. It filters available tags by that prefix, evaluates
the constraint, and returns the highest matching version.

```text
revision="refs/tags/example/development/dev-lint/~=1.2.0"
         |------------- prefix ----------------| |- constraint -|

1. Filter tags starting with  refs/tags/example/development/dev-lint/
2. Parse version suffixes:    1.0.0, 1.2.0, 1.2.3, 1.3.0, 2.0.0
3. Evaluate ~=1.2.0:          1.2.0   1.2.3   (others excluded)
4. Return highest match:      refs/tags/example/development/dev-lint/1.2.3
```

#### Supported Constraint Types

| Operator | Syntax | Meaning |
| --- | --- | --- |
| Patch-compatible | `~=1.2.0` | `>=1.2.0, <1.3.0` (any patch in 1.2.x) |
| Range | `>=1.0.0,<2.0.0` | Any version up to (not including) 2.0.0 |
| Wildcard | `*` | Any available version (selects the latest) |
| Exact | `==1.2.3` | Only version 1.2.3 |
| Minimum | `>=1.0.0` | 1.0.0 or higher |
| Exclusion | `!=1.0.1` | Any version except 1.0.1 |

#### XML Escaping

Certain characters are reserved in XML and must be escaped inside
attribute values. The most common case is `<` in range constraints:

| Character | Escape | When required |
| --- | --- | --- |
| `<` | `&lt;` | Always (reserved XML character) |
| `&` | `&amp;` | Always (reserved XML character) |
| `"` | `&quot;` | Inside `"` delimited attributes |
| `'` | `&apos;` | Inside `'` delimited attributes |
| `>` | `&gt;` | Optional (`>` also valid in attributes) |

Example with range constraint:

```xml
<project name="my-package"
         path=".packages/my-package"
         remote="origin"
         revision="refs/tags/my-package/>=1.0.0,&lt;2.0.0" />
```

### PEP 440 Version Resolution in .kanon

The CLI supports PEP 440 constraint syntax in `KANON_SOURCE_<name>_REVISION`
entries in `.kanon`. Constraints are resolved against available git tags
before being passed to the sync engine.

#### Supported Operators

| Operator | Syntax | Meaning |
| --- | --- | --- |
| Compatible release | `~=1.2.0` | `>=1.2.0, <1.3.0` |
| Range | `>=1.0.0,<2.0.0` | Any version in range |
| Exact | `==1.2.3` | Only 1.2.3 |
| Minimum | `>=1.0.0` | 1.0.0 or higher |
| Exclusion | `!=1.0.1` | Any version except 1.0.1 |
| Wildcard | `*` | Latest available |

Plain strings without PEP 440 operators pass through unchanged.

#### Prefixed Constraints (KANON_SOURCE_\<name\>_REVISION)

Source revisions support an optional `refs/tags/` prefix. This is recommended
because the resolved value is passed to `kanon repo init -b`, which accepts
full ref paths:

```properties
# Resolves to refs/tags/1.1.2 -- works directly with kanon repo init -b
KANON_SOURCE_build_REVISION=refs/tags/~=1.1.0

# Namespaced -- only considers tags under that path
KANON_SOURCE_build_REVISION=refs/tags/dev/python/my-lib/~=1.2.0

# Also supported -- resolves against all tags
KANON_SOURCE_build_REVISION=~=1.1.0
```

For full details, see [docs/version-resolution.md](docs/version-resolution.md).

### Absolute Linkfile Destinations

`<linkfile dest>` accepts absolute paths after `envsubst` expansion, enabling
marketplace symlinks to directories outside the project (e.g.,
`${CLAUDE_MARKETPLACES_DIR}/...`).

---

## SSH Authentication Setup

Kanon uses HTTPS Git URLs internally. If you authenticate with GitHub via SSH
instead of HTTPS tokens, configure Git to rewrite HTTPS URLs to SSH globally:

```bash
git config --global url."git@github.com:".insteadOf "https://github.com/"
```

This tells Git to use SSH for all `github.com` requests, which Kanon's
`git clone`, `git ls-remote`, and `kanon repo` commands will then use
automatically.

**Note:** The `--global` flag is required. Using `--local` will not work
because `kanon repo` operates in its own working directories with their own
local Git configuration.

For other Git hosts, adjust the URL accordingly:

```bash
git config --global url."git@gitlab.com:".insteadOf "https://gitlab.com/"
git config --global \
  url."git@bitbucket.org:".insteadOf "https://bitbucket.org/"
```

To verify the configuration:

```bash
git config --global --get-regexp url
```

---

## Developer Setup

### Prerequisites

- Python 3.11+
- [uv](https://docs.astral.sh/uv/)

### Install from Source

```bash
make install-dev
```

### Set Up Git Hooks

```bash
make install-hooks
```

### Run Tests

```bash
make test              # All tests with coverage
make test-unit         # Unit tests only
make test-integration  # Integration tests (modules end-to-end)
make test-functional   # Functional tests (CLI via subprocess)
make test-scenarios    # End-to-end scenario tests
make test-cov          # Tests with coverage report
```

### Build

```bash
make publish       # Clean, build, and check distribution
```

### Project Structure

```text
src/kanon_cli/
  cli.py              # Entry point
  commands/           # Subcommand implementations
  core/               # Core logic (install, clean, kanon parsing, lockfile)
  completions/        # Shell-completion generators
  utils/              # Shared helpers
  repo/               # kanon repo subsystem (manifest sync, PEP 440)
tests/                # Unit and functional tests
docs/                 # Configuration, lifecycle, version resolution docs
pyproject.toml        # Package config (hatchling, entry point: kanon)
```

### Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for commit conventions, PR process,
and how the automated release pipeline works.

### CI/CD Pipeline

This project uses a fully automated SDLC pipeline:

1. **PR Validation** -- Lint, build, test (90% coverage), security scan on
   every PR
2. **Main Branch Validation** -- Full validation + CodeQL on merge to main
3. **Manual QA Approval** -- Human gate before release
4. **Automated Release** -- Semantic versioning from conventional commit
   prefixes, changelog generation, tagging
5. **PyPI Publishing** -- Automated publish via OIDC trusted publishing

PR titles must follow
[Conventional Commits](https://www.conventionalcommits.org/) format
(e.g., `feat: add feature`, `fix: resolve bug`) as they drive automatic
version bumps.

---

## Documentation

- [How It Works](docs/how-it-works.md) -- Technical deep-dive into Kanon
  internals
- [Setup Guide](docs/setup-guide.md) -- Step-by-step setup for new and
  existing projects
- [Configuration](docs/configuration.md) -- `.kanon` format and variable
  expansion
- [Lifecycle](docs/lifecycle.md) -- Install and clean lifecycle step-by-step
- [Multi-Source Guide](docs/multi-source-guide.md) -- Configuring multiple
  manifest sources
- [Version Resolution](docs/version-resolution.md) -- PEP 440 resolver
  details
- [Creating Manifest Repos](docs/creating-manifest-repos.md) -- Authoring
  manifest repositories
- [Creating Packages](docs/creating-packages.md) -- Authoring individual
  package repositories
- [Claude Marketplaces Guide](docs/claude-marketplaces-guide.md) --
  Marketplace architecture and plugin lifecycle
- [Pipeline Integration](docs/pipeline-integration.md) -- Using Kanon tasks
  in CI/CD pipelines
- [Integration Testing](docs/integration-testing.md) -- End-to-end CLI test
  plan
- [kanon repo reference](docs/repo/README.md) -- Manifest format, `.repo/`
  layout, hooks, smart sync, Python support, Windows notes
- [Contributing](CONTRIBUTING.md) -- How to create and maintain Kanon
  packages and marketplaces

---

## License

Apache 2.0. See [LICENSE](LICENSE).
