Section 20.1 takes a registry's name from its directory basename, so the reference tool's default store at ~/.canned-prompts/registry was named `registry` — giving `registry:practice/thing` as a qualified reference. The workplan had recorded this as reading poorly in one output line. It was understated: the name reaches qualified references, the on-disk catalog layout (catalog/registry/...), and index.yaml rows (registry: registry). Once packages are installed and inclusions recorded, changing it becomes a migration rather than an edit. Renaming the directory to ~/.canned-prompts/default fixes it with no special case in code, nothing written into the user's store, and no second naming mechanism competing with section 20.1's manifest. check_legacy_registry refuses to silently create a fresh empty store beside a populated ~/.canned-prompts/registry — that failure would have been worse than the wart being fixed. It names the exact mv, and the --registry flag for keeping the old store. The check fires only for the default path; an explicit --registry is never second-guessed. Tests 95 -> 99. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bjefh8NUiEiahN4JLwoSKM Assistant: claude-code Assistant-Model: opus Assistant-Process: 388925@bnt-lap001 Assistant-Session: 3507023f-e0fd-4a1e-9d90-a0d4217d1502
111 lines
4.7 KiB
Markdown
111 lines
4.7 KiB
Markdown
# 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
|
|
<registry>/<id path>/<version>/...
|
|
```
|
|
|
|
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
|
|
<catalog>/<registry name>/<id path>/<version>/...
|
|
```
|
|
|
|
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 `<registry>:<id>`.
|
|
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 `<registry>:<id>` 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`.
|
|
- 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.
|