akm-cli 0.7.5 → 0.8.0-rc.6

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 (236) hide show
  1. package/{.github/CHANGELOG.md → CHANGELOG.md} +113 -2
  2. package/README.md +20 -4
  3. package/SECURITY.md +93 -0
  4. package/dist/cli/config-migrate.js +144 -0
  5. package/dist/cli/config-validate.js +39 -0
  6. package/dist/cli/confirm.js +73 -0
  7. package/dist/cli/parse-args.js +133 -0
  8. package/dist/cli.js +1995 -551
  9. package/dist/commands/agent-dispatch.js +110 -0
  10. package/dist/commands/agent-support.js +68 -0
  11. package/dist/commands/completions.js +3 -0
  12. package/dist/commands/config-cli.js +130 -534
  13. package/dist/commands/consolidate.js +1531 -0
  14. package/dist/commands/curate.js +44 -3
  15. package/dist/commands/db-cli.js +23 -0
  16. package/dist/commands/distill-promotion-policy.js +660 -0
  17. package/dist/commands/distill.js +990 -75
  18. package/dist/commands/eval-cases.js +43 -0
  19. package/dist/commands/events.js +5 -23
  20. package/dist/commands/graph.js +477 -0
  21. package/dist/commands/health.js +400 -0
  22. package/dist/commands/help/help-accept.md +9 -0
  23. package/dist/commands/help/help-improve.md +77 -0
  24. package/dist/commands/help/help-proposals.md +15 -0
  25. package/dist/commands/help/help-propose.md +17 -0
  26. package/dist/commands/help/help-reject.md +8 -0
  27. package/dist/commands/history.js +54 -46
  28. package/dist/commands/improve-profiles.js +146 -0
  29. package/dist/commands/improve-result-file.js +103 -0
  30. package/dist/commands/improve.js +2175 -0
  31. package/dist/commands/info.js +5 -2
  32. package/dist/commands/init.js +50 -2
  33. package/dist/commands/installed-stashes.js +102 -139
  34. package/dist/commands/knowledge.js +136 -0
  35. package/dist/commands/lint/agent-linter.js +49 -0
  36. package/dist/commands/lint/base-linter.js +479 -0
  37. package/dist/commands/lint/command-linter.js +49 -0
  38. package/dist/commands/lint/default-linter.js +16 -0
  39. package/dist/commands/lint/index.js +183 -0
  40. package/dist/commands/lint/knowledge-linter.js +16 -0
  41. package/dist/commands/lint/markdown-insertion.js +343 -0
  42. package/dist/commands/lint/memory-linter.js +61 -0
  43. package/dist/commands/lint/registry.js +36 -0
  44. package/dist/commands/lint/skill-linter.js +45 -0
  45. package/dist/commands/lint/task-linter.js +50 -0
  46. package/dist/commands/lint/types.js +4 -0
  47. package/dist/commands/lint/vault-key-rules.js +139 -0
  48. package/dist/commands/lint/workflow-linter.js +56 -0
  49. package/dist/commands/lint.js +4 -0
  50. package/dist/commands/migration-help.js +5 -2
  51. package/dist/commands/proposal.js +66 -12
  52. package/dist/commands/propose.js +86 -31
  53. package/dist/commands/reflect.js +1119 -73
  54. package/dist/commands/registry-search.js +5 -2
  55. package/dist/commands/remember.js +69 -6
  56. package/dist/commands/schema-repair.js +203 -0
  57. package/dist/commands/search.js +115 -14
  58. package/dist/commands/self-update.js +3 -0
  59. package/dist/commands/show.js +144 -25
  60. package/dist/commands/source-add.js +17 -45
  61. package/dist/commands/source-clone.js +3 -0
  62. package/dist/commands/source-manage.js +14 -19
  63. package/dist/commands/tasks.js +438 -0
  64. package/dist/commands/url-checker.js +42 -0
  65. package/dist/commands/vault.js +130 -77
  66. package/dist/core/action-contributors.js +28 -0
  67. package/dist/core/asset-ref.js +7 -0
  68. package/dist/core/asset-registry.js +7 -16
  69. package/dist/core/asset-serialize.js +88 -0
  70. package/dist/core/asset-spec.js +22 -0
  71. package/dist/core/common.js +157 -0
  72. package/dist/core/concurrent.js +25 -0
  73. package/dist/core/config-io.js +347 -0
  74. package/dist/core/config-migration.js +625 -0
  75. package/dist/core/config-schema.js +501 -0
  76. package/dist/core/config-sources.js +108 -0
  77. package/dist/core/config-types.js +4 -0
  78. package/dist/core/config-walker.js +337 -0
  79. package/dist/core/config.js +327 -987
  80. package/dist/core/errors.js +40 -19
  81. package/dist/core/events.js +91 -138
  82. package/dist/core/file-lock.js +104 -0
  83. package/dist/core/frontmatter.js +3 -6
  84. package/dist/core/lesson-lint.js +3 -0
  85. package/dist/core/markdown.js +20 -0
  86. package/dist/core/memory-belief.js +62 -0
  87. package/dist/core/memory-contradiction-detect.js +274 -0
  88. package/dist/core/memory-improve.js +806 -0
  89. package/dist/core/parse.js +158 -0
  90. package/dist/core/paths.js +326 -14
  91. package/dist/core/proposal-quality-validators.js +364 -0
  92. package/dist/core/proposal-validators.js +69 -0
  93. package/dist/core/proposals.js +498 -42
  94. package/dist/core/state-db.js +927 -0
  95. package/dist/core/text-truncation.js +107 -0
  96. package/dist/core/time.js +54 -0
  97. package/dist/core/warn.js +62 -1
  98. package/dist/core/write-source.js +3 -0
  99. package/dist/indexer/db-backup.js +391 -0
  100. package/dist/indexer/db-search.js +152 -253
  101. package/dist/indexer/db.js +933 -103
  102. package/dist/indexer/ensure-index.js +64 -0
  103. package/dist/indexer/file-context.js +3 -0
  104. package/dist/indexer/graph-boost.js +376 -101
  105. package/dist/indexer/graph-db.js +391 -0
  106. package/dist/indexer/graph-dedup.js +95 -0
  107. package/dist/indexer/graph-extraction.js +550 -124
  108. package/dist/indexer/index-context.js +4 -0
  109. package/dist/indexer/indexer.js +506 -291
  110. package/dist/indexer/llm-cache.js +47 -0
  111. package/dist/indexer/manifest.js +3 -0
  112. package/dist/indexer/matchers.js +148 -160
  113. package/dist/indexer/memory-inference.js +99 -74
  114. package/dist/indexer/metadata-contributors.js +29 -0
  115. package/dist/indexer/metadata.js +255 -196
  116. package/dist/indexer/path-resolver.js +92 -0
  117. package/dist/indexer/project-context.js +192 -0
  118. package/dist/indexer/ranking-contributors.js +331 -0
  119. package/dist/indexer/ranking.js +81 -0
  120. package/dist/indexer/search-fields.js +5 -9
  121. package/dist/indexer/search-hit-enrichers.js +111 -0
  122. package/dist/indexer/search-source.js +44 -10
  123. package/dist/indexer/semantic-status.js +5 -16
  124. package/dist/indexer/staleness-detect.js +447 -0
  125. package/dist/indexer/usage-events.js +12 -9
  126. package/dist/indexer/walker.js +28 -0
  127. package/dist/integrations/agent/builders.js +135 -0
  128. package/dist/integrations/agent/config.js +122 -230
  129. package/dist/integrations/agent/detect.js +3 -0
  130. package/dist/integrations/agent/index.js +7 -13
  131. package/dist/integrations/agent/model-aliases.js +55 -0
  132. package/dist/integrations/agent/profiles.js +70 -5
  133. package/dist/integrations/agent/prompts.js +150 -74
  134. package/dist/integrations/agent/runner.js +151 -0
  135. package/dist/integrations/agent/sdk-runner.js +126 -0
  136. package/dist/integrations/agent/spawn.js +118 -23
  137. package/dist/integrations/github.js +3 -0
  138. package/dist/integrations/lockfile.js +32 -69
  139. package/dist/integrations/session-logs/index.js +68 -0
  140. package/dist/integrations/session-logs/providers/claude-code.js +59 -0
  141. package/dist/integrations/session-logs/providers/opencode.js +55 -0
  142. package/dist/integrations/session-logs/types.js +4 -0
  143. package/dist/llm/call-ai.js +62 -0
  144. package/dist/llm/client.js +72 -124
  145. package/dist/llm/embedder.js +3 -19
  146. package/dist/llm/embedders/cache.js +3 -7
  147. package/dist/llm/embedders/local.js +3 -0
  148. package/dist/llm/embedders/remote.js +20 -8
  149. package/dist/llm/embedders/types.js +3 -7
  150. package/dist/llm/feature-gate.js +89 -48
  151. package/dist/llm/graph-extract.js +676 -70
  152. package/dist/llm/index-passes.js +9 -23
  153. package/dist/llm/memory-infer.js +52 -71
  154. package/dist/llm/metadata-enhance.js +42 -29
  155. package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
  156. package/dist/output/cli-hints-full.md +281 -0
  157. package/dist/output/cli-hints-short.md +65 -0
  158. package/dist/output/cli-hints.js +5 -318
  159. package/dist/output/context.js +3 -0
  160. package/dist/output/renderers.js +223 -256
  161. package/dist/output/shapes.js +150 -105
  162. package/dist/output/text.js +318 -30
  163. package/dist/registry/build-index.js +3 -0
  164. package/dist/registry/create-provider-registry.js +3 -0
  165. package/dist/registry/factory.js +3 -0
  166. package/dist/registry/origin-resolve.js +3 -0
  167. package/dist/registry/providers/index.js +3 -0
  168. package/dist/registry/providers/skills-sh.js +70 -49
  169. package/dist/registry/providers/static-index.js +53 -48
  170. package/dist/registry/providers/types.js +3 -24
  171. package/dist/registry/resolve.js +11 -16
  172. package/dist/registry/types.js +3 -0
  173. package/dist/scripts/migrate-storage.js +17307 -0
  174. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8900 -0
  175. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  176. package/dist/setup/detect.js +3 -0
  177. package/dist/setup/ripgrep-install.js +3 -0
  178. package/dist/setup/ripgrep-resolve.js +3 -0
  179. package/dist/setup/setup.js +775 -37
  180. package/dist/setup/steps.js +3 -15
  181. package/dist/sources/include.js +3 -0
  182. package/dist/sources/provider-factory.js +5 -12
  183. package/dist/sources/provider.js +3 -20
  184. package/dist/sources/providers/filesystem.js +19 -23
  185. package/dist/sources/providers/git.js +7 -5
  186. package/dist/sources/providers/index.js +3 -0
  187. package/dist/sources/providers/install-types.js +3 -13
  188. package/dist/sources/providers/npm.js +3 -4
  189. package/dist/sources/providers/provider-utils.js +3 -0
  190. package/dist/sources/providers/sync-from-ref.js +3 -11
  191. package/dist/sources/providers/tar-utils.js +3 -0
  192. package/dist/sources/providers/website.js +18 -22
  193. package/dist/sources/resolve.js +3 -0
  194. package/dist/sources/types.js +3 -0
  195. package/dist/sources/website-ingest.js +7 -0
  196. package/dist/tasks/backends/cron.js +203 -0
  197. package/dist/tasks/backends/exec-utils.js +28 -0
  198. package/dist/tasks/backends/index.js +24 -0
  199. package/dist/tasks/backends/launchd-template.xml +19 -0
  200. package/dist/tasks/backends/launchd.js +187 -0
  201. package/dist/tasks/backends/schtasks-template.xml +29 -0
  202. package/dist/tasks/backends/schtasks.js +215 -0
  203. package/dist/tasks/parser.js +211 -0
  204. package/dist/tasks/resolveAkmBin.js +87 -0
  205. package/dist/tasks/runner.js +458 -0
  206. package/dist/tasks/schedule.js +211 -0
  207. package/dist/tasks/schema.js +15 -0
  208. package/dist/tasks/validator.js +62 -0
  209. package/dist/version.js +3 -0
  210. package/dist/wiki/index-template.md +12 -0
  211. package/dist/wiki/ingest-workflow-template.md +54 -0
  212. package/dist/wiki/log-template.md +8 -0
  213. package/dist/wiki/schema-template.md +61 -0
  214. package/dist/wiki/wiki-templates.js +15 -0
  215. package/dist/wiki/wiki.js +13 -61
  216. package/dist/workflows/authoring.js +8 -25
  217. package/dist/workflows/cli.js +3 -0
  218. package/dist/workflows/db.js +140 -10
  219. package/dist/workflows/document-cache.js +3 -10
  220. package/dist/workflows/parser.js +3 -0
  221. package/dist/workflows/renderer.js +11 -3
  222. package/dist/workflows/runs.js +62 -91
  223. package/dist/workflows/schema.js +3 -0
  224. package/dist/workflows/scope-key.js +3 -0
  225. package/dist/workflows/validator.js +4 -8
  226. package/dist/workflows/workflow-template.md +24 -0
  227. package/docs/README.md +9 -2
  228. package/docs/data-and-telemetry.md +225 -0
  229. package/docs/migration/release-notes/0.7.0.md +1 -1
  230. package/docs/migration/release-notes/0.7.5.md +2 -2
  231. package/docs/migration/release-notes/0.8.0.md +48 -0
  232. package/docs/migration/v0.7-to-v0.8.md +1307 -0
  233. package/package.json +20 -8
  234. package/.github/LICENSE +0 -374
  235. package/dist/commands/install-audit.js +0 -381
  236. package/dist/templates/wiki-templates.js +0 -100
