akm docs

Migrating from akm 0.5.x to 0.6.0

Also see the archived pre-1.0 plan. The architecture refactor landed mid-0.6 cycle. It removes the openviking source provider, renames the config key stashes[] to sources[] (auto-migrated in memory), adds the writable and defaultWriteTarget fields, and treats Context Hub as a regular git repo. If you are on 0.5.x and upgrading to a current 0.6.x build, read both this file and the archived plan.

This guide covers everything you need to know to upgrade from akm 0.5.x to 0.6.0. Most projects will work without changes thanks to automatic on-disk migrations; a small number of config fields and CLI flags require manual updates.

If you only want a one-line summary of breaking changes, run:

akm help migrate 0.6.0

That command prints the same content you are reading here, sourced directly from CHANGELOG.md and the release notes embedded in this document.

Table of contents

Installing 0.6.0

Pick whichever install method you used for 0.5.x:

# npm
npm install -g akm-cli@0.6.0

# bun
bun install -g akm-cli@0.6.0

# From source (contributors)
git fetch origin && git checkout v0.6.0 && bun install

If you installed via akm upgrade on 0.5.x, you can use it again on 0.6.0 — the command re-runs the same underlying package install:

akm upgrade

The upgrade itself does not touch your stash files, your config.json, or your registry index. Automatic migrations run the first time a 0.6.0 command reads them. See Automatic migrations for the exact list.

What changed in 0.6.0

0.6.0 unifies the runtime domain model around two concepts: stashes (any source of content akm can read) and registries (services that help you discover stashes). Several historical layers — "kits" as a runtime concept, the parallel installed[] config array, the special-cased context-hub provider type — are removed in favour of that simpler model.

First run after upgrade triggers a full index.db rebuild because DB_VERSION was bumped 8 → 9 to add the workflow_documents table. The next akm index repopulates everything; workflow.db (run state) is unaffected.

The high-level changes:

Area 0.5.x 0.6.0
Wire format used by registries kits[] stashes[]
Discovery keyword / topic akm-kit akm-stash
Registry index schema v2 (with kits[]) v3 (with stashes[])
Runtime domain noun "source" / "kit" / "stash" stash (one term)
Provider types filesystem, git, openviking, context-hub (special-cased) filesystem, git, openviking (only)
Stash source kinds (StashSource["type"]) local, npm, github, git, website filesystem, git, npm, github, website, openviking
Lock file stash.lock akm.lock
installed[] in config.json user-facing machine-managed in akm.lock
stashDir top-level field required for primary stash deprecated; use primary: true on a stash entry
disableGlobalStashes boolean replaced by stashInheritance: "merge" | "replace"
--for-agent flag top-level flag deprecated; use --detail=agent
akm enable context-hub hardcoded toggle removed; add as a regular git stash

Each of these is covered in detail below.

Automatic migrations (no action required)

These migrations happen silently on first run of v0.6.0. None of them lose data; all of them are idempotent.

1. stash.lock → akm.lock

If ~/.config/akm/stash.lock (or the equivalent path under AKM_CONFIG_DIR) exists and akm.lock does not, akm renames it on startup. The first command you run in 0.6.0 will perform the rename and then proceed normally.

If both files exist, akm prefers akm.lock and leaves the old stash.lock in place untouched. You can delete stash.lock manually after confirming your config is healthy with akm info.

2. installed[] → stashes[] + akm.lock

Every entry in config.installed[] is mapped to a StashEntry record in config.stashes[], and its lock data (resolved version, cache directory, install timestamp) is written to akm.lock. The installed[] field is removed from config.json the next time akm writes it (for example, after akm config set … or akm add …).

The mapping uses these rules:

installed[] field New location
id stashes[].name (also kept as stashes[].id for compatibility)
source (npm / github / git / local) stashes[].source.type
ref stashes[].source.ref
installRef stashes[].source.ref (resolved form)
installedAt / version / cachePath akm.lock.stashes[<name>]

Until your config.json is rewritten, both representations are read at startup.

3. stashDir → primary: true

The top-level stashDir field is still read for backwards compatibility. At load time, akm synthesizes a primary stash entry equivalent to:

{
  "name": "primary",
  "type": "filesystem",
  "primary": true,
  "source": { "type": "filesystem", "path": "<stashDir>" }
}

