akm-cli 0.10.261015-beta → 0.10.261017-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 (124) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +8 -10
  3. package/SECURITY.md +7 -99
  4. package/STABILITY.md +58 -98
  5. package/dist/assets/hints/cli-hints-full.md +6 -4
  6. package/dist/assets/hints/cli-hints-short.md +4 -2
  7. package/dist/assets/opencode/akm-plugin.js +16 -8
  8. package/dist/assets/prompts/workflow-unit-preamble.md +3 -1
  9. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -2
  10. package/dist/assets/stash-skeleton/facts/conventions/organization.md +11 -17
  11. package/dist/cli.js +32 -41
  12. package/dist/commands/feedback-cli.js +5 -4
  13. package/dist/commands/health/plugin-staleness.js +86 -22
  14. package/dist/commands/health.js +15 -16
  15. package/dist/commands/proposal/proposal-cli.js +1 -1
  16. package/dist/commands/read/registry-search.js +4 -1
  17. package/dist/commands/read/search-cli.js +2 -2
  18. package/dist/commands/read/search.js +6 -6
  19. package/dist/commands/read/show.js +15 -3
  20. package/dist/commands/sources/self-update.js +9 -5
  21. package/dist/commands/tasks/tasks.js +12 -13
  22. package/dist/core/adapter/adapters/agent-skills-adapter.js +7 -7
  23. package/dist/core/adapter/adapters/akm-task-adapter.js +1 -1
  24. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  25. package/dist/core/adapter/adapters/claude-adapter.js +1 -1
  26. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  27. package/dist/core/adapter/adapters/generic-files-adapter.js +1 -1
  28. package/dist/core/adapter/adapters/llm-wiki-adapter.js +1 -1
  29. package/dist/core/adapter/adapters/opencode-adapter.js +1 -1
  30. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -1
  31. package/dist/core/asset/markdown.js +9 -0
  32. package/dist/core/bundle-rename.js +17 -7
  33. package/dist/core/config/config-walker.js +35 -32
  34. package/dist/core/config/schema/connections.js +6 -3
  35. package/dist/core/config/schema/harnesses.js +8 -1
  36. package/dist/core/config/schema/primitives.js +12 -1
  37. package/dist/core/config/schema/sources-bundles.js +2 -2
  38. package/dist/core/npm-package.js +48 -13
  39. package/dist/indexer/bundle-identity-guard.js +6 -6
  40. package/dist/indexer/index-written-assets.js +3 -3
  41. package/dist/indexer/indexer.js +12 -8
  42. package/dist/indexer/materialize-embeddings.js +12 -2
  43. package/dist/indexer/passes/metadata.js +103 -46
  44. package/dist/indexer/search/ranking.js +7 -3
  45. package/dist/indexer/search/search-fields.js +1 -1
  46. package/dist/indexer/usage/usage-events.js +30 -8
  47. package/dist/indexer/walk/walker.js +1 -1
  48. package/dist/output/renderers.js +2 -2
  49. package/dist/output/shapes/helpers.js +5 -3
  50. package/dist/output/text/command-format.js +5 -6
  51. package/dist/output/text/show-format.js +7 -0
  52. package/dist/registry/semver.js +12 -0
  53. package/dist/scripts/akm-migrate-node.js +21653 -5734
  54. package/dist/scripts/akm-migrate.js +21661 -5742
  55. package/dist/sources/providers/git-provider.js +2 -4
  56. package/dist/sources/providers/provider-utils.js +8 -13
  57. package/dist/sources/snapshot-fetchers/website-ingest.js +6 -7
  58. package/dist/storage/repositories/events-repository.js +15 -0
  59. package/dist/storage/repositories/improve-ledger-repository.js +13 -0
  60. package/dist/storage/repositories/index-entries-repository.js +43 -5
  61. package/dist/storage/repositories/outcome-repository.js +12 -0
  62. package/dist/storage/repositories/proposals-repository.js +5 -5
  63. package/dist/storage/repositories/salience-repository.js +13 -0
  64. package/dist/storage/repositories/task-history-repository.js +8 -7
  65. package/dist/storage/repositories/workflow-runs-repository.js +10 -8
  66. package/dist/tasks/backends/cron.js +19 -0
  67. package/docs/README.md +24 -60
  68. package/docs/agents/README.md +3 -8
  69. package/docs/architecture/README.md +18 -20
  70. package/docs/architecture/adapters.md +40 -43
  71. package/docs/architecture/architecture.md +171 -151
  72. package/docs/architecture/improvement.md +95 -91
  73. package/docs/architecture/internals/classification.md +101 -119
  74. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +33 -35
  75. package/docs/architecture/internals/health-advisories.md +59 -46
  76. package/docs/architecture/internals/improve-workflow.md +166 -693
  77. package/docs/architecture/internals/indexing.md +65 -465
  78. package/docs/architecture/internals/storage-locations.md +301 -848
  79. package/docs/architecture/persisted-data-compat.md +6 -6
  80. package/docs/architecture/specs/okf-support.md +67 -95
  81. package/docs/architecture/specs/ref.md +67 -124
  82. package/docs/architecture/testing/testing-workflow.md +114 -175
  83. package/docs/architecture/workflow-engine.md +34 -33
  84. package/docs/guides/README.md +5 -5
  85. package/docs/guides/author-bundles.md +72 -98
  86. package/docs/guides/author-workflows.md +11 -18
  87. package/docs/guides/bundles.md +91 -123
  88. package/docs/guides/capture-knowledge.md +10 -9
  89. package/docs/guides/concepts.md +45 -30
  90. package/docs/guides/discover-and-load.md +51 -36
  91. package/docs/guides/getting-started.md +27 -26
  92. package/docs/guides/improve-the-library.md +98 -80
  93. package/docs/guides/recipes/headless-install.md +4 -4
  94. package/docs/guides/recipes/website-source.md +9 -5
  95. package/docs/guides/run-workflows.md +10 -11
  96. package/docs/guides/scheduling.md +11 -17
  97. package/docs/guides/use-with-any-agent.md +15 -11
  98. package/docs/guides/wikis.md +6 -9
  99. package/docs/integration/bundling-akm.md +21 -26
  100. package/docs/maintainers/README.md +2 -2
  101. package/docs/maintainers/local-development.md +3 -16
  102. package/docs/maintainers/release-checklist.md +15 -8
  103. package/docs/migration/README.md +2 -1
  104. package/docs/migration/release-notes/0.10.0.md +167 -22
  105. package/docs/migration/release-notes/README.md +2 -2
  106. package/docs/migration/v0.9-to-v0.10.md +47 -7
  107. package/docs/product-surface.md +10 -8
  108. package/docs/reference/README.md +26 -15
  109. package/docs/reference/asset-types.md +11 -12
  110. package/docs/reference/bundle-types.md +6 -5
  111. package/docs/reference/cli.md +74 -31
  112. package/docs/reference/configuration.md +46 -60
  113. package/docs/reference/data-and-telemetry.md +204 -421
  114. package/docs/reference/env-and-secrets.md +80 -83
  115. package/docs/reference/memory.md +1 -1
  116. package/docs/reference/refs.md +23 -36
  117. package/docs/reference/registry.md +138 -348
  118. package/docs/reference/supported-formats.md +8 -8
  119. package/docs/reference/tasks.md +13 -19
  120. package/docs/reference/website-sources.md +23 -15
  121. package/docs/reference/workflow-schema.md +28 -47
  122. package/docs/reference/workflows.md +33 -37
  123. package/package.json +4 -4
  124. package/schemas/akm-config.json +535 -302