@@ -1,53 +1,15 @@
1
- /**
2
- * `akm history` surfaces internal mutation/usage events for a single asset
3
- * (`--ref`) or stash-wide.
4
- *
5
- * Event sources:
6
- * - `usage_events` SQLite table: search, show, and feedback events recorded
7
- * by the local indexer during normal CLI use.
8
- * - `events.jsonl` append-only stream (opt-in via `--include-proposals`):
9
- * proposal lifecycle events (`promoted`, `rejected`) emitted by
10
- * `akm proposal accept` / `akm proposal reject`. Use this flag to see
11
- * the full proposal review trail alongside usage events.
12
- *
13
- * The two sources are merged and sorted chronologically (oldest first) so
14
- * consumers see a coherent lifecycle trail in a single output.
15
- */
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
16
4
  import { parseAssetRef } from "../core/asset-ref";
17
5
  import { UsageError } from "../core/errors";
18
6
  import { readEvents } from "../core/events";
7
+ import { listProposals } from "../core/proposals";
8
+ import { isoToSqlite, parseSinceToIso } from "../core/time";
19
9
  import { closeDatabase, openExistingDatabase } from "../indexer/db";
20
10
  // Proposal lifecycle event types emitted by the proposal substrate (#225).
21
11
  const PROPOSAL_EVENT_TYPES = new Set(["promoted", "rejected"]);