No on-disk rewrite happens automatically. The field is rewritten only when you next run akm config set … against any other field.

4. Type alias normalization

Stash entries with type: "context-hub" or type: "github" are loaded as type: "git" in memory. akm config list will show the normalized form. Update your config files to use "git" directly (and the source.type in the discriminated union) to remove the migration step on future runs.

Alias on disk Normalized to
"context-hub" "git"
"github" (as a stash type, not a source locator) "git"

The locator forms github:owner/repo and git+https://… passed to akm add are unaffected — they continue to mean what they always have.

Manual actions required

The following items are not auto-migrated. They are small in scope but must be applied to keep your scripts and project configs working.

1. Replace disableGlobalStashes

If your project .akm/config.json (or user config) contains:

{ "disableGlobalStashes": true }

Replace it with:

{ "stashInheritance": "replace" }

stashInheritance accepts two values:

Value Meaning
"merge" (default) Project stashes are appended after user-level stashes
"replace" Project stashes replace user-level stashes entirely

This is a strict superset of the old behavior — disableGlobalStashes: true maps exactly to stashInheritance: "replace".

2. Replace --for-agent with --detail=agent

In scripts, aliases, or agent prompts that use the --for-agent flag:

# Before
akm search "deployment" --for-agent  # doclint:ignore (pre-0.7.0 flag, this is the "Before" side of the migration)
akm show script:deploy.sh --for-agent  # doclint:ignore (pre-0.7.0 flag, this is the "Before" side of the migration)

# After
akm search "deployment" --detail=agent
akm show script:deploy.sh --detail=agent

--for-agent is still accepted in 0.6.0 but emits a deprecation warning to stderr. It will be removed in 0.7.0.

3. Replace akm enable context-hub / akm disable context-hub

context-hub is no longer a special-cased provider. It is just a git repository like any other. The hardcoded enable/disable toggles are removed.

# Before
akm enable context-hub  # doclint:ignore (pre-0.6.0 command, this is the "Before" side of the migration)
akm disable context-hub  # doclint:ignore (pre-0.6.0 command, this is the "Before" side of the migration)

# After — add it explicitly if you want it
akm add github:andrewyng/context-hub --name context-hub  # doclint:ignore (historical — pre-0.9.0 command spelling)

# To temporarily disable without removing
# Edit your config.json and set "enabled": false on the stash entry

The generalized commands akm registry enable/disable <name> and akm stash enable/disable <name> are now available for toggling any configured registry or stash by name. See CLI Reference for the full syntax.

4. Update akm list parsing in scripts

The single tri-state kind field ("local" | "managed" | "remote") in akm list --format json output is replaced by two orthogonal fields:

Field Values Meaning
origin "local", "remote" Where the content comes from
access "indexed", "live" How akm queries it

The old combinations map cleanly to the new ones:

Old kind New origin New access
"local" "local" "indexed"
"managed" "remote" "indexed"
"remote" "remote" "live"
# Before — checking for managed kits
akm list --format json | jq '.[] | select(.kind == "managed")'  # doclint:ignore (historical — pre-0.9.0 command spelling)

# After — checking for remote indexed stashes
akm list --format json | jq '.[] | select(.origin == "remote" and .access == "indexed")'  # doclint:ignore (historical — pre-0.9.0 command spelling)

The --kind filter on akm list is replaced by --origin and --access. For backward compatibility during 0.6.x, --kind <legacy-value> is still accepted and translated to the new pair.

5. Handle new error codes in automated scripts

Error JSON now includes a machine-readable code field on every failure:

{ "ok": false, "error": "Stash not found: foo", "code": "STASH_NOT_FOUND", "hint": "Run `akm list` to see configured stashes." }

Scripts that previously parsed error messages by string matching should switch to code comparisons. The full code list is documented in the CLI Reference.

Common codes you can rely on:

Code Meaning
STASH_NOT_FOUND Named stash does not exist in config
STASH_DUPLICATE A stash with that name is already configured
REGISTRY_NOT_FOUND Named registry does not exist in config
ASSET_NOT_FOUND akm show <ref> could not resolve the ref
INSTALL_AUDIT_BLOCKED Install was blocked by the security audit
CONFIG_INVALID Config file failed validation
USAGE The CLI was invoked with bad flags or arguments