@@ -69,20 +69,28 @@ export function insertUsageEventStrict(db, event) {
69
69
  // ── Query ────────────────────────────────────────────────────────────────────
70
70
  /**
71
71
  * User feedback counts for many assets at once, keyed by durable `entry_ref`.
72
- * Refs with no user feedback are absent from the map. Read in one query per
73
- * chunk of refs.
72
+ * A negative that carried an exact fix does not count once an accepted
73
+ * `feedback` proposal for the asset exists at or after it (the rule `akm
74
+ * improve` uses for a fix already acted on); reverting that proposal makes it
75
+ * count again. Refs with no user feedback are absent from the map. Read in one
76
+ * query per chunk of refs.
74
77
  */
75
78
  export function countFeedbackSignalsByRef(db, refs) {
76
79
  const counts = new Map();
77
80
  for (let i = 0; i < refs.length; i += SQLITE_CHUNK_SIZE) {
78
81
  const chunk = refs.slice(i, i + SQLITE_CHUNK_SIZE);
79
82
  const rows = db
80
- .prepare(`SELECT entry_ref,
81
- SUM(CASE WHEN signal = 'positive' THEN 1 ELSE 0 END) AS pos,
82
- SUM(CASE WHEN signal = 'negative' THEN 1 ELSE 0 END) AS neg
83
- FROM usage_events
84
- WHERE event_type = 'feedback' AND source = 'user' AND entry_ref IN (${chunk.map(() => "?").join(",")})
85
- GROUP BY entry_ref`)
83
+ .prepare(`SELECT u.entry_ref,
84
+ SUM(CASE WHEN u.signal = 'positive' THEN 1 ELSE 0 END) AS pos,
85
+ SUM(CASE WHEN u.signal = 'negative' AND NOT (
86
+ json_extract(u.metadata, '$.fix') IS NOT NULL AND EXISTS (
87
+ SELECT 1 FROM proposals p
88
+ WHERE p.ref = u.entry_ref AND p.source = 'feedback' AND p.status = 'accepted'
89
+ AND julianday(p.created_at) >= julianday(u.created_at))
90
+ ) THEN 1 ELSE 0 END) AS neg
91
+ FROM usage_events u
92
+ WHERE u.event_type = 'feedback' AND u.source = 'user' AND u.entry_ref IN (${chunk.map(() => "?").join(",")})
93
+ GROUP BY u.entry_ref`)
86
94
  .all(...chunk);
87
95
  for (const row of rows)
88
96
  counts.set(row.entry_ref, { pos: row.pos, neg: row.neg });
@@ -150,3 +158,17 @@ export function purgeOldUsageEvents(db, retentionDays) {
150
158
  /* Table may not exist yet */
151
159
  }
152
160
  }
161
+ /**
162
+ * Rewrite every `usage_events.entry_ref` naming `oldBundleId` to `newBundleId`
163
+ * (`akm bundle rename`). Search ranking counts feedback by `entry_ref`, so an
164
+ * event left under the old prefix stops counting for the renamed bundle's
165
+ * assets. `entry_id` is untouched: the rename keeps every entry's row id. A
166
+ * legacy unqualified ref is not matched by the `<old>//` prefix and is left alone.
167
+ * Returns the number of rows rewritten.
168
+ */
169
+ export function renameUsageEventRefs(db, oldBundleId, newBundleId) {
170
+ const prefix = `${oldBundleId}//`;
171
+ return Number(db
172
+ .prepare(`UPDATE usage_events SET entry_ref = ? || substr(entry_ref, length(?) + 1) WHERE substr(entry_ref, 1, length(?)) = ?`)
173
+ .run(newBundleId, oldBundleId, prefix, prefix).changes);
174
+ }
@@ -112,7 +112,7 @@ function walkStashGit(stashRoot, options) {
112
112
  ? ignoredResult.stdout.toString("utf8")
113
113
  : String(ignoredResult.stdout ?? "");
114
114
  // Data-hygiene filename skips: the legacy metadata sidecar (never indexed as
115
- // content — Chunk-8 folds it into the bundle format) plus git dot-files.
115
+ // content) plus git dot-files.
116
116
  const SKIP_FILES = new Set([".stash.json", ".gitignore", ".gitattributes"]);
117
117
  const stdout = Buffer.isBuffer(result.stdout) ? result.stdout.toString("utf8") : String(result.stdout ?? "");
118
118
  const files = [...stdout.split("\0"), ...ignoredStdout.split("\0")]
@@ -217,8 +217,8 @@ const agentMdRenderer = {
217
217
  },
218
218
  };
219
219
  // ── 4. knowledge-md ──────────────────────────────────────────────────────────
220
- const KNOWLEDGE_ACTION = "Reference material - read the content below. For a large document, read one section with `akm show <ref>#fragment` " +
221
- "(an unmatched #fragment lists the available slugs).";
220
+ const KNOWLEDGE_ACTION = "Reference material - read the content below. For a large document, run `akm show <ref> --detail brief` for its " +
221
+ "outline (`toc`, no body), then `akm show <ref>#<slug>` for only the sections you need.";
222
222
  const knowledgeMdRenderer = {
223
223
  name: "knowledge-md",
224
224
  buildShowResponse(ctx) {
@@ -71,7 +71,7 @@ export function shapeProposalEntry(entry, detail) {
71
71
  if (detail === "normal") {
72
72
  // `confidence` and `gateDecision` (#577) explain why a proposal is pending,
73
73
  // so they are projected at `normal` for `akm proposal list/show` when present.
74
- // `retirement` (alpha.9) is the same kind of field for a consolidate
74
+ // `retirement` is the same kind of field for a consolidate
75
75
  // `retire` proposal: without it, "list/show" carries no reason a reviewer
76
76
  // could act on (its `payload.content` is empty by design — a retire
77
77
  // proposal deletes, it does not write).
@@ -278,7 +278,7 @@ export function shapeSearchHit(hit, detail) {
278
278
  }
279
279
  // Stash hit (local or remote)
280
280
  // `ref` is included at `brief` so agents can run `akm show <ref>` without
281
- // needing --detail full or --detail agent (REC-03).
281
+ // needing --detail full or --detail agent.
282
282
  if (detail === "brief") {
283
283
  return pickFields(hit, ["type", "name", "ref", "action", "estimatedTokens", "keys"]);
284
284
  }
@@ -348,6 +348,7 @@ export function shapeShowOutput(result, detail, shape = "human") {
348
348
  "editHint",
349
349
  "description",
350
350
  "action",
351
+ "toc",
351
352
  "content",
352
353
  "template",
353
354
  "prompt",
@@ -375,12 +376,13 @@ export function shapeShowOutput(result, detail, shape = "human") {
375
376
  const base = pickFields(result, [
376
377
  "type",
377
378
  "name",
378
- // ref is always projected, same as path/editable below (R-020).
379
+ // ref is always projected, same as path/editable below.
379
380
  "ref",
380
381
  "origin",
381
382
  "action",
382
383
  "description",
383
384
  "tags",
385
+ "toc",
384
386
  "content",
385
387
  "template",
386
388
  "prompt",
@@ -7,8 +7,7 @@
7
7
  * events/history, search/curate, and the stash-lifecycle verbs (init,
8
8
  * index, list, add, remove, update, upgrade, clone).
9
9
  *
10
- * Split out of `helpers.ts` (formerly 1418 lines / 59 fns) as its own
11
- * sibling module: unlike `show`/`workflow`/`proposal` (each a stateful,
10
+ * Unlike `show`/`workflow`/`proposal` (each a stateful,
12
11
  * multi-branch renderer with its own module), these are flat
13
12
  * field-extraction formatters with no shared state.
14
13
  */
@@ -308,7 +307,7 @@ export function formatSearchPlain(r, detail) {
308
307
  if (parts.length > 0)
309
308
  lines.push(`timing: ${parts.join(", ")}`);
310
309
  }
311
- // REC-02: When stash hits exist, tell the agent the next required step so it
310
+ // When stash hits exist, tell the agent the next required step so it
312
311
  // doesn't skip `akm show` and write from training memory instead.
313
312
  if (hits.length >= 1) {
314
313
  // Prefer skill/command/agent type hits for the "Next:" ref — knowledge docs are
@@ -517,7 +516,7 @@ export function formatAddPlain(r) {
517
516
  // writes a desired locator to config — nothing is synced until a later
518
517
  // `akm bundle update`. Force-fitting the declarative shape's `added`/`entry`/`message`
519
518
  // fields into the "Installed <ref> (N scanned...)" wording produced
520
- // "Installed undefined (0 directories scanned...)" (R-014) because it
519
+ // "Installed undefined (0 directories scanned...)" because it
521
520
  // implied a sync that never happened. `AddResponse` always carries an
522
521
  // `index` block (even when zero entries were scanned); `SourceAddResult`
523
522
  // never does — that is the reliable shape discriminator used below to
@@ -630,11 +629,11 @@ export function formatBundleRenamePlain(r) {
630
629
  return lines.join("\n");
631
630
  }
632
631
  export function formatUpdatePlain(r) {
633
- // R-015: `processed` alone conflated three cases into one empty array —
632
+ // `processed` alone would conflate three cases into one empty array —
634
633
  // a true no-op (--all with nothing configured), a source this call flatly
635
634
  // never looked at (a plain source under --all), and a plain git/website
636
635
  // source that WAS just synced successfully but has no `UpdateResultItem`
637
- // shape to report (no version/lock to diff). All three rendered as the
636
+ // shape to report (no version/lock to diff). All three would render as the
638
637
  // same misleading "nothing to update". `plainSynced` and `skipped` (see
639
638
  // sources/types.ts) cover the other two so this can report accurately.
640
639
  const processed = r.processed;
@@ -59,6 +59,13 @@ export function formatShowPlain(r, detail) {
59
59
  if (r.schemaVersion !== undefined)
60
60
  lines.push(`schemaVersion: ${String(r.schemaVersion)}`);
61
61
  }
62
+ if (Array.isArray(r.toc) && r.toc.length > 0) {
63
+ lines.push("");
64
+ lines.push("toc:");
65
+ for (const heading of r.toc) {
66
+ lines.push(` ${"#".repeat(heading.level)} ${heading.text} (#${heading.slug}, line ${heading.startLine})`);
67
+ }
68
+ }
62
69
  const links = typeof r.links === "object" && r.links !== null ? r.links : undefined;
63
70
  if (links) {
64
71
  lines.push("");
@@ -41,6 +41,18 @@ export function isSemverRange(input) {
41
41
  export function maxSatisfying(versions, range) {
42
42
  return semver.maxSatisfying(versions, range) ?? undefined;
43
43
  }
44
+ /**
45
+ * Highest of `versions` in semver order, prereleases included (`0.10.1-beta` sorts above `0.9.30`). Invalid entries
46
+ * are ignored; `undefined` when none is valid. Use this to order known versions, not `maxSatisfying(versions, "*")`,
47
+ * which skips every prerelease.
48
+ */
49
+ export function highestVersion(versions) {
50
+ return semver.rsort(versions.filter((version) => semver.valid(version) !== null))[0];
51
+ }
52
+ /** True when `version` is a valid semver prerelease (`1.2.3-beta`). */
53
+ export function isPrerelease(version) {
54
+ return semver.valid(version) !== null && semver.prerelease(version) !== null;
55
+ }
44
56
  /** True when `version` satisfies `range` (both real semver forms). False for an invalid version or range. */
45
57
  export function satisfiesRange(version, range) {
46
58
  return semver.satisfies(version, range);