# canned-prompts reference CLI This is intentionally a **small reference implementation**, not the intended final architecture. It demonstrates nine verbs: ```text add PATH search QUERY show ID resolve ID --set key=value render ID --set key=value eval ID index [ID] install ID [--version VERSION] publish PATH ``` The implementation uses a local catalog plus a filesystem registry and performs no model calls. `resolve` and `render` are separate because the specification separates them (§ 5.1): resolution decides each value and may be non-deterministic, rendering substitutes and always is. `resolve` prints where every value came from — supplied, default, or fallback — before any prompt is produced. ## Stores Default locations: ```text ~/.canned-prompts/catalog ~/.canned-prompts/default ``` A **registry** stores packages flat, because an id is unambiguous within one registry: ```text ///... ``` A **catalog** is namespaced by registry, because identity is registry-scoped (§ 3.2) and the same id may be installed from more than one place: ```text ////... ``` For example: ```text ~/.canned-prompts/catalog/default/practice/pqrst-estimate/1.0.0/ ~/.canned-prompts/catalog/local/practice/pqrst-estimate/1.0.0/ ``` A registry's name comes from its optional `registry.yaml`, and otherwise from its directory basename — which is why the default registry directory is called `default` and not `registry`: the basename reaches qualified references, catalog paths and index rows, and `registry:practice/thing` reads poorly. A store left at the old `~/.canned-prompts/registry` is reported with the command to move it, rather than a fresh empty one being created beside it. `add` takes a package from a path rather than a registry, so it files it under `local` (override with `--as`). Commands that take an ID accept a bare id or a qualified `:`. A bare id installed from more than one registry is reported as ambiguous rather than resolved by guessing. ## Design choices - YAML manifest via PyYAML. - `{{ name }}` template substitution only. - No arbitrary expression/code execution. - Published versions are immutable by default. - `add`, `publish` and `install` copy only reserved paths and manifest- referenced files. A working directory's `.git`, virtualenv or scratch files never ship; whatever is left out is named on stderr. - Version precedence follows SemVer: a prerelease ranks below its release, and `any`, `newest` and `>=` skip prereleases entirely. Only an exact pin selects one. - `install` copies from registry to catalog. - `add` copies a package directly to catalog. - `search`, `show`, `resolve`, and `render` operate on catalog packages, and print qualified `:` references. - `include` defaults are satisfied (inclusion is deterministic); `derive` defaults are reported, not run. Inclusion cycles are detected and named. - `resolve`, `render` and `eval` warn when an included value will render more than once — two inputs including the same thing, or an included package inheriting an outer input of the same name. Nothing is deduplicated; the fix is a better factoring. - `eval` runs the deterministic render checks of any eval declaring the `canned-prompts/eval-rubric/v0.1` schema, and reports output criteria as declared but not run. Unrecognized schemas are skipped, not rejected. A failed render check exits non-zero. - `resolve` lists required capabilities and context dependencies, which this tool cannot verify, rather than implying it checked them. - `add` and `install` name declared prompt dependencies the catalog cannot satisfy. They do not fetch them — resolution is the consumer's job (§ 10) — but a package that looks installed and cannot render should say so. - `add`, `install` and `publish` record an entry in the store's `index.yaml`: source, method, first-inclusion date, and the package's declared author, source and licence. `index` lists it. Re-adding keeps the original `included_at` and updates `last_seen_at`. - `--registry` accepts an `http(s)://` URL as well as a path. HTTP uses the stdlib, so this stays dependency-light. `CANNED_PROMPTS_PUBLISH_TOKEN` supplies the bearer token when publishing. - Publishing to an HTTP registry needs `--as NAME`: a URL addresses a service that **hosts several registries**, so unlike a directory it does not name one (§ 20.4). Installing names it in a qualified reference. - An optional `registry.yaml` names a registry and records namespace claims. `publish` warns when a namespace is declared `closed` — it cannot authenticate a publisher, and says so rather than implying it checked. - Static input defaults are applied; **derived** defaults (§ 6.1) are not. This tool never calls a model, so a derived default is satisfied only by its static fallback `value`. Without one, `resolve` reports the input as unresolved and `render` refuses rather than substituting empty text. Use this implementation to challenge the format. Replace it once real usage reveals the right architecture.