Wire format change: kits[] → stashes[]

The wire format that registries publish, and that the akm CLI consumes when searching a registry, has been renamed from kits[] to stashes[]. This is a clean break — there is no transparent fallback. akm-cli >= 0.6.0 only parses the v3 schema.

Before (v2 index, parsed by 0.5.x):

{
  "version": 2,
  "updatedAt": "2026-04-01T00:00:00Z",
  "kits": [
    {
      "id": "npm:@scope/my-kit",
      "name": "my-kit",
      "description": "Deployment scripts and skills",
      "ref": "@scope/my-kit",
      "source": "npm",
      "tags": ["deploy"],
      "assetTypes": ["script", "skill"]
    }
  ]
}

After (v3 index, parsed by 0.6.x):

{
  "version": 3,
  "updatedAt": "2026-04-23T00:00:00Z",
  "stashes": [
    {
      "id": "npm:@scope/my-kit",
      "name": "my-kit",
      "description": "Deployment scripts and skills",
      "tags": ["deploy"],
      "assetTypes": ["script", "skill"],
      "stash": {
        "name": "my-kit",
        "type": "npm",
        "source": { "type": "npm", "ref": "@scope/my-kit" }
      }
    }
  ]
}

The new stash property carries a Partial<StashEntry> draft. akm add <registry-hit-id> takes that draft, fills in defaults (notably name if not provided), and writes it directly into your config.stashes[]. This removes the parallel source / ref / installRef fields that previously had to be reassembled by the CLI.

The old source / ref / installRef fields may appear on a v3 entry and are accepted for one version cycle as deprecated read-only fallbacks. They will be removed in v4. Index generators should write only the new stash field going forward.

Registry index schema v3

The schema previously shipped as registry-index.schema.json (removed from the repository since; the highlights below still describe the v3 structure it covered). Highlights:

Migrating a self-hosted registry

If you publish your own registry index, follow these steps:

  1. Bump version to 3. akm 0.6.0 will refuse to load version: 1 or version: 2 indexes and report REGISTRY_INDEX_INCOMPATIBLE.
  2. Rename kits to stashes. A simple JSON rewrite is sufficient.
  3. Add the stash draft to each entry. For an npm package:
    "stash": {
      "name": "my-kit",
      "type": "npm",
      "source": { "type": "npm", "ref": "@scope/my-kit" }
    }
    
    For a GitHub repo:
    "stash": {
      "name": "my-kit",
      "type": "git",
      "source": { "type": "git", "url": "https://github.com/owner/repo", "ref": "main" }
    }
    
  4. Optionally remove the legacy source / ref / installRef fields. They are deprecated in v3 and will be removed in v4. Keeping them does not hurt during the 0.6.x window if you have older clients to support.
  5. Republish. akm 0.6.x clients will pick up the new index on their next refresh (1-hour TTL for static-index providers).

If you generate your index with the akm repository's index builder (scripts/build-registry-index.ts, formerly the akm registry build-index subcommand), upgrading to 0.6.0 is sufficient — the generator emits v3 by default.

For a hand-rolled v1/v2 index, the migration is a small mechanical rewrite: rename the top-level kits array to stashes, set version to 3, and republish. A scripted helper (e.g. jq one-liner) is enough:

jq '{version: 3, updatedAt, stashes: .kits}' old-index.json > new-index.json

Publisher / kit-maker changes

If you publish a kit (npm package or GitHub repo) and want it to be discoverable through registry search, the keyword / topic has changed.

npm packages

Update keywords in your package.json:

 {
   "name": "@you/my-kit",
   "version": "1.0.0",
-  "keywords": ["akm-kit"]
+  "keywords": ["akm-stash"]
 }

For maximum compatibility during the transition, you can include both:

{ "keywords": ["akm-stash", "akm-kit"] }

akm-cli >= 0.6.0 discovers packages tagged with akm-stash. The legacy akm-kit keyword is no longer queried by the CLI and existing registry build pipelines should switch to the new keyword. Older 0.5.x clients that have not upgraded will only see your package if you also keep akm-kit in your keywords list — drop it once your audience has moved.

GitHub repositories

Update the repo topic:

gh repo edit --add-topic akm-stash
gh repo edit --remove-topic akm-kit   # optional, after the transition

