akm-cli 0.10.261015-beta → 0.10.261016-beta

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/STABILITY.md +2 -1
  3. package/dist/assets/hints/cli-hints-full.md +6 -4
  4. package/dist/assets/hints/cli-hints-short.md +4 -2
  5. package/dist/assets/opencode/akm-plugin.js +16 -8
  6. package/dist/assets/prompts/workflow-unit-preamble.md +3 -1
  7. package/dist/commands/health/plugin-staleness.js +86 -22
  8. package/dist/commands/read/search-cli.js +2 -2
  9. package/dist/commands/read/show.js +15 -3
  10. package/dist/core/asset/markdown.js +9 -0
  11. package/dist/core/config/config-walker.js +35 -32
  12. package/dist/core/config/schema/connections.js +6 -3
  13. package/dist/core/config/schema/harnesses.js +8 -1
  14. package/dist/core/config/schema/primitives.js +12 -1
  15. package/dist/core/config/schema/sources-bundles.js +2 -2
  16. package/dist/core/npm-package.js +48 -13
  17. package/dist/output/renderers.js +2 -2
  18. package/dist/output/shapes/helpers.js +2 -0
  19. package/dist/output/text/show-format.js +7 -0
  20. package/dist/registry/semver.js +12 -0
  21. package/dist/scripts/akm-migrate-node.js +21629 -5738
  22. package/dist/scripts/akm-migrate.js +21632 -5741
  23. package/dist/tasks/backends/cron.js +19 -0
  24. package/docs/architecture/internals/health-advisories.md +1 -1
  25. package/docs/architecture/specs/ref.md +4 -3
  26. package/docs/architecture/testing/testing-workflow.md +7 -8
  27. package/docs/guides/discover-and-load.md +12 -4
  28. package/docs/guides/getting-started.md +3 -2
  29. package/docs/guides/use-with-any-agent.md +4 -1
  30. package/docs/maintainers/release-checklist.md +15 -8
  31. package/docs/reference/cli.md +22 -3
  32. package/package.json +4 -4
  33. package/schemas/akm-config.json +535 -302
@@ -438,6 +438,15 @@ function readCrontab(exec) {
438
438
  const stderr = (result.stderr ?? "").trim();
439
439
  if (((result.stdout ?? "").trim() === "" && stderr === "") || /no crontab/i.test(stderr))
440
440
  return "";
441
+ // `crontab` reads the spool through its setgid `crontab` group (setuid root on some distributions). A process
442
+ // started with no-new-privileges (an agent sandbox, a container, systemd `NoNewPrivileges=`) runs it without that
443
+ // privilege, and the spool refuses the read. The crontab itself is fine, so say so instead of passing on a bare
444
+ // `fopen` error. Without no-new-privileges a refusal has another cause and is reported as what it said.
445
+ if (result.noNewPrivileges && /permission denied/i.test(stderr)) {
446
+ throw new ConfigError("crontab -l could not read the crontab spool (permission denied). `crontab` reads it through its setgid " +
447
+ "(or setuid) bit, which this process, started with no-new-privileges (an agent sandbox, a container, " +
448
+ "systemd NoNewPrivileges=), does not grant; run this from a normal shell to see the installed tasks.", "INVALID_CONFIG_FILE", "Run the command from a normal login shell or terminal, not from a sandboxed process.");
449
+ }
441
450
  throw new ConfigError(`crontab -l failed (exit ${result.status}): ${result.stderr || result.stdout || "no output"}.`, "INVALID_CONFIG_FILE", "The `crontab` binary ran but did not report success; check its output above.");
442
451
  }
443
452
  function writeCrontab(exec, content) {
@@ -463,6 +472,15 @@ function replaceCrontab(exec, existing, next) {
463
472
  throw err;
464
473
  }
465
474
  }
