UNPKG

akm-cli

Version:

akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur

135 lines (118 loc) • 7.53 kB
--- type: fact category: convention description: Where to place an asset in the stash — the one path partition axis, chosen by asset type, so refs stay stable and slug search (akm search "<slug>" --type <type>) co-locates related assets. when_to_use: Surfaced to authoring agents when they create or move any asset and must decide its subdirectory/name. updated: 2026-07-28 --- <!-- SOFT guidance only — advice, not a contract. Nothing here is enforced by the proposal gate. It steers WHERE assets go so that path-derived refs stay stable and scoped search (`akm search "<slug>" --type <type>`) keeps working. Tune the axis choices and the domain vocabulary to match how your stash is queried. --> # Stash organization conventions A file's path under its type directory **becomes part of its ref** (`knowledge/auth/oauth-refresh-races.md` → `knowledge/auth/oauth-refresh-races`). That ref is an address other assets cite, and its segments are search terms — `akm search "projectA" --type memory` reconstructs a project's memories. The path is therefore the one facet you **cannot express twice and cannot change without breaking the ref** — so spend it deliberately, on exactly one axis. Retrieval is search, not browse: no one walks these folders at query time. A subdirectory buys you three things: its tokens are indexed as part of the asset's **name** (the highest-weighted search field), they are auto-added to `tags` (even when you set `tags` explicitly), and for scope-born types a slug matching the current repo's name earns an automatic in-project ranking boost. Every other facet belongs in frontmatter, where the index can actually read it. ## Choose the partition axis by asset TYPE, not per-asset judgment Deciding "project or domain?" per asset is non-deterministic — two agents guess differently and bake the wrong guess into an immutable ref. Decide by type: - **Scope-born types → current project / client / team slug.** `memory`, `lesson`, `task`, `env`, `secret`. These are born bound to the work in front of you, so the working context *is* the answer — no judgment needed. When the scope is a single repo, use its repo/package name as the slug (as `git remote`/package.json spell it) — ranking auto-boosts assets whose name or tags match the current repo. Client/team slugs get no such boost; just reuse the existing spelling. - `memories/projectA/auth-token-refresh` - `lessons/clientX/migration-rollback-gotcha` - `secrets/clientX/api-key` - **Reuse-born types → stable domain from a short vocabulary.** `knowledge`, `skill`, `fact`, `script`. These are meant to be reused across projects, so co-locate them by subject — any project retrieves them with `akm search "<domain>" --type <type>`. - `knowledge/auth/oauth-refresh-races` - `skills/testing/flaky-test-triage` - `facts/policies/pii-handling` - **Global-by-nature types → type root or a tool slug.** `command`, `agent`, `workflow`, and stash-wide `env`. Scoping these to a project rarely improves precision and only adds rename risk. `env` defaults to the project/client slug; only an env consumed by every project sits at the type root — if any single project would break when it changes, it is scope-born. **Why reuse types don't take the project axis:** project relevance is already recovered at query time — AKM blends a per-project usage signal into ranking (scoped utility, keyed off the current repo, not the asset's path). Free the scarce path segment for the domain — the *only* handle that co-locates cross-project reuse. ## Placement rules - **One axis only.** Never encode two dimensions in the path (`client/project/subsystem`, or `domain+status`). Second dimensions go in tags. - **Depth 1 by default, 2 max**, and only for strict stable containment (`knowledge/databases/postgres/…`, `secret/clientX/projectA/…`). Never a third semantic level — deeper axes go in frontmatter. - **Segments are lowercase-hyphen semantic tokens** that read as query terms (`connection-pooling`, `tls-handshake-debugging`) — never opaque IDs, numbers, or Johnny.Decimal-style codes, which carry no search signal. - **Never put a volatile facet in the path** — status, date, version, priority, author, `wip`/`done`. Each one changes and forces a ref-breaking rename. They belong in frontmatter tags. - **Flat fallback beats an invented folder.** If no domain fits a reuse-type asset, write it at the type root (`knowledge/http-retry-basics`) rather than coining a one-off domain. An unneeded folder is pure rename liability; propose a vocabulary addition instead of fragmenting the tree. - **Reuse an existing slug before minting a new one.** Before coining a new project/client/domain slug, `akm search` for the existing spelling (`acme` vs `acme-corp`) so the prefix does not fragment. Keep the domain vocabulary in `facts/conventions/domains`. - **Off-axis facets go in `tags:`, not a bare field.** The indexed FTS fields are name, description, tags, hints, and bounded low-weight body content — there is **no `project` field**, so `project: projectA` in frontmatter is invisible to search. Put the off-axis facet in `tags` instead (a project-scoped memory adds `tags: [auth]`; a domain-scoped asset genuinely tied to a project adds `tags: [projectA]` sparingly). - **Directory tokens join `tags` on their own.** The scope/domain segments of the path are always auto-added to `tags` — even when you set `tags` explicitly — so there is no need to restate them; the tag-match ranking boost fires for the scope token either way. Filename tokens are auto-added only when `tags` is empty. ## Renames and evolution - **A ref is chosen once. Default to not renaming.** A rename dangles inbound xrefs silently at write time — nothing catches the breakage until the next `akm lint` run flags the dead frontmatter refs (`missing-ref`) — while the dead ref string keeps scoring in FTS, and a renamed file is a new index entry, so the asset's accumulated usage-ranking history resets. - **A rename is delete plus create, and you do it by hand.** There is no `akm mv` — moving the file strands every inbound ref and gives the destination a fresh identity. If a rename is truly unavoidable: ```sh mv memories/old-note.md memories/new-note.md # update any intentional refs (fully qualified: bundle//memories/old-note) akm index akm lint # confirms nothing dangles ``` A memory's `.derived.md` twin must move with its base. Citing files in read-only sources cannot be fixed at all — another reason not to rename. The asset's accumulated usage-ranking history resets unless you carry it over with `bun scripts/rekey-asset-ref.ts <old-ref> <new-ref>` (a maintainer script, run from a source clone after the move and before `akm index`). - When a project-scoped note turns out to be domain-general, **append, don't promote**: write a new `knowledge/<domain>/…` asset that xrefs the originating memory. Never rename the memory up a rung — that breaks its ref. The atomic note still serves factoid recall; the new synthesis serves reuse; the xref bridges them. ## Real isolation is a separate stash, not a folder A path prefix is a ranking scope, not a security boundary. When you need enforced isolation (client confidentiality, secret-leak containment), mount a **separate stash** and let source resolution keep it apart — don't rely on a subdirectory.