Or update topics from the repository About sidebar in the GitHub web UI.

package.json akm.include and friends

The akm.include array and other publish-time package.json fields are unchanged in 0.6.0. Existing kits do not need to be re-published unless you also want to drop the akm-kit keyword.

Sibling repositories

akm ships as three coordinated repositories. All three shipped the 0.6.0 terminology cut together:

If you consume only the official hosted registry, no action is needed — the registry already serves a v3 index from every 0.6.0+ CLI build.

New in 0.6.0 (bonus features)

These are additive — none of them require action to upgrade — but they are worth a scan because they unlock new workflows once you're on 0.6.0.

Internal types and file renames (developers only)

These changes only affect contributors to akm itself or to projects that import from akm's source. akm has no public API (no barrel exports, no exports map in package.json), so importing from akm internals was never officially supported — but we list the renames here for anyone who was doing it anyway.

Type renames

Before After
RegistryKitEntry RegistryStashEntry
InstalledKitEntry StashEntry + StashLockEntry (split)
InstalledKitListEntry StashListEntry
KitInstallResult StashInstallResult
KitInstallStatus StashInstallStatus
KitSource ("npm" | "github" | "git" | "local") StashSource["type"] ("filesystem" | "git" | "npm" | "github" | "website" | "openviking")

StashSource is a discriminated union per type — see src/stash-source.ts.