475
+ /** Whether this process has Linux's no-new-privileges flag, which every child (`crontab` included) inherits. */
476
+ function hasNoNewPrivileges() {
477
+ try {
478
+ return /^NoNewPrivs:\s*1$/m.test(fs.readFileSync("/proc/self/status", "utf8"));
479
+ }
480
+ catch {
481
+ return false;
482
+ }
483
+ }
466
484
  function defaultCronExec() {
467
485
  const run = (args, input) => {
468
486
  const r = spawnSync("crontab", args, { encoding: "utf8", ...(input !== undefined ? { input } : {}) });
@@ -471,6 +489,7 @@ function defaultCronExec() {
471
489
  stdout: r.stdout ?? "",
472
490
  stderr: r.stderr ?? "",
473
491
  enoent: r.error?.code === "ENOENT",
492
+ noNewPrivileges: r.status !== 0 && hasNoNewPrivileges(),
474
493
  };
475
494
  };
476
495
  return {
@@ -37,7 +37,7 @@ changes.
37
37
  | `index-state-generation` | Durable usage links in `state.db` disagree with the current searchable index generation, which an interrupted cross-database update leaves behind. | Stop concurrent writers, then run `akm index --full` to relink state to the current index. |
38
38
  | `cli-version` | (#950) Compares the installed akm-cli version against the latest GitHub release (the same source `akm upgrade` trusts). `--probe`-gated: `unknown` "not probed" with `--no-probe`; offline/rate-limited also degrades to `unknown`, never a false `warn`. | Run `akm upgrade` when a newer release is reported. An `unknown` result carries no staleness claim either way — check manually if it matters. |
39
39
  | `scheduler-binary` | (#953) Reads the scheduler's recorded akm invocation for the installed tasks (the same binding `task sync`/`task doctor` already read — no crontab/launchd/schtasks text parsing) and runs it with `--version`, compared against the running CLI's version. `--probe`-gated: `unknown` "not probed" with `--no-probe`. `unknown` when no scheduled task is installed or the recorded binary cannot be executed. | A `warn` names both versions — upgrading akm through a different installer than the one active at the last `task sync` left the schedule pointed at the old binary; run `akm task sync` to rebind it. |
40
- | `plugin-version` / `opencode-plugin-version` | (itlackey/akm#832,) `plugin-version` reports each installed Claude Code harness plugin's version and its declared `AKM_VERSION_RANGE` admission of the running CLI. `opencode-plugin-version` does the same 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. `plugin-version`'s newest-tag lookup is `--probe`-gated: with `--no-probe` its row is `unknown` ("not probed") and only the local range check still runs. `opencode-plugin-version` reads local files only and is never gated. | A `warn` names the stale/out-of-range plugin and the update command (`claude plugin update <ref>` for the Claude plugin; update the OpenCode plugin for the bundled copy). |
40
+ | `plugin-version` / `opencode-plugin-version` | (itlackey/akm#832,) `plugin-version` reports each installed Claude Code harness plugin's version and its declared `AKM_VERSION_RANGE` admission of the running CLI. `opencode-plugin-version` does the same 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 `opencode-plugin-version` 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. 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. `plugin-version`'s newest-tag lookup is `--probe`-gated: with `--no-probe` its row is `unknown` ("not probed") and only the local range check still runs. `opencode-plugin-version` reads local files only and is never gated. | A `warn` names the stale/out-of-range plugin and the update command (`claude plugin update <ref>` for the Claude plugin; update the OpenCode plugin for the bundled copy). |
41
41
 
42
42
  ## Connections, harnesses and the work on them
43
43
 
@@ -132,8 +132,9 @@ runs collapsed to `-`, trimmed) and match the fragment against those slugs;
132
132
  fall back to a case-insensitive match against raw heading text. The selected
133
133
  region runs from that heading to the next heading of the same or higher level.
134
134
  A fragment that matches no heading is an error that **lists the available
135
- fragment slugs** — that error is the discovery mechanism, replacing the removed
136
- `toc` view mode.
135
+ fragment slugs**. The outline itself is `akm show <ref> --detail brief`, whose
136
+ `toc` lists every heading with the slug that selects it; it replaces the
137
+ removed `toc` view mode.
137
138
 
138
139
  **Every other item kind — adapter-owned selector.** When one item exposes
139
140
  multiple exports the adapter MAY append a stable fragment
@@ -157,7 +158,7 @@ is removed. It was a second argv parser (`normalizeShowArgv` rewrote
157
158
  | --- | --- |
158
159
  | `akm show X section "Auth"` | `akm show X#auth` |
159
160
  | `akm show X full` | `akm show X` (no fragment = whole item) |
160
- | `akm show X toc` | `akm show X#<unmatched>` lists available fragment slugs |
161
+ | `akm show X toc` | `akm show X --detail brief` (its `toc` field) |
161
162
  | `akm show X lines 10 30` | removed — every response carries `path`; slice the file |
162
163
  | `akm show X frontmatter` | removed — if a raw-YAML projection proves necessary it returns as a `--shape` value, the designated projection axis |
163
164
 
@@ -406,9 +406,9 @@ For a local release gate without Docker, use:
406
406
  ./tests/release-check.sh --skip-docker
407
407
  ```
408
408
 
409
- That script checks the packaged artifact: bin targets, the migrator, package
410
- acceptance, and the upgrade rehearsal against the packed tarball. Lint, the
411
- typecheck and the test suites are `bun run check`.
409
+ That script checks the packaged artifact: bin targets, the migrator and package
410
+ acceptance. Lint, the typecheck and the test suites are `bun run check`; the
411
+ upgrade rehearsal runs in CI.
412
412
 
413
413
  The Release workflow runs Gated CI (real embeddings, Docker, and the
414
414
  Linux/macOS/Windows schedulers) on the commit it publishes and requires CI to
@@ -521,17 +521,16 @@ throws naming the fix instead. Env overrides:
521
521
  resolving the highest published stable version below the candidate's
522
522
  `package.json` version.
523
523
  - `AKM_CANDIDATE_TARBALL=<path>` — use an already-packed candidate tarball
524
- (`tests/release-check.sh` passes `$PACKAGE_CANDIDATE`) instead of running
525
- `npm pack` on the working tree (which requires `dist/cli.js` — run
526
- `bun run build` first).
524
+ instead of running `npm pack` on the working tree (which requires
525
+ `dist/cli.js` — run `bun run build` first).
527
526
 
528
527
  Fetched previous-release tarballs are cached under
529
528
  `${TMPDIR:-/tmp}/akm-upgrade-rehearsal/previous-release/<version>/` so repeat
530
529
  runs do not re-hit the network. It is wired into CI as the `upgrade-rehearsal`
531
530
  job (`.github/workflows/ci.yml`), which runs on pushes to `main` and release
532
531
  branches, on manual runs and on every commit of a pull request that is ready
533
- for review (a draft pull request skips it until it is marked ready), and into
534
- `tests/release-check.sh` right after packing the release candidate.
532
+ for review (a draft pull request skips it until it is marked ready). The Release
533
+ workflow requires that CI run to pass on the commit it publishes.
535
534
 
536
535
  ## Coverage Gap Guide
537
536
 
@@ -73,14 +73,21 @@ lookup to one installed bundle when a conceptId is ambiguous across bundles.
73
73
 
74
74
  ## 4. Load a document section
75
75
 
76
- Knowledge docs and other markdown assets can be large. Append `#fragment` to
77
- a ref to return one section by heading slug instead of the whole document:
76
+ Knowledge docs and other markdown assets can be large. Read the outline first,
77
+ then only the sections you need. `--detail brief` returns the document's `toc`
78
+ without the body, and appending `#<slug>` to a ref returns one section:
78
79
 
79
80
  ```sh
81
+ akm show knowledge/api-guide --detail brief
82
+ # → {"toc": [{"level": 2, "text": "Authentication", "slug": "authentication", "startLine": 12}, ...], ...}
80
83
  akm show knowledge/api-guide#authentication
81
84
  # → just the "Authentication" section, not the full document
82
85
  ```
83
86
 
87
+ Every `slug` in `toc` selects its section, including repeated headings, which
88
+ get `-1`, `-2`, ... suffixes. A whole-document show carries the same `toc`
89
+ next to the body.
90
+
84
91
  Search can also return an opaque `#akm-fragment-...` selector for a bounded
85
92
  piece of a long document. The exact selector remains the default. For memories
86
93
  whose opening context helps interpret a later matching turn, opt into bounded
@@ -96,8 +103,9 @@ reports `contextTruncated` in JSON. The response also distinguishes canonical
96
103
  `parentRef` from `selectedRef` and includes fragment position, line bounds,
97
104
  neighbors, and separate fragment/parent size estimates.
98
105
 
99
- An unmatched fragment lists the available slugs instead of erroring, so a
100
- guess that misses still tells you what to try next.
106
+ An unmatched fragment is an error that lists the available slugs, so a guess
107
+ that misses still tells you what to try next; `--detail brief` is the way to
108
+ see them without guessing.
101
109
 
102
110
  ## 5. Record the outcome
103
111
 
@@ -198,8 +198,9 @@ akm show <ref> # e.g. akm show workflows/deploy
198
198
 
199
199
  **Success check:** structured JSON with everything an agent needs to use the
200
200
  asset — a `run` command plus optional `cwd` and `setup` for scripts, a
201
- `prompt` payload for agents, or the document `content` for knowledge (append
202
- `#<heading-slug>` to a ref for one section).
201
+ `prompt` payload for agents, or the document `content` for knowledge (with
202
+ `--detail brief`, just its outline `toc`; append `#<slug>` to a ref for one
203
+ section).
203
204
 
204
205
  AKM retrieves every supported capability type. It directly orchestrates
205
206
  defined execution surfaces such as workflows, agent dispatch, tasks, and
@@ -52,7 +52,10 @@ akm feedback workflows/deploy-to-prod --positive --reason "Completed without iss
52
52
 
53
53
  **Get refs from search.** Agents should call `akm search --detail agent` or
54
54
  `akm curate` to discover refs — not guess them. The `ref` field in search
55
- results is the stable token to pass to `akm show`. Feedback closes the loop.
55
+ results is the stable token to pass to `akm show`. For a long document,
56
+ `akm show <ref> --detail brief` returns its outline (`toc`, no body), and
57
+ `akm show <ref>#<slug>` then loads only the sections the task needs. Feedback
58
+ closes the loop.
56
59
  Record `--negative` only when the asset's content is wrong or stale; a note that
57
60
  simply did not fit the task is not negative feedback, so record nothing for it.
58
61
  `akm feedback <ref> --negative --reason "<what is wrong and what should change>"`
@@ -19,7 +19,8 @@ next):
19
19
  the `package.json` version (`tests/workflow-release.test.ts`).
20
20
  3. Add a migration note if the release needs one (see
21
21
  [CONTRIBUTING](https://github.com/itlackey/akm/blob/main/.github/CONTRIBUTING.md#shipping-a-release--migration-notes)).
22
- 4. Commit `release: <version>`, push it to the branch, and let CI run on it.
22
+ 4. Commit `release: <version>`, push it to the branch and dispatch the release
23
+ (next section); the workflow waits for CI on that commit.
23
24
 
24
25
  ## 2. Release akm
25
26
 
@@ -31,13 +32,19 @@ The Release workflow, on the pushed commit:
31
32
 
32
33
  1. runs Gated CI: semantic search with real embeddings, the Docker install
33
34
  matrix, and the native schedulers on Linux, macOS and Windows;
34
- 2. requires CI to have passed on the same commit (it does not run lint, the
35
- typecheck or the test suites again);
36
- 3. runs `tests/release-check.sh --skip-docker`: the packaged artifact's bin
37
- targets and migrator, package acceptance, and the upgrade rehearsal against
38
- the packed tarball;
39
- 4. builds the package and standalone binaries, publishes to npm (`latest` for
40
- a stable version, `next` for a prerelease) and creates the GitHub release.
35
+ 2. runs `tests/release-check.sh --skip-docker`: the packaged artifact's bin
36
+ targets and migrator, and package acceptance;
37
+ 3. builds the package and standalone binaries;
38
+ 4. requires CI to pass on the same commit, waiting up to 25 minutes for the run
39
+ to finish: the newest push or manual run counts, a pull request's does not
40
+ (it does not run lint, the typecheck, the test suites or the upgrade
41
+ rehearsal again);
42
+ 5. publishes to npm (`latest` for a stable version, `next` for a prerelease)
43
+ and creates the GitHub release.
44
+
45
+ Push nothing else to the branch until the Release finishes: a push cancels the
46
+ release commit's CI run (`ci.yml` cancels an in-progress run on the same ref),
47
+ and the Release then fails.
41
48
 
42
49
  If a job fails on a flake, rerun the failed jobs. If the fix needs a code
43
50
  change, commit it and release that commit. npm can take several minutes to show
@@ -435,6 +435,10 @@ to each connection's endpoint, one probe per distinct endpoint, checks the
435
435
  installed akm-cli version against the latest GitHub release (`cli-version`),
436
436
  runs the scheduler's recorded akm binary with `--version` to check it
437
437
  against the running CLI (`scheduler-binary`).
438
+ The `plugin-version` advisory compares the installed Claude Code plugin with the
439
+ channel its marketplace clone follows: the newest stable tag on the remote's
440
+ default branch, the release tag on the branch head for another branch such as
441
+ `next`, and the pinned tag for a marketplace added at a tag.
438
442
 
439
443
  Primary result fields:
440
444
 
@@ -822,7 +826,10 @@ contends with a full `akm index` rebuild in progress.
822
826
 
823
827
  Display an asset by ref. On a markdown document `#fragment` selects one
824
828
  section by heading slug (falling back to case-insensitive heading text); an
825
- unmatched fragment lists the available slugs.
829
+ unmatched fragment lists the available slugs. For a large document, read the
830
+ outline first and then only the sections you need: `akm show <ref> --detail
831
+ brief` returns the document's `toc` without the body, and `akm show
832
+ <ref>#<slug>` returns one section.
826
833
 
827
834
  ```sh
828
835
  akm show scripts/deploy.sh
@@ -830,7 +837,8 @@ akm show skills/code-review
830
837
  akm show agents/architect
831
838
  akm show commands/release
832
839
  akm show workflows/ship-release
833
- akm show knowledge/guide # the whole document
840
+ akm show knowledge/guide # the whole document, plus its toc
841
+ akm show knowledge/guide --detail brief # the outline (toc), no body
834
842
  akm show knowledge/guide#authentication # just that section
835
843
  akm show '<search-result-ref>' --context lead --max-tokens 800
836
844
  akm show knowledge/guide#nope # lists the available fragment slugs
@@ -879,6 +887,17 @@ reduced metadata-first view without `content`/`template`/`prompt`;
879
887
  including `ref`/`path`/`editable`, plus the optional fragment metadata
880
888
  described below.
881
889
 
890
+ `toc` is the outline of a whole Markdown document (knowledge, and every other
891
+ Markdown asset whose `#fragment` selects a section): one
892
+ `{ "level", "text", "slug", "startLine" }` entry per heading, in order, at every
893
+ `--detail` level, `brief` and `agent` included. `slug` is exactly what
894
+ `<ref>#<slug>` selects, duplicate headings included (`notes`, `notes-1`, ...),
895
+ and `startLine` is the heading's 1-based line in the file. It is read from the
896
+ same file the section is cut from, so it never lists a slug the show cannot
897
+ resolve. It is omitted for non-Markdown assets, for documents with no headings,
898
+ and for a fragment show (`<ref>#<slug>`). `--format text` prints it as a
899
+ `toc:` block.
900
+
882
901
  `links` lists the asset's declared links, grouped by kind: `outgoing` (the
883
902
  assets its own `xrefs:`, `supersededBy:`, `contradictedBy:`,
884
903
  `currentBeliefRefs:`, wiki `sources:`, `.derived` parent, page links, or
@@ -944,7 +963,7 @@ Returns type-specific payloads:
944
963
  | skill | `content` (full SKILL.md) |
945
964
  | command | `template`, `description` |
946
965
  | agent | `prompt`, `description`, `modelHint` |
947
- | knowledge | `content` — the whole document, or one section via `#fragment` |
966
+ | knowledge | `content` — the whole document, or one section via `#fragment`; `toc` on a whole document |
948
967
  | workflow | `workflowTitle`, `workflowParameters`, `steps` (each step's `orchestration` summary names its `engine` (the connection or harness it runs on) and model, or — for an [exec step](https://github.com/itlackey/akm/blob/main/docs/reference/workflow-schema.md#what-akm-show-reports-for-an-exec-step) — its `exec.command` and no engine at all) |
949
968
  | memory | `content` |
950
969
  | env | `keys` (key names only — values and comment text never returned) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.10.261015-beta",
3
+ "version": "0.10.261016-beta",
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": [
@@ -75,6 +75,7 @@
75
75
  "check": "bun run lint && bunx tsc --noEmit && bun run test:unit && bun run test:integration",
76
76
  "check:fast": "bun run lint && bunx tsc --noEmit && bun run test:unit",
77
77
  "check:changed": "bun run lint && bunx tsc --noEmit && bun test tests/integration/output-baseline.test.ts tests/integration/registry/registry-search.test.ts tests/integration/show-argv-entrypoint.test.ts tests/output-shapes-unit.test.ts",
78
+ "check:ci": "bash scripts/check-like-ci.sh",
78
79
  "sweep:tmp": "bun scripts/sweep-test-tmp.ts",
79
80
  "test": "bash scripts/test-unit.sh",
80
81
  "test:unit": "bash scripts/test-unit.sh",
@@ -103,8 +104,7 @@
103
104
  "ajv": "8.20.0",
104
105
  "bun-types": "^1.3.13",
105
106
  "cmd-shim": "9.0.2",
106
- "typescript": "^5.9.3",
107
- "zod-to-json-schema": "^3.23.0"
107
+ "typescript": "^5.9.3"
108
108
  },
109
109
  "optionalDependencies": {
110
110
  "better-sqlite3": "13.0.3",
@@ -124,6 +124,6 @@
124
124
  "semver": "^7.6.0",
125
125
  "turndown": "^7.2.4",
126
126
  "yaml": "^2.8.4",
127
- "zod": "^3.23.0"
127
+ "zod": "^4.6.6"
128
128
  }
129
129
  }