akm-cli 0.10.261019 → 0.10.261021
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/STABILITY.md +0 -8
- package/dist/assets/templates/html/metrics.html +14 -8
- package/dist/assets/ui/overview.js +2 -2
- package/dist/commands/health/llm-usage.js +16 -2
- package/dist/commands/health/plugin-staleness.js +126 -112
- package/dist/commands/health/stash-exposure.js +23 -5
- package/dist/commands/health/type-directory-check.js +17 -49
- package/dist/commands/health.js +45 -15
- package/dist/commands/metrics/collect.js +9 -1
- package/dist/commands/metrics/metrics-cli.js +1 -1
- package/dist/commands/metrics/report-view.js +20 -9
- package/dist/commands/sources/plugin-channels.js +147 -0
- package/dist/commands/sources/plugin-upgrade.js +341 -59
- package/dist/commands/sources/self-update.js +6 -3
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +15 -2
- package/dist/core/common.js +4 -1
- package/dist/indexer/passes/metadata.js +8 -6
- package/dist/indexer/walk/matchers.js +10 -0
- package/dist/output/text/command-format.js +10 -3
- package/dist/scripts/akm-migrate-node.js +69 -40
- package/dist/scripts/akm-migrate.js +69 -40
- package/dist/workflows/exec/param-secrets.js +29 -1
- package/docs/architecture/internals/health-advisories.md +3 -3
- package/docs/architecture/testing/testing-workflow.md +13 -7
- package/docs/reference/cli.md +39 -11
- package/package.json +3 -1
|
@@ -38,10 +38,36 @@ function keyLooksSecret(key) {
|
|
|
38
38
|
const lower = key.toLowerCase();
|
|
39
39
|
return SECRET_KEY_HINTS.some((hint) => lower.includes(hint));
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Unix (`/`, `./`, `../`, `~/`) or Windows (`C:\`, `\\server`) filesystem path,
|
|
43
|
+
* or a bare relative path whose last segment carries a file extension
|
|
44
|
+
* (`reports/2026-10-30/summary.md`). Base64 never contains `.`, so the
|
|
45
|
+
* extension keeps slash-bearing base64 blobs on the whole-string check.
|
|
46
|
+
*/
|
|
47
|
+
const PATH_LIKE = /^(\/|\.{1,2}[\\/]|~[\\/]|[A-Za-z]:[\\/]|\\\\)|^[\w.-]+[\\/][^?#]*\.[A-Za-z0-9]{1,8}$/;
|
|
48
|
+
/** A canonical UUID — an ordinary run/session directory name, not a credential. */
|
|
49
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
50
|
+
/**
|
|
51
|
+
* Whether one path segment or query piece looks like a credential: a known
|
|
52
|
+
* prefix, a secret-named query key (`token=...`), or a long unbroken token
|
|
53
|
+
* (hex/base64 run, or mixed case with digits).
|
|
54
|
+
*/
|
|
55
|
+
function segmentLooksSecret(seg) {
|
|
56
|
+
if (SECRET_VALUE_PREFIX.test(seg))
|
|
57
|
+
return true;
|
|
58
|
+
const eq = seg.indexOf("=");
|
|
59
|
+
if (eq > 0 && keyLooksSecret(seg.slice(0, eq)) && seg.length - eq > 8)
|
|
60
|
+
return true;
|
|
61
|
+
if (seg.length >= 32 && !UUID.test(seg) && /^[A-Za-z0-9+_=-]+$/.test(seg) && /\d/.test(seg) && /[a-zA-Z]/.test(seg))
|
|
62
|
+
return true;
|
|
63
|
+
return seg.length >= 20 && /^[A-Za-z0-9]+$/.test(seg) && /[a-z]/.test(seg) && /[A-Z]/.test(seg) && /\d/.test(seg);
|
|
64
|
+
}
|
|
41
65
|
/**
|
|
42
66
|
* A string value is secret-shaped when it is long, whitespace-free, and either
|
|
43
67
|
* carries a known credential prefix or has high character-class diversity /
|
|
44
|
-
* base64-hex shape — an entropy proxy, not a real entropy computation.
|
|
68
|
+
* base64-hex shape — an entropy proxy, not a real entropy computation. A
|
|
69
|
+
* filesystem path is judged per segment instead, so separators, dates and
|
|
70
|
+
* digits do not count as diversity.
|
|
45
71
|
*/
|
|
46
72
|
function valueLooksSecret(value) {
|
|
47
73
|
const s = value.trim();
|
|
@@ -49,6 +75,8 @@ function valueLooksSecret(value) {
|
|
|
49
75
|
return false;
|
|
50
76
|
if (SECRET_VALUE_PREFIX.test(s))
|
|
51
77
|
return true;
|
|
78
|
+
if (PATH_LIKE.test(s))
|
|
79
|
+
return s.split(/[/\\?&#]/).some(segmentLooksSecret);
|
|
52
80
|
const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((re) => re.test(s)).length;
|
|
53
81
|
if (s.length >= 24 && classes >= 3)
|
|
54
82
|
return true;
|
|
@@ -34,7 +34,7 @@ harness's `--version`, looks up the latest akm release on GitHub for `cli-versio
|
|
|
34
34
|
Claude plugin is installed. With
|
|
35
35
|
`--no-probe` none of these runs: the connection rows carry no `reachable` field and the
|
|
36
36
|
message says reachability was not probed, and `plugin-version` reports `unknown` (its
|
|
37
|
-
`AKM_VERSION_RANGE` check is local and still runs). The `plugin-version` and `cli-version`
|
|
37
|
+
`AKM_MIN_VERSION` (or legacy `AKM_VERSION_RANGE`) check is local and still runs). The `plugin-version` and `cli-version`
|
|
38
38
|
lookups are best-effort and silent on failure; see `src/commands/health/plugin-staleness.ts`
|
|
39
39
|
and `src/commands/health/version-drift.ts`.
|
|
40
40
|
|
|
@@ -56,13 +56,13 @@ and `src/commands/health/version-drift.ts`.
|
|
|
56
56
|
| `cli-version` | `warn`: a newer akm release exists on GitHub. `unknown`: `--no-probe`, or the release lookup failed. | Run `akm upgrade`. |
|
|
57
57
|
| `scheduler-binary` | `warn`: the akm binary recorded in the scheduler reports a different version than the running CLI. `unknown`: `--no-probe`, no scheduled task installed, or the recorded binary cannot be run. | Run `akm task sync` to rebind the schedule. |
|
|
58
58
|
| `index-state-generation` | `warn`: usage links in state.db point at index entries that no longer match (an index and state.db from different generations). | Stop concurrent writers and run `akm index --full`. |
|
|
59
|
-
| `stash-git-exposure` | `warn
|
|
59
|
+
| `stash-git-exposure` | `warn`, one per bundle: `env/` or `secrets/` files are tracked by git in a configured bundle (the working stash included) and that bundle has a remote. Skipped for a bundle with no `.git` at its root. | Run `git rm --cached` on the listed files and add `env/` and `secrets/` to `.gitignore`. A `.gitignore` rule alone does not untrack files. To keep tracking them on purpose, add `# akm: intentionally track env and secrets` to the stash `.gitignore`; the advisory is then not reported. |
|
|
60
60
|
| `binary-config-skew` | `warn`: `config.json` has a `configVersion` newer than this binary knows. | Upgrade this install. |
|
|
61
61
|
| `egress-endpoints` | Every remote destination in the effective config, as one informational entry: enabled registries, git and website bundles, each connection's endpoint and the embedding endpoint (credentials in a URL are masked). Silent when nothing remote is configured. | Eyeball it against what you expect; a destination you did not configure came from an `extends` base or a shared bundle. |
|
|
62
62
|
| `type-directory-disagreement` | `warn`: an indexed asset's resolved type differs from the type its directory declares. Each line gives the file, both types, and the classifier signal that won (`winner`). | Review the listed files. Intended overrides (for example a `knowledge/` file containing `$ARGUMENTS`) are marked `known-good override`. |
|
|
63
63
|
| `memory-cleanup-archive` | `pass` with size and file count when the working bundle has `.akm/memory-cleanup/archive` content and no git history (the purge does not run there). `warn` when a git-backed bundle has archived files the purge cannot remove (untracked, modified, or unverifiable in git). | Commit the archived files so the purge can remove them once they age out. |
|
|
64
64
|
| `data-dir-usage` | `warn`: the data directory is over 1 GB and either more than 3 times the size of `state.db`, `index.db`, and `logs.db` (with their WAL and SHM files), or one top-level subdirectory holds more than half of it. The message names that subdirectory and its size. | Inspect the named subdirectory. `backups/tasks/` holds task-file snapshots written by `akm migrate apply`. |
|
|
65
|
-
| `plugin-version` | One row per
|
|
65
|
+
| `plugin-version` | One row per native `claude plugin list --json` AKM installation, with scope, enabled state and install path; cached versions are never used to choose an installation. `AKM_MIN_VERSION` takes precedence over legacy `AKM_VERSION_RANGE`. Disabled and other-project installations are inert. Missing metadata or multiple applicable paths/versions produce `unknown`, since the native list does not identify which conflicting installation a running session loaded. The newest version is read on the channel the marketplace clone follows: on the remote's default branch the newest stable tag, on any other branch (such as `next`) the release tag on that branch's head, and on a clone pinned to a tag that tag. The newest-tag lookup is `--probe`-gated: with `--no-probe` the row is `unknown` ("not probed") and only the local range check still runs. `warn`: the plugin is stale, or its range does not admit the running CLI. | Run the `claude plugin update <ref>` command in the message. |
|
|
66
66
|
| `opencode-plugin-version` | The same check for the OpenCode plugin's own bundled `akm-cli` (read from its `package.json`, never executed): it runs in-process and shares the host's databases, so its lag matters the same way. OpenCode 1 caches each spec in its own folder, so it reads the one folder of the spec the global OpenCode config names under `plugin` (`akm-opencode`, read as `akm-opencode@latest`, or `akm-opencode@next`), and for `akm-opencode@<version>` reports the plugin as pinned with no staleness verdict; with no entry, or no folder for it, it reads `packages/akm-opencode`, and it never scans the other cached specs. It reads local files only and is never `--probe`-gated. `warn`: the bundled version differs from the running CLI. Omitted when the OpenCode plugin is not installed. | Update the OpenCode plugin. |
|
|
67
67
|
| `state-db-readable` | `fail`: `state.db` cannot be read, or is missing in a read-only run (which does not create it). Every other check is skipped, because none of them can read it. | Check the owner and mode of the data directory (or point `AKM_DATA_DIR` / `XDG_DATA_HOME` at one you own); run any akm command that records state, such as `akm health`, to create it. |
|
|
68
68
|
|
|
@@ -439,18 +439,24 @@ orchestration, checksum, and failure paths.
|
|
|
439
439
|
|
|
440
440
|
`tests/integration/upgrade-rehearsal/` runs once per origin in
|
|
441
441
|
`KNOWN_UPGRADE_ORIGINS` (`previous-release.ts`): `previous`, the highest stable
|
|
442
|
-
release below the candidate's `package.json` version
|
|
443
|
-
|
|
442
|
+
release below the candidate's `package.json` version; `0.9.32`, which keeps the
|
|
443
|
+
full engines migration covered after `previous` advances to 0.10; and `0.9.15`,
|
|
444
|
+
which covers scheduler rows installed before host-local activation was recorded.
|
|
445
|
+
Each origin installs that release as a real global npm
|
|
444
446
|
package (a real `npm pack` / `npm install --global` into a throwaway prefix, see
|
|
445
|
-
`scripts/package-install.ts`)
|
|
447
|
+
`scripts/package-install.ts`). The `previous` and `0.9.32` origins build a full
|
|
448
|
+
home — filesystem,
|
|
446
449
|
git (with an in-bundle symlink at its root), website, and npm bundles, scheduled
|
|
447
|
-
tasks, a synced native (fake) crontab, and
|
|
448
|
-
itself writes
|
|
449
|
-
|
|
450
|
+
tasks, a synced native (fake) crontab, and model configuration the release
|
|
451
|
+
itself writes: native connections/harnesses on 0.10+, or the full 0.9 `engines`
|
|
452
|
+
form (an llm engine with an `apiKeyFile`, agent engines, a `models.json`, and a
|
|
453
|
+
command and task that name an engine). The `0.9.15` origin uses a smaller home
|
|
454
|
+
with one directly installed scheduled task. Each then installs
|
|
450
455
|
the CANDIDATE build OVER it in the SAME prefix: an in-place swap, the same thing
|
|
451
456
|
a real `npm i -g akm-cli@…` or `bun add -g` upgrade does. It then runs the
|
|
452
457
|
candidate against that home (`migrate status` / `apply`, which converts the
|
|
453
|
-
`engines` form, files its key and rewrites the `engine:` keys,
|
|
458
|
+
`engines` form, files its key and rewrites the `engine:` keys, or preserves
|
|
459
|
+
native connections/harnesses, `bundle list`,
|
|
454
460
|
`search` / `show`, `task sync` dry-run and real — plain, no `--rebind`, as an
|
|
455
461
|
upgrading user runs it — executing the generated cron command and confirming it
|
|
456
462
|
ran the candidate, `task run`, `health`, `improve --dry-run`), and finally runs a
|
package/docs/reference/cli.md
CHANGED
|
@@ -541,11 +541,11 @@ Result sections (`schemaVersion: 1`):
|
|
|
541
541
|
| Field | Description |
|
|
542
542
|
| --- | --- |
|
|
543
543
|
| `window`, `filters` | The resolved window and the usage source counted |
|
|
544
|
-
| `usage` | Search, show, curate and select totals, `
|
|
544
|
+
| `usage` | Search, show, curate and select totals, `searchesWithHits` and `selectsPerSearch` (selects divided by searches that returned a hit: a plain ratio that can exceed 1, not a conversion percentage, since selects carry no source or search id), `searchMedianMs`, a daily series, top assets, top and zero-result queries, and rows by source |
|
|
545
545
|
| `feedback` | Positive and negative totals, per-asset valence, per-tag counts, and the most recent negatives with their reasons |
|
|
546
546
|
| `utility` | A ten-bucket histogram of `utility_scores`, the lowest and highest assets, and how many indexed entries were never used |
|
|
547
547
|
| `outcomes` | The assets with the lowest `outcome_score` |
|
|
548
|
-
| `llm` | `akm health`'s LLM usage aggregate (`byStage`, `byProcess`, and `byEngine`, keyed by the name of the connection or harness) |
|
|
548
|
+
| `llm` | `akm health`'s LLM usage aggregate (`byStage`, `byProcess`, and `byEngine`, keyed by the name of the connection or harness). `usageCalls` and `reasoningCalls` count the calls whose provider response reported token usage; a call without it adds no tokens and the report shows `unknown`, not zero, when none reported |
|
|
549
549
|
| `index`, `tasks`, `proposals`, `workflows` | Index runs and median time, task runs and fail rate, proposals by status and accept rate by source, workflow runs and tokens by model |
|
|
550
550
|
| `rows` | The raw usage and LLM rows of the window; see below |
|
|
551
551
|
| `notes` | Anything that limits what the numbers mean |
|
|
@@ -1537,7 +1537,9 @@ computed, with a 256 MiB binary limit. Release/checksum metadata is capped at
|
|
|
1537
1537
|
akm upgrade # Install a newer release if there is one, run every pending migration, then update the harness plugins
|
|
1538
1538
|
akm upgrade --check # Report pending updates, CLI and plugins, without changing anything (no migration step)
|
|
1539
1539
|
akm upgrade --force # Force the install even if already on latest
|
|
1540
|
-
akm upgrade --next #
|
|
1540
|
+
akm upgrade --next # Include prereleases when selecting the newest CLI release
|
|
1541
|
+
akm upgrade --channel next # Switch CLI and installed harness plugins to next
|
|
1542
|
+
akm upgrade --channel latest # Return CLI and installed harness plugins to stable
|
|
1541
1543
|
```
|
|
1542
1544
|
|
|
1543
1545
|
| Flag | Description |
|
|
@@ -1545,7 +1547,9 @@ akm upgrade --next # Install the newest prerelease (@next) instead of the
|
|
|
1545
1547
|
| `--check` | Report pending updates (CLI and per-harness plugins) without changing anything |
|
|
1546
1548
|
| `--force` | Force upgrade even if on latest version |
|
|
1547
1549
|
| `--skip-post-upgrade` | Skip the post-upgrade index rebuild |
|
|
1548
|
-
| `--next` |
|
|
1550
|
+
| `--channel latest\|next` | Switch the CLI and installed Claude, Codex and OpenCode plugins together; works with `--check` |
|
|
1551
|
+
| `--allow-downgrade` | Permit an older target version during an explicit `--channel` switch |
|
|
1552
|
+
| `--next` | Include prereleases when selecting the newest CLI release; retain configured harness channels (see below). Works with `--check`, `--force` and `-q` |
|
|
1549
1553
|
|
|
1550
1554
|
Offline, or to migrate without a release check, run `akm migrate apply`
|
|
1551
1555
|
directly: it is the same step.
|
|
@@ -1607,7 +1611,7 @@ target is the akm-cli that `akm-opencode@latest` depends on
|
|
|
1607
1611
|
The result then carries `lockstep: { plugin, pinnedVersion, newestVersion, heldBack }`,
|
|
1608
1612
|
`latestVersion` is the pinned version, the install names that exact version
|
|
1609
1613
|
rather than `@latest`, and the text output says the CLI is held back. The CLI
|
|
1610
|
-
is never moved backwards to meet the pin
|
|
1614
|
+
is never moved backwards to meet the pin unless an explicit channel switch also names `--allow-downgrade`. With both OpenCode plugins present
|
|
1611
1615
|
(`akm-opencode` and `akm-opencode-v2`), the CLI is held to the older of their
|
|
1612
1616
|
two pins, and `lockstep.plugin` names the one that sets it. Claude Code and Codex have no
|
|
1613
1617
|
in-process copy and are not part of this rule.
|
|
@@ -1623,7 +1627,7 @@ retries.
|
|
|
1623
1627
|
#### Prereleases (`--next`)
|
|
1624
1628
|
|
|
1625
1629
|
`akm upgrade --next` means "newest available, prereleases included". The result
|
|
1626
|
-
carries `channel: "next"` (`"latest"` otherwise)
|
|
1630
|
+
carries `channel: "next"` (`"latest"` otherwise), plus the CLI's inferred `currentChannel` and each installed plugin's configured `channel` and `channelMismatch`.
|
|
1627
1631
|
|
|
1628
1632
|
- **CLI.** The target is the `next` dist-tag of `akm-cli` (read from the npm
|
|
1629
1633
|
registry, so it works for binary installs too) when that is newer than the
|
|
@@ -1656,14 +1660,38 @@ carries `channel: "next"` (`"latest"` otherwise); no other field changes.
|
|
|
1656
1660
|
- **Claude Code and Codex.** Their plugin comes from the `akm-plugins` git
|
|
1657
1661
|
marketplace, and the channel is the ref the marketplace follows: the default
|
|
1658
1662
|
branch is stable, the `next` branch carries prereleases. `akm upgrade` updates
|
|
1659
|
-
whatever ref the marketplace follows
|
|
1660
|
-
`--next
|
|
1661
|
-
|
|
1662
|
-
`settings.json`), or run `codex plugin marketplace add itlackey/akm-plugins --ref next`
|
|
1663
|
-
in Codex. Under `--next` their entries carry a note naming the ref followed.
|
|
1663
|
+
whatever ref the marketplace follows. `--next` alone leaves that ref unchanged;
|
|
1664
|
+
`--channel next` changes it. Under `--next` their entries carry a note naming
|
|
1665
|
+
the ref followed.
|
|
1664
1666
|
The plugins need akm 0.10.261001 or newer; any newer akm passes.
|
|
1665
1667
|
- `--check --next` reports all of this and changes nothing.
|
|
1666
1668
|
|
|
1669
|
+
#### Switching channels together (`--channel`)
|
|
1670
|
+
|
|
1671
|
+
`akm upgrade --channel next` selects the actual npm `next` CLI tag and changes
|
|
1672
|
+
installed harness plugins to their `next` branch or package tag.
|
|
1673
|
+
`--channel latest` selects stable and removes those prerelease refs. An explicit
|
|
1674
|
+
switch may replace a pinned plugin spec. The command checks target CLI and plugin
|
|
1675
|
+
versions first; if any would go backwards, repeat with `--allow-downgrade`.
|
|
1676
|
+
A failed CLI install or migration stops the plugin switch.
|
|
1677
|
+
|
|
1678
|
+
Claude and Codex retain their marketplace registrations, plugin enablement,
|
|
1679
|
+
`pluginConfigs`, and unrelated settings. akm edits only the marketplace ref,
|
|
1680
|
+
then uses the native updater. Claude's installed user, project and local scopes
|
|
1681
|
+
are updated explicitly. OpenCode changes only the installed plugin's spec,
|
|
1682
|
+
retaining tuple options and JSONC comments, then refreshes the desired cache.
|
|
1683
|
+
Every changed config file has a sibling `.akm-channel-*.bak` backup, whose path
|
|
1684
|
+
appears in that harness's `backups` result. Restart the harness to load updates.
|
|
1685
|
+
|
|
1686
|
+
`--check --channel next` shows `channel`, `targetChannel` and mismatches without
|
|
1687
|
+
writing settings or fetching Git marketplaces. The CLI's `currentChannel` is
|
|
1688
|
+
inferred from its version: prerelease means `next`, otherwise `latest`.
|
|
1689
|
+
A configured channel is distinct from a successful refresh: native update
|
|
1690
|
+
failures retain the configured target and backup paths but report `failed`.
|
|
1691
|
+
An installed plugin that cannot switch, including OpenCode awaiting shutdown,
|
|
1692
|
+
makes an explicit switch exit `1`; retry after resolving its reported cause.
|
|
1693
|
+
The switch is coordinated, not a transaction across independent harnesses.
|
|
1694
|
+
|
|
1667
1695
|
#### Containers
|
|
1668
1696
|
|
|
1669
1697
|
With the plugin step, a container entrypoint needs only:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.261021",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "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 Cursor.",
|
|
6
6
|
"keywords": [
|
|
@@ -119,9 +119,11 @@
|
|
|
119
119
|
"citty": "^0.2.2",
|
|
120
120
|
"dotenv": "^17.4.2",
|
|
121
121
|
"fast-xml-parser": "^5.10.1",
|
|
122
|
+
"jsonc-parser": "^3.3.1",
|
|
122
123
|
"marked": "^18.1.1",
|
|
123
124
|
"node-html-parser": "^9.0.1",
|
|
124
125
|
"semver": "^7.6.0",
|
|
126
|
+
"smol-toml": "^1.9.1",
|
|
125
127
|
"turndown": "^7.2.4",
|
|
126
128
|
"yaml": "^2.8.4",
|
|
127
129
|
"zod": "^4.6.6"
|