Domain model unification (#123)

The old triplet of "source" / "kit" / "stash" is collapsed to a single runtime concept: stash. StashEntry is the canonical shape used by config, registry drafts, and the index. kit survives only as a publishing concept (the bundle you ship via npm or GitHub).

The data shape of StashEntry:

type StashEntry = {
  name: string;              // required, unique within config
  type: StashSource["type"]; // discriminator
  primary?: boolean;         // exactly one entry may set this
  enabled?: boolean;         // default true
  source: StashSource;       // discriminated union per type
  options?: Record<string, unknown>;
};

akm.lock records the resolved state separately:

type StashLockEntry = {
  name: string;              // matches StashEntry.name
  resolvedVersion?: string;  // for npm / github / git
  resolvedRef?: string;      // commit SHA / tarball SHA-256
  cachePath?: string;        // absolute path under ~/.cache/akm/
  installedAt: string;       // ISO timestamp
};

File renames

Before After
src/installed-kits.ts src/installed-stashes.ts
src/registry-kit-entry.ts src/registry-stash-entry.ts
docs/kit-makers.md docs/stash-makers.md (with a redirect stub at the old path)
tests/installed-kits.test.ts tests/installed-stashes.test.ts

The redirect stub at docs/kit-makers.md is a one-line link to stash-makers.md so existing inbound links and akm hints snippets continue to resolve.

context-hub removal (#124)

src/providers/context-hub.ts is removed. The old hardcoded provider type is gone; akm enable context-hub and akm disable context-hub now error with STASH_NOT_FOUND and a hint pointing at akm add github:andrewyng/context-hub. context-hub remains usable as an ordinary git stash — it is just a public repo.

If you previously imported ContextHubProvider directly, switch to the generic GitStashProvider from src/providers/git.ts.

Registry-install fold (#125)

The registry-install path is folded into the unified install pipeline in src/install/. registry-install.ts no longer exists as a top-level module. The behaviour is unchanged — registry hits hand off to the same install pipeline used by akm add, and audit/lock/index steps run in the same order.

cli.ts decomposition (#126)

The monolithic src/cli.ts is split into per-command modules under src/cli/. The exported runCli() function and the akm binary behaviour are unchanged; this is purely an internal refactor. Tests that imported helpers from src/cli.ts should now import from the corresponding src/cli/<command>.ts.

Provider / dep refactor (#127)

RegistryProvider and StashProvider interfaces are aligned around a shared BaseProvider shape with explicit lifecycle (init, search, show, dispose). The dependency-injection wiring moves into src/provider-registry.ts. Provider authors should re-read Registry Providers — the public contract is unchanged but the internal hook points have moved.

Config ergonomics (#128)

Embedding and LLM connection config now share a BaseConnectionConfig shape:

type BaseConnectionConfig = {
  endpoint?: string;
  apiKey?: string;
  model?: string;
  headers?: Record<string, string>;
  timeoutMs?: number;
};

Both embedding and llm config keys accept this shape. Existing configs continue to work — the previous fields are a strict subset.

A new output.detail: "agent" preset replaces the --for-agent flag (see Manual actions required §2).

The ASSET_TYPES mutable array export is removed from public API. Use getAssetTypes() from src/asset-spec.ts instead.

Removed without replacement

Removed Reason
akm enable context-hub context-hub is just a git stash; use akm add github:andrewyng/context-hub
akm disable context-hub same
ASSET_TYPES exported array use getAssetTypes() (already existed)
InstalledKitEntry type replaced by StashEntry + StashLockEntry (split)
KitSource = "npm" | "github" | "git" | "local" type replaced by StashSource["type"]
disableGlobalStashes config field replaced by stashInheritance: "merge" | "replace"
installed[] config array now machine-managed in akm.lock
kits[] registry wire format replaced by stashes[] (schema v3)
akm-kit keyword / topic for discovery replaced by akm-stash
context-hub as a stash type normalized to git at load time

Verifying the upgrade

After upgrading, run:

akm info

Look for these signals that the migration completed cleanly:

Then:

akm config list
akm list  # doclint:ignore (historical — pre-0.9.0 command spelling)

akm config list should show your stashes[] populated (including entries that were migrated from the old installed[] array). akm list should show origin and access columns instead of the old kind column.

If akm.lock was created from a previous stash.lock, you should see it at ~/.config/akm/akm.lock (or under AKM_CONFIG_DIR).

A full reindex is not required by the upgrade. Run one only if you also changed your embedding model or want to pick up new asset types:

akm index --full

Post-upgrade checklist

Five-step confirmation that nothing regressed:

akm info --format text                                # 1. version 0.6.x, no context-hub provider
akm config list --format json | head -40              # 2. stashes[] populated; no `installed[]`
akm list                                              # 3. your sources resolve; `origin`/`access` columns  # doclint:ignore (historical — pre-0.9.0 command spelling)
akm workflow list --format json | head -20            # 4. any active workflow runs still show
akm search "<a query you know works>" --format json   # 5. indexed content still matches

If any step returns an error or missing data, see Troubleshooting below.

Troubleshooting

"Unknown stash type 'context-hub'" after upgrade. Legacy aliases normalize at config load, but a truly corrupt entry can still slip through. Run akm config show to see how your entry is parsed. If it still rejects, edit config.json to set "type": "git" and restore the URL from your backup.

registry search returns warnings: ["schema version 2 unsupported"]. Your registry is still serving v2. Pull the latest akm-registry and regenerate, or swap your registry URL to the official hosted one. See Migrating a self-hosted registry.

akm list shows empty stashes[] but your 0.5.x config had entries. The migration copies installed[] into stashes[] only on the first 0.6.0 command that writes config (e.g. akm add, akm config set). If you've only run read-only commands, the new array may not yet be populated. Run akm add <any-stash> or force a rewrite with akm config set stashes "$(akm config get stashes)".

akm workflow create <name> in a fresh stash still errors path escapes the stash. That was fixed in 0.6.0 (#157) — confirm you're on the 0.6.0 CLI with akm info | grep version. If you are and still see it, the symlink resolution is likely hitting an edge case; file an issue with the output of realpath "$AKM_STASH_DIR".

akm.lock was not created. akm only creates the lockfile when it first writes to the stash directory. Run any write command (akm add, akm remember, akm import, akm workflow create) to generate it.

--for-agent still works, but I want to migrate my scripts. Replace it with --detail=agent — same behaviour, preferred spelling. The old flag is retained as a deprecated alias through at least the 0.6.x series.

Rolling back

If you need to roll back to 0.5.x for any reason, the auto-migrations are non-destructive but your config.json may have been rewritten to remove installed[] if you ran any akm config set or akm add command on 0.6.0. Restore from a backup if you need the old shape.

akm.lock is not read by 0.5.x — it will be ignored. Your stash content itself is unchanged across versions; only the metadata layer evolves.

To pin to the last 0.5.x release:

npm install -g akm-cli@0.5
# or
bun install -g akm-cli@0.5

If you find a regression in 0.6.0, please file an issue at https://github.com/itlackey/akm/issues with the output of akm info and a redacted copy of your config.json.