22
12
  // ── Helpers ──────────────────────────────────────────────────────────────────
23
- function normalizeSince(since) {
24
- // Accept "YYYY-MM-DD", "YYYY-MM-DDTHH:MM:SSZ", epoch ms, or anything Date can parse.
25
- const trimmed = since.trim();
26
- if (!trimmed) {
27
- throw new UsageError("--since cannot be empty.", "INVALID_FLAG_VALUE");
28
- }
29
- // Pure-digit input → epoch milliseconds
30
- if (/^\d+$/.test(trimmed)) {
31
- const ms = Number.parseInt(trimmed, 10);
32
- const d = new Date(ms);
33
- if (Number.isNaN(d.getTime())) {
34
- throw new UsageError(`Invalid --since value: ${since}`, "INVALID_FLAG_VALUE");
35
- }
36
- return d
37
- .toISOString()
38
- .replace("T", " ")
39
- .replace(/\.\d+Z$/, "");
40
- }
41
- const parsed = new Date(trimmed);
42
- if (Number.isNaN(parsed.getTime())) {
43
- throw new UsageError(`Invalid --since value: ${since}. Expected ISO timestamp (e.g. 2026-04-01T00:00:00Z) or epoch ms.`, "INVALID_FLAG_VALUE");
44
- }
45
- // Match the "YYYY-MM-DD HH:MM:SS" format SQLite's datetime('now') stores.
46
- return parsed
47
- .toISOString()
48
- .replace("T", " ")
49
- .replace(/\.\d+Z$/, "");
50
- }
51
13
  function parseMetadata(raw) {
52
14
  if (!raw)
53
15
  return null;
@@ -66,6 +28,7 @@ function toEntry(row) {
66
28
  entryId: row.entry_id,
67
29
  query: row.query,
68
30
  signal: row.signal,
31
+ source: row.source,
69
32
  metadata: parseMetadata(row.metadata),
70
33
  createdAt: row.created_at,
71
34
  };
@@ -104,7 +67,7 @@ export async function akmHistory(options = {}) {
104
67
  parseAssetRef(trimmed);
105
68
  normalizedRef = trimmed;
106
69
  }
107
- const sinceNormalized = options.since !== undefined ? normalizeSince(options.since) : undefined;
70
+ const sinceNormalized = options.since !== undefined ? isoToSqlite(parseSinceToIso(options.since)) : undefined;
108
71
  const db = options.db ?? openExistingDatabase();
109
72
  const ownsDb = options.db === undefined;
110
73
  try {
@@ -118,8 +81,12 @@ export async function akmHistory(options = {}) {
118
81
  conditions.push("created_at >= ?");
119
82
  params.push(sinceNormalized);
120
83
  }
84
+ if (options.source !== undefined) {
85
+ conditions.push("source = ?");
86
+ params.push(options.source);
87
+ }
121
88
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
122
- const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, created_at
89
+ const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, source, created_at
123
90
  FROM usage_events ${where}
124
91
  ORDER BY id ASC`;
125
92
  const rows = db.prepare(sql).all(...params);
@@ -128,7 +95,7 @@ export async function akmHistory(options = {}) {
128
95
  const sources = ["usage_events"];
129
96
  const proposalEntries = [];
130
97
  if (options.includeProposals === true) {
131
- sources.push("events.jsonl");
98
+ sources.push("state.db");
132
99
  // Convert sinceNormalized ("YYYY-MM-DD HH:MM:SS") to ISO for readEvents
133
100
  // which uses `ts >= since` where `ts` is ISO-8601.
134
101
  const sinceIso = sinceNormalized !== undefined ? `${sinceNormalized.replace(" ", "T")}Z` : undefined;
@@ -154,6 +121,7 @@ export async function akmHistory(options = {}) {
154
121
  entryId: null,
155
122
  query: null,
156
123
  signal: null,
124
+ source: null,
157
125
  metadata: event.metadata ?? null,
158
126
  createdAt,
159
127
  });
@@ -169,6 +137,45 @@ export async function akmHistory(options = {}) {
169
137
  return 1;
170
138
  return a.id - b.id;
171
139
  });
140
+ // ── Accept-rate-per-source (F-4 / #385) ─────────────────────────────────
141
+ let acceptRateBySource;
142
+ if (options.acceptRateBySource) {
143
+ const stashDir = options.stashDir;
144
+ if (stashDir) {
145
+ const bySource = new Map();
146
+ const countProposals = (statuses, includeArchive) => {
147
+ for (const status of statuses) {
148
+ const proposals = listProposals(stashDir, { status, includeArchive });
149
+ for (const p of proposals) {
150
+ const src = p.source || "(unknown)";
151
+ const entry = bySource.get(src) ?? { accepted: 0, rejected: 0, pending: 0 };
152
+ if (status === "accepted")
153
+ entry.accepted++;
154
+ else if (status === "rejected")
155
+ entry.rejected++;
156
+ else
157
+ entry.pending++;
158
+ bySource.set(src, entry);
159
+ }
160
+ }
161
+ };
162
+ countProposals(["pending"], false);
163
+ countProposals(["accepted", "rejected"], true);
164
+ acceptRateBySource = Array.from(bySource.entries())
165
+ .map(([source, counts]) => {
166
+ const decided = counts.accepted + counts.rejected;
167
+ return {
168
+ source,
169
+ total: decided + counts.pending,
170
+ accepted: counts.accepted,
171
+ rejected: counts.rejected,
172
+ pending: counts.pending,
173
+ acceptRate: decided > 0 ? counts.accepted / decided : null,
174
+ };
175
+ })
176
+ .sort((a, b) => b.total - a.total); // Most active source first
177
+ }
178
+ }
172
179
  const response = {
173
180
  schemaVersion: 1,
174
181
  ...(normalizedRef !== undefined ? { ref: normalizedRef } : {}),
@@ -176,6 +183,7 @@ export async function akmHistory(options = {}) {
176
183
  totalCount: entries.length,
177
184
  entries,
178
185
  sources,
186
+ ...(acceptRateBySource !== undefined ? { acceptRateBySource } : {}),
179
187
  };
180
188
  return response;
181
189
  }
@@ -0,0 +1,146 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { parseAssetRef } from "../core/asset-ref";
5
+ import { warn } from "../core/warn";
6
+ /** Profile name used as the final fallback when nothing else resolves. */
7
+ const FALLBACK_PROFILE_NAME = "default";
8
+ // Built-in default allowed types per process
9
+ export const DEFAULT_ALLOWED_TYPES = {
10
+ reflect: ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"],
11
+ distill: ["memory"],
12
+ consolidate: ["memory"],
13
+ };
14
+ const BUILTIN_PROFILES = {
15
+ default: {
16
+ description: "Standard improve pass — all sub-processes, markdown asset types.",
17
+ processes: {
18
+ reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
19
+ distill: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.distill },
20
+ consolidate: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.consolidate },
21
+ memoryInference: { enabled: true },
22
+ graphExtraction: { enabled: true },
23
+ feedbackDistillation: { enabled: true },
24
+ // validation: deliberately undefined — third-tier classifier is opt-in.
25
+ },
26
+ },
27
+ quick: {
28
+ description: "Reflect-only pass — no distill, consolidate, memoryInference, or graphExtraction.",
29
+ processes: {
30
+ reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
31
+ distill: { enabled: false },
32
+ consolidate: { enabled: false },
33
+ memoryInference: { enabled: false },
34
+ graphExtraction: { enabled: false },
35
+ feedbackDistillation: { enabled: false },
36
+ },
37
+ },
38
+ thorough: {
39
+ // Reserved for future divergence; for now behaviorally identical to
40
+ // `default`. Documented here so callers picking `--profile thorough` do
41
+ // not expect a different code path until we wire stricter limits in.
42
+ description: "All sub-processes enabled (currently identical to default; reserved for future divergence).",
43
+ processes: {
44
+ reflect: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.reflect },
45
+ distill: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.distill },
46
+ consolidate: { enabled: true, allowedTypes: DEFAULT_ALLOWED_TYPES.consolidate },
47
+ memoryInference: { enabled: true },
48
+ graphExtraction: { enabled: true },
49
+ feedbackDistillation: { enabled: true },
50
+ },
51
+ },
52
+ "memory-focus": {
53
+ description: "Memory and lesson improvement only — no distill or consolidate.",
54
+ processes: {
55
+ reflect: { enabled: true, allowedTypes: ["memory", "lesson"] },
56
+ distill: { enabled: false },
57
+ consolidate: { enabled: false },
58
+ memoryInference: { enabled: true },
59
+ graphExtraction: { enabled: false },
60
+ feedbackDistillation: { enabled: false },
61
+ },
62
+ },
63
+ };
64
+ /**
65
+ * Default enabled-state for known improve processes when neither the user
66
+ * profile nor the built-in default profile specifies an override.
67
+ *
68
+ * These mirror the legacy `LlmFeatureFlags` defaults so callers that bypass
69
+ * the profile system (rare — most run through `resolveImproveProfile`) get
70
+ * the same answer.
71
+ */
72
+ const IMPROVE_PROCESS_DEFAULTS = {
73
+ reflect: true,
74
+ distill: true,
75
+ consolidate: true,
76
+ memoryInference: true,
77
+ graphExtraction: true,
78
+ feedbackDistillation: true,
79
+ validation: false,
80
+ };
81
+ /**
82
+ * Compute the effective enabled-state for a named improve process.
83
+ *
84
+ * Resolution order: explicit `profile.processes.<name>.enabled` (boolean) →
85
+ * the built-in {@link IMPROVE_PROCESS_DEFAULTS} fallback → `false`.
86
+ */
87
+ export function resolveProcessEnabled(processName, profile) {
88
+ const processes = profile.processes;
89
+ const entry = processes?.[processName];
90
+ if (entry && typeof entry.enabled === "boolean")
91
+ return entry.enabled;
92
+ return IMPROVE_PROCESS_DEFAULTS[processName] ?? false;
93
+ }
94
+ function deepMerge(base, override) {
95
+ if (typeof base !== "object" || base === null)
96
+ return override ?? base;
97
+ const result = { ...base };
98
+ for (const key of Object.keys(override)) {
99
+ const ov = override[key];
100
+ // Treat `null` the same as `undefined` so user overrides never wipe a
101
+ // built-in field with `null`. The on-disk parser already strips nulls,
102
+ // but the programmatic API exposes this path and callers occasionally
103
+ // pass JSON-shaped objects with explicit nulls.
104
+ if (ov !== undefined && ov !== null) {
105
+ const bv = base[key];
106
+ if (typeof bv === "object" && bv !== null && typeof ov === "object" && ov !== null && !Array.isArray(bv)) {
107
+ result[key] = deepMerge(bv, ov);
108
+ }
109
+ else {
110
+ result[key] = ov;
111
+ }
112
+ }
113
+ }
114
+ return result;
115
+ }
116
+ export function resolveImproveProfile(name, config) {
117
+ const requestedName = name ??
118
+ (typeof config.defaults?.improve === "string" ? config.defaults.improve : undefined) ??
119
+ FALLBACK_PROFILE_NAME;
120
+ const hasBuiltin = requestedName in BUILTIN_PROFILES;
121
+ const hasUserDefined = !!config.profiles?.improve?.[requestedName];
122
+ let effectiveName = requestedName;
123
+ if (!hasBuiltin && !hasUserDefined && requestedName !== FALLBACK_PROFILE_NAME) {
124
+ warn(`[akm] Improve profile "${requestedName}" not found in built-ins or config. ` +
125
+ `Falling back to "${FALLBACK_PROFILE_NAME}".`);
126
+ effectiveName = FALLBACK_PROFILE_NAME;
127
+ }
128
+ const builtin = BUILTIN_PROFILES[effectiveName] ?? BUILTIN_PROFILES[FALLBACK_PROFILE_NAME];
129
+ const userOverride = config.profiles?.improve?.[effectiveName] ?? {};
130
+ return deepMerge(builtin, userOverride);
131
+ }
132
+ export function shouldSkipRef(ref, processName, profile) {
133
+ const cfg = profile.processes?.[processName];
134
+ // Check if the process itself is disabled
135
+ if (cfg?.enabled === false)
136
+ return { skip: true, reason: "process-disabled" };
137
+ const parsed = parseAssetRef(ref);
138
+ const allowed = cfg?.allowedTypes ?? DEFAULT_ALLOWED_TYPES[processName];
139
+ if (!allowed.includes(parsed.type))
140
+ return { skip: true, reason: "type-filter" };
141
+ // Hardcoded: wiki raw directories are never processed by any improve process.
142
+ if (parsed.type === "wiki" && parsed.name.split("/")[1] === "raw") {
143
+ return { skip: true, reason: "raw-wiki" };
144
+ }
145
+ return { skip: false, reason: "" };
146
+ }
@@ -0,0 +1,103 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Helpers for persisting the `akm improve` result envelope.
6
+ *
7
+ * v0.8.0 behavioural default change:
8
+ * - Default: the full result is recorded as a single row in the
9
+ * `improve_runs` table of `state.db` (migration 003). Stdout is empty.
10
+ * The existing `[improve] ...` progress log lines on stderr remain the
11
+ * canonical console UX.
12
+ * - `--json-to-stdout` restores the prior behaviour: full JSON to stdout,
13
+ * nothing written to state.db.
14
+ *
15
+ * v0.8.0 storage change (this module): the previous on-disk artifact at
16
+ * `<stash>/.akm/runs/<runId>/improve-result.json` is no longer written. The
17
+ * canonical record now lives in `improve_runs` (see
18
+ * `src/core/state-db.ts`). Pre-existing files from older runs are not
19
+ * deleted by this change — they become historical artifacts. Zero current
20
+ * code paths read them, so no consumers needed to update.
21
+ *
22
+ * Run-id format: ISO-8601 timestamp (colons/dots replaced by `-`) plus an
23
+ * 8-char hex random suffix. There is no existing canonical run-id helper for
24
+ * persistent per-command artefacts on disk — the `workflow_runs` table uses
25
+ * `randomUUID()` but is database-scoped, and `consolidate-journal.json` is a
26
+ * single-slot artefact. We mint a fresh timestamped id for each improve run.
27
+ */
28
+ import crypto from "node:crypto";
29
+ import path from "node:path";
30
+ import { openStateDatabase, recordImproveRun } from "../core/state-db";
31
+ /**
32
+ * Build a stable run-id for a single improve invocation.
33
+ *
34
+ * Shape: `<iso-8601-utc-with-dashes>-<8 hex chars>`, e.g.
35
+ * `2026-05-19T17-30-22-123Z-a1b2c3d4`.
36
+ *
37
+ * The hex suffix protects against same-millisecond collisions when multiple
38
+ * runs happen back-to-back in tests or scripts.
39
+ */
40
+ export function buildImproveRunId(now = new Date()) {
41
+ const iso = now.toISOString().replace(/[:.]/g, "-");
42
+ const rand = crypto.randomBytes(4).toString("hex");
43
+ return `${iso}-${rand}`;
44
+ }
45
+ /**
46
+ * Return a stable, human-recognisable reference for a given improve run.
47
+ *
48
+ * Historical compatibility shim: callers used to receive a stash-relative
49
+ * file path like `.akm/runs/<runId>/improve-result.json`. With the state.db
50
+ * migration, no such file exists, but several callers still log "wrote to
51
+ * <path>" style messages. Returning a `state.db//improve_runs/<runId>`
52
+ * locator preserves the "the result is at <thing>" signature so existing
53
+ * log lines and error messages continue to make sense without rewriting
54
+ * every call site.
55
+ */
56
+ export function relativeImproveResultPath(runId) {
57
+ return path.join("state.db", "improve_runs", runId);
58
+ }
59
+ /**
60
+ * Persist the full improve result into the `improve_runs` table of state.db.
61
+ *
62
+ * Backwards-compatible signature: the function name, argument list, and
63
+ * return type all match the pre-0.8.0 file-writing helper. The returned
64
+ * string is the `state.db//improve_runs/<runId>` locator (see
65
+ * {@link relativeImproveResultPath}), which is intended for log messages
66
+ * only — no caller should treat it as a filesystem path. Zero current
67
+ * readers existed for the previous file path, so this is a pure storage
68
+ * swap.
69
+ *
70
+ * The state.db row carries the scope and dry-run flag from `result.scope`
71
+ * and `result.dryRun`, plus the full result JSON for full fidelity. The
72
+ * dry-run column is indexed so productivity audits can filter cleanly
73
+ * (closes the dry-run/real-run artifact-trap recorded in MEMORY.md
74
+ * `feedback_akm_dryrun_artifact_trap`).
75
+ */
76
+ export function writeImproveResultFile(stashDir, runId, result) {
77
+ const db = openStateDatabase();
78
+ try {
79
+ const startedAt = new Date().toISOString();
80
+ recordImproveRun(db, {
81
+ id: runId,
82
+ startedAt,
83
+ completedAt: startedAt,
84
+ stashDir,
85
+ dryRun: Boolean(result.dryRun),
86
+ profile: null,
87
+ scopeMode: result.scope?.mode ?? "all",
88
+ scopeValue: result.scope?.value ?? null,
89
+ guidance: result.guidance ?? null,
90
+ ok: Boolean(result.ok),
91
+ result,
92
+ });
93
+ }
94
+ finally {
95
+ try {
96
+ db.close();
97
+ }
98
+ catch {
99
+ // best-effort
100
+ }
101
+ }
102
+ return relativeImproveResultPath(runId);
103
+ }