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
@@ -0,0 +1,400 @@
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 { spawnSync } from "node:child_process";
5
+ import fs from "node:fs";
6
+ import { loadConfig } from "../core/config";
7
+ import { ConfigError, UsageError } from "../core/errors";
8
+ import { appendEvent, readEvents } from "../core/events";
9
+ import { getStateDbPathInDataDir } from "../core/paths";
10
+ import { openStateDatabase, queryTaskHistory } from "../core/state-db";
11
+ import { parseSinceToIso } from "../core/time";
12
+ import { readSemanticStatus } from "../indexer/semantic-status";
13
+ import { detectAgentCliProfiles, requireAgentProfile } from "../integrations/agent";
14
+ import { getExecutionLogCandidates } from "../integrations/session-logs";
15
+ const DEFAULT_SINCE_MS = 24 * 60 * 60 * 1000;
16
+ const IMPROVE_COMPLETED_EVENT = "improve_completed";
17
+ const HEALTH_PROBE_EVENT = "health_probe";
18
+ const ACTIVE_RUN_WARN_MS = 15 * 60 * 1000;
19
+ export function parseHealthSince(since) {
20
+ if (since === undefined || since.trim() === "") {
21
+ return new Date(Date.now() - DEFAULT_SINCE_MS).toISOString();
22
+ }
23
+ const trimmed = since.trim();
24
+ const durationMatch = trimmed.match(/^(\d+)([dhm])$/i);
25
+ if (durationMatch) {
26
+ const amount = Number.parseInt(durationMatch[1] ?? "0", 10);
27
+ const unit = (durationMatch[2] ?? "d").toLowerCase();
28
+ if (!Number.isFinite(amount) || amount < 0) {
29
+ throw new UsageError("--since must be a non-negative duration or timestamp.", "INVALID_FLAG_VALUE");
30
+ }
31
+ const multiplier = unit === "h" ? 60 * 60 * 1000 : unit === "m" ? 30 * 24 * 60 * 60 * 1000 : 24 * 60 * 60 * 1000;
32
+ return new Date(Date.now() - amount * multiplier).toISOString();
33
+ }
34
+ return parseSinceToIso(trimmed);
35
+ }
36
+ function roundRate(value) {
37
+ return Number(value.toFixed(4));
38
+ }
39
+ function parseTaskMetadata(row) {
40
+ try {
41
+ return JSON.parse(row.metadata_json);
42
+ }
43
+ catch {
44
+ return {};
45
+ }
46
+ }
47
+ function createUnknownImproveMetrics() {
48
+ return {
49
+ invoked: 0,
50
+ completed: 0,
51
+ skipped: 0,
52
+ skipReasons: {},
53
+ plannedRefs: 0,
54
+ actions: {
55
+ reflect: 0,
56
+ distill: 0,
57
+ distillSkipped: 0,
58
+ memoryPrune: 0,
59
+ memoryInference: 0,
60
+ graphExtraction: 0,
61
+ error: 0,
62
+ },
63
+ reflectsWithErrorContext: 0,
64
+ coverageGapCount: 0,
65
+ executionLogCandidateCount: 0,
66
+ evalCasesWritten: 0,
67
+ deadUrlCount: 0,
68
+ memorySummary: { eligible: 0, derived: 0 },
69
+ memoryCleanup: {
70
+ pruneCandidates: 0,
71
+ contradictionCandidates: 0,
72
+ beliefStateTransitions: 0,
73
+ consolidationCandidates: 0,
74
+ archived: 0,
75
+ warnings: 0,
76
+ },
77
+ consolidation: { ran: false, processed: 0, durationMs: 0 },
78
+ memoryInference: { ran: false, writes: 0, durationMs: 0 },
79
+ graphExtraction: { ran: false, extractedFiles: 0, durationMs: 0 },
80
+ };
81
+ }
82
+ function toFiniteNumber(value) {
83
+ if (typeof value === "number" && Number.isFinite(value))
84
+ return value;
85
+ if (typeof value === "string" && value.trim()) {
86
+ const parsed = Number(value);
87
+ if (Number.isFinite(parsed))
88
+ return parsed;
89
+ }
90
+ return 0;
91
+ }
92
+ function summarizeImproveCompleted(events) {
93
+ const metrics = createUnknownImproveMetrics();
94
+ metrics.completed = events.length;
95
+ for (const event of events) {
96
+ const meta = event.metadata ?? {};
97
+ metrics.plannedRefs += toFiniteNumber(meta.plannedRefs);
98
+ metrics.actions.reflect += toFiniteNumber(meta.reflectActions);
99
+ metrics.actions.distill += toFiniteNumber(meta.distillActions);
100
+ metrics.actions.distillSkipped += toFiniteNumber(meta.distillSkippedActions);
101
+ metrics.actions.memoryPrune += toFiniteNumber(meta.memoryPruneActions);
102
+ metrics.actions.memoryInference += toFiniteNumber(meta.memoryInferenceActions);
103
+ metrics.actions.graphExtraction += toFiniteNumber(meta.graphExtractionActions);
104
+ metrics.actions.error += toFiniteNumber(meta.errorActions);
105
+ metrics.reflectsWithErrorContext += toFiniteNumber(meta.reflectsWithErrorContext);
106
+ metrics.coverageGapCount += toFiniteNumber(meta.coverageGapCount);
107
+ metrics.executionLogCandidateCount += toFiniteNumber(meta.executionLogCandidateCount);
108
+ metrics.evalCasesWritten += toFiniteNumber(meta.evalCasesWritten);
109
+ metrics.deadUrlCount += toFiniteNumber(meta.deadUrlCount);
110
+ metrics.memorySummary.eligible += toFiniteNumber(meta.memoryEligible);
111
+ metrics.memorySummary.derived += toFiniteNumber(meta.memoryDerived);
112
+ metrics.memoryCleanup.pruneCandidates += toFiniteNumber(meta.memoryCleanupPruneCandidates);
113
+ metrics.memoryCleanup.contradictionCandidates += toFiniteNumber(meta.memoryCleanupContradictionCandidates);
114
+ metrics.memoryCleanup.beliefStateTransitions += toFiniteNumber(meta.memoryCleanupBeliefStateTransitions);
115
+ metrics.memoryCleanup.consolidationCandidates += toFiniteNumber(meta.memoryCleanupConsolidationCandidates);
116
+ metrics.memoryCleanup.archived += toFiniteNumber(meta.memoryCleanupArchived);
117
+ metrics.memoryCleanup.warnings += toFiniteNumber(meta.memoryCleanupWarnings);
118
+ metrics.consolidation.processed += toFiniteNumber(meta.consolidationProcessed);
119
+ metrics.consolidation.durationMs += toFiniteNumber(meta.consolidationDurationMs);
120
+ metrics.memoryInference.writes += toFiniteNumber(meta.memoryInferenceWrites);
121
+ metrics.memoryInference.durationMs += toFiniteNumber(meta.memoryInferenceDurationMs);
122
+ metrics.graphExtraction.extractedFiles += toFiniteNumber(meta.graphExtractionExtractedFiles);
123
+ metrics.graphExtraction.durationMs += toFiniteNumber(meta.graphExtractionDurationMs);
124
+ }
125
+ metrics.consolidation.ran = metrics.consolidation.processed > 0 || metrics.consolidation.durationMs > 0;
126
+ metrics.memoryInference.ran = metrics.memoryInference.writes > 0 || metrics.memoryInference.durationMs > 0;
127
+ metrics.graphExtraction.ran = metrics.graphExtraction.extractedFiles > 0 || metrics.graphExtraction.durationMs > 0;
128
+ return metrics;
129
+ }
130
+ function buildImproveSkipSummary(events) {
131
+ const skipReasons = {};
132
+ for (const event of events) {
133
+ const reason = typeof event.metadata?.reason === "string" && event.metadata.reason.trim() ? event.metadata.reason : "unknown";
134
+ skipReasons[reason] = (skipReasons[reason] ?? 0) + 1;
135
+ }
136
+ return { skipped: events.length, skipReasons };
137
+ }
138
+ function probeStateDbRoundTrip(stateDbPath) {
139
+ const before = readEvents({}, { dbPath: stateDbPath }).nextOffset;
140
+ const started = Date.now();
141
+ appendEvent({ eventType: HEALTH_PROBE_EVENT, ref: "health:probe", metadata: { source: "akm health" } }, { dbPath: stateDbPath });
142
+ const after = readEvents({ sinceOffset: before, type: HEALTH_PROBE_EVENT, ref: "health:probe" }, { dbPath: stateDbPath });
143
+ const durationMs = Date.now() - started;
144
+ if (after.events.length === 0 || after.nextOffset <= before) {
145
+ return { ok: false, durationMs, error: "probe event was not readable after append" };
146
+ }
147
+ return { ok: true, durationMs };
148
+ }
149
+ function runAgentProbe() {
150
+ const config = loadConfig();
151
+ // v2: check profiles.agent first
152
+ if (config.profiles?.agent) {
153
+ const defaultName = config.defaults?.agent;
154
+ const profileCount = Object.keys(config.profiles.agent).length;
155
+ if (profileCount === 0) {
156
+ return {
157
+ name: "agent-profile",
158
+ kind: "deterministic",
159
+ status: "unknown",
160
+ confidence: "high",
161
+ message: "No agent profiles configured in profiles.agent.",
162
+ };
163
+ }
164
+ const profileName = defaultName ?? Object.keys(config.profiles.agent)[0];
165
+ const profile = config.profiles.agent[profileName];
166
+ return {
167
+ name: "agent-profile",
168
+ kind: "deterministic",
169
+ status: "pass",
170
+ confidence: "high",
171
+ message: `v2 agent profile "${profileName}" configured (platform: ${profile?.platform ?? "unknown"}).`,
172
+ evidence: { profile: profileName, platform: profile?.platform, profileCount },
173
+ };
174
+ }
175
+ if (!config.profiles?.agent && !config.defaults?.agent) {
176
+ return {
177
+ name: "agent-profile",
178
+ kind: "deterministic",
179
+ status: "unknown",
180
+ confidence: "high",
181
+ message: "No agent config present.",
182
+ };
183
+ }
184
+ let profile;
185
+ try {
186
+ profile = requireAgentProfile(config);
187
+ }
188
+ catch (error) {
189
+ return {
190
+ name: "agent-profile",
191
+ kind: "deterministic",
192
+ status: "warn",
193
+ confidence: "high",
194
+ message: error instanceof Error ? error.message : String(error),
195
+ };
196
+ }
197
+ if (profile.sdkMode === true) {
198
+ return {
199
+ name: "agent-profile",
200
+ kind: "deterministic",
201
+ status: profile.model ? "pass" : "warn",
202
+ confidence: "high",
203
+ message: profile.model
204
+ ? `SDK mode profile "${profile.name}" is configured.`
205
+ : `SDK mode profile "${profile.name}" has no explicit model.`,
206
+ evidence: { profile: profile.name, sdkMode: true, model: profile.model ?? null },
207
+ };
208
+ }
209
+ const detections = detectAgentCliProfiles(config);
210
+ const detection = detections.find((entry) => entry.name === profile.name);
211
+ if (!detection?.available) {
212
+ return {
213
+ name: "agent-profile",
214
+ kind: "deterministic",
215
+ status: "fail",
216
+ confidence: "high",
217
+ message: `Default agent profile "${profile.name}" is not available on PATH.`,
218
+ evidence: { profile: profile.name, bin: profile.bin },
219
+ };
220
+ }
221
+ const version = spawnSync(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
222
+ if ((version.status ?? 1) !== 0) {
223
+ return {
224
+ name: "agent-profile",
225
+ kind: "deterministic",
226
+ status: "warn",
227
+ confidence: "medium",
228
+ message: `Agent binary "${profile.bin}" was found but \`--version\` failed.`,
229
+ evidence: {
230
+ profile: profile.name,
231
+ bin: profile.bin,
232
+ exitCode: version.status ?? null,
233
+ stderr: (version.stderr ?? "").trim(),
234
+ },
235
+ };
236
+ }
237
+ return {
238
+ name: "agent-profile",
239
+ kind: "deterministic",
240
+ status: "pass",
241
+ confidence: "high",
242
+ message: `Agent profile "${profile.name}" is available.`,
243
+ evidence: { profile: profile.name, bin: profile.bin, version: (version.stdout ?? "").trim() },
244
+ };
245
+ }
246
+ export function akmHealth(options = {}) {
247
+ const since = parseHealthSince(options.since);
248
+ const stateDbPath = getStateDbPathInDataDir();
249
+ const hardChecks = [];
250
+ const advisories = [];
251
+ const getExecutionLogCandidatesFn = options.getExecutionLogCandidatesFn ?? getExecutionLogCandidates;
252
+ let db;
253
+ try {
254
+ db = openStateDatabase(stateDbPath);
255
+ }
256
+ catch (error) {
257
+ throw new ConfigError(`Unable to open state.db: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
258
+ }
259
+ try {
260
+ const tables = db
261
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name IN ('events', 'task_history', 'proposals', 'schema_migrations') ORDER BY name")
262
+ .all();
263
+ const tableNames = tables.map((row) => row.name).sort();
264
+ const requiredTables = ["events", "proposals", "schema_migrations", "task_history"];
265
+ const missingTables = requiredTables.filter((name) => !tableNames.includes(name));
266
+ hardChecks.push({
267
+ name: "state-db-schema",
268
+ kind: "deterministic",
269
+ status: missingTables.length === 0 ? "pass" : "fail",
270
+ confidence: "high",
271
+ message: missingTables.length === 0
272
+ ? "state.db opened and required tables are present."
273
+ : `state.db is missing required tables: ${missingTables.join(", ")}`,
274
+ evidence: { path: stateDbPath, tables: tableNames },
275
+ });
276
+ const probe = probeStateDbRoundTrip(stateDbPath);
277
+ hardChecks.push({
278
+ name: "state-db-round-trip",
279
+ kind: "deterministic",
280
+ status: probe.ok ? "pass" : "fail",
281
+ confidence: "high",
282
+ message: probe.ok ? "state.db append/read round-trip succeeded." : `state.db round-trip failed: ${probe.error}`,
283
+ evidence: { path: stateDbPath, durationMs: probe.durationMs },
284
+ });
285
+ const taskRows = queryTaskHistory(db, { since });
286
+ const taskRowsWithLogs = taskRows.filter((row) => row.log_path !== null);
287
+ const existingLogRows = taskRowsWithLogs.filter((row) => row.log_path && fs.existsSync(row.log_path));
288
+ const failedTaskRows = taskRows.filter((row) => row.status === "failed");
289
+ const activeRows = taskRows.filter((row) => row.status === "active");
290
+ const stuckActiveRuns = activeRows.filter((row) => Date.now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS).length;
291
+ const promptRows = taskRows.filter((row) => row.target_kind === "prompt");
292
+ const promptFailures = promptRows.filter((row) => {
293
+ const detail = parseTaskMetadata(row).detail;
294
+ return typeof detail?.reason === "string" && detail.reason.length > 0;
295
+ });
296
+ const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
297
+ const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
298
+ const agentFailureRate = promptRows.length === 0 ? 0 : promptFailures.length / promptRows.length;
299
+ hardChecks.push({
300
+ name: "task-history-read",
301
+ kind: "deterministic",
302
+ status: "pass",
303
+ confidence: "high",
304
+ message: `Read ${taskRows.length} task-history row(s) since ${since}.`,
305
+ evidence: { rows: taskRows.length, since },
306
+ });
307
+ hardChecks.push({
308
+ name: "task-log-backing",
309
+ kind: "deterministic",
310
+ status: logBackingRate === 1 ? "pass" : "fail",
311
+ confidence: "high",
312
+ message: logBackingRate === 1
313
+ ? "Every task_history log_path resolved on disk."
314
+ : `${taskRowsWithLogs.length - existingLogRows.length} task log(s) referenced in task_history are missing.`,
315
+ evidence: { totalWithLogs: taskRowsWithLogs.length, existingLogs: existingLogRows.length },
316
+ });
317
+ hardChecks.push({
318
+ name: "active-runs",
319
+ kind: "deterministic",
320
+ status: stuckActiveRuns === 0 ? "pass" : "warn",
321
+ confidence: "high",
322
+ message: stuckActiveRuns === 0
323
+ ? "No active task runs exceeded the stale threshold."
324
+ : `${stuckActiveRuns} active task run(s) are older than ${Math.round(ACTIVE_RUN_WARN_MS / 60000)} minutes.`,
325
+ evidence: { stuckActiveRuns },
326
+ });
327
+ hardChecks.push(runAgentProbe());
328
+ const semanticStatus = readSemanticStatus();
329
+ advisories.push({
330
+ name: "semantic-search-runtime",
331
+ kind: "deterministic",
332
+ status: !semanticStatus ||
333
+ semanticStatus.status === "pending" ||
334
+ semanticStatus.status === "ready-js" ||
335
+ semanticStatus.status === "ready-vec"
336
+ ? "pass"
337
+ : "warn",
338
+ confidence: "medium",
339
+ message: semanticStatus
340
+ ? `Semantic search status: ${semanticStatus.status}`
341
+ : "No semantic-search runtime status recorded yet.",
342
+ evidence: semanticStatus ? { ...semanticStatus } : undefined,
343
+ });
344
+ const improveInvoked = readEvents({ since, type: "improve_invoked" }, { dbPath: stateDbPath }).events.length;
345
+ const improveCompletedEvents = readEvents({ since, type: IMPROVE_COMPLETED_EVENT }, { dbPath: stateDbPath }).events;
346
+ const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events;
347
+ const improveSummary = summarizeImproveCompleted(improveCompletedEvents);
348
+ improveSummary.invoked = improveInvoked;
349
+ const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
350
+ improveSummary.skipped = skipSummary.skipped;
351
+ improveSummary.skipReasons = skipSummary.skipReasons;
352
+ let sessionLogEntries = [];
353
+ try {
354
+ const sinceDays = Math.max(0, Math.ceil((Date.now() - new Date(since).getTime()) / (24 * 60 * 60 * 1000)));
355
+ sessionLogEntries = getExecutionLogCandidatesFn(sinceDays).map((entry) => ({
356
+ topic: entry.topic,
357
+ frequency: entry.frequency,
358
+ source: entry.source,
359
+ isFailurePattern: entry.isFailurePattern,
360
+ }));
361
+ }
362
+ catch {
363
+ sessionLogEntries = [];
364
+ }
365
+ advisories.push({
366
+ name: "session-log-failures",
367
+ kind: "heuristic",
368
+ status: sessionLogEntries.length === 0 ? "pass" : "warn",
369
+ confidence: sessionLogEntries.length === 0 ? "low" : "medium",
370
+ message: sessionLogEntries.length === 0
371
+ ? "No repeated external session-log failure patterns were detected."
372
+ : `${sessionLogEntries.length} repeated external session-log failure pattern(s) detected.`,
373
+ evidence: { candidates: sessionLogEntries.slice(0, 5) },
374
+ });
375
+ const metrics = {
376
+ taskFailRate: roundRate(taskFailRate),
377
+ agentFailureRate: roundRate(agentFailureRate),
378
+ stuckActiveRuns,
379
+ logBackingRate: roundRate(logBackingRate),
380
+ probeRoundTripMs: probe.durationMs,
381
+ };
382
+ const hardFailure = hardChecks.some((check) => check.status === "fail");
383
+ const deterministicWarnings = [...hardChecks, ...advisories].some((check) => check.status === "warn" && check.kind === "deterministic");
384
+ const status = hardFailure ? "fail" : deterministicWarnings ? "warn" : "pass";
385
+ return {
386
+ schemaVersion: 1,
387
+ ok: !hardFailure,
388
+ status,
389
+ since,
390
+ hardChecks,
391
+ advisories,
392
+ metrics,
393
+ improve: improveSummary,
394
+ sessionLogAdvisories: sessionLogEntries,
395
+ };
396
+ }
397
+ finally {
398
+ db.close();
399
+ }
400
+ }
@@ -0,0 +1,9 @@
1
+ Usage:
2
+ akm accept <id>
3
+
4
+ Description:
5
+ Accept a proposal and promote it into the stash.
6
+
7
+ Examples:
8
+ akm accept proposal_123
9
+ akm accept proposal_123 --target team-stash
@@ -0,0 +1,77 @@
1
+ Usage:
2
+ akm improve
3
+ akm improve <type>
4
+ akm improve <ref>
5
+
6
+ Description:
7
+ Analyze existing AKM assets and generate improvement proposals.
8
+
9
+ Modes:
10
+ akm improve
11
+ Improve all eligible assets in the current scope.
12
+
13
+ akm improve <type>
14
+ Improve all assets of a given type.
15
+ Example: akm improve memory
16
+
17
+ akm improve <ref>
18
+ Improve one specific asset.
19
+ Example: akm improve workflow:release-checklist
20
+
21
+ What it does:
22
+ - reviews feedback and recent history
23
+ - proposes edits to existing assets
24
+ - distills lessons where useful
25
+ - promotes durable skill lessons into skill reference-doc proposals when justified
26
+ - cleans and consolidates memories
27
+ - writes results to the proposal queue
28
+
29
+ Options:
30
+ --task <text> Add extra guidance for this improvement pass
31
+ --dry-run Show planned actions without generating proposals
32
+ --target <source> Override the write target for accepted proposals
33
+ --auto-accept[=<value>]
34
+ Confidence threshold (0-100) for auto-accepting proposals.
35
+ Default when flag is absent: ON at threshold 90 (all sub-processes).
36
+ --auto-accept same as --auto-accept=90
37
+ --auto-accept=<N> integer 0-100; accept proposals at or above N
38
+ --auto-accept=safe alias for 90 (back-compat, not deprecated)
39
+ --auto-accept=false disable auto-accept for all sub-processes;
40
+ reflect/distill proposals go to the queue and
41
+ consolidation will prompt interactively on HTTP paths
42
+ Note: until proposals carry real confidence scores, any non-`false`
43
+ value behaves like the legacy "safe" mode (whole-batch auto-accept).
44
+ --profile <name> Improve profile to apply. Built-ins: default, quick,
45
+ thorough, memory-focus. User-defined profiles under
46
+ `profiles.improve.<name>` in config are also accepted.
47
+ Profiles bundle process gating, type filters,
48
+ cooldown overrides, and run-level autoAccept/limit
49
+ defaults. Falls back to `defaults.improve` in config,
50
+ then to "default". Unknown names fall back to default
51
+ with a warning.
52
+ --ignore-cooldown Disable reflect/distill/consolidate cooldown checks for this run
53
+ --reflect-cooldown-days <n>
54
+ Override reflect cooldown with a non-negative integer
55
+ --distill-cooldown-days <n>
56
+ Override distill cooldown with a non-negative integer
57
+ --consolidate-cooldown-days <n>
58
+ Override consolidate cooldown with a non-negative integer
59
+ --consolidate-recovery <mode>
60
+ Recovery mode for stale consolidate journals: abort (default) or clean
61
+ --require-feedback-signal
62
+ Only process refs with recent feedback signal events
63
+ --min-retrieval-count <n>
64
+ Retrieval fallback threshold when no recent feedback exists (default: 5)
65
+ --json-to-stdout Emit the full JSON result on stdout (legacy behaviour).
66
+ (0.8.0+: full result is recorded in the improve_runs table of
67
+ state.db and stdout is empty; use --json-to-stdout for the prior
68
+ behaviour, e.g. `akm improve --json-to-stdout | jq`.)
69
+
70
+ Examples:
71
+ akm improve
72
+ akm improve memory
73
+ akm improve skill
74
+ akm improve skill:code-review
75
+ akm improve workflow:incident-response --task "reduce duplication"
76
+ akm improve --profile quick
77
+ akm improve --profile memory-focus
@@ -0,0 +1,15 @@
1
+ Usage:
2
+ akm proposals
3
+
4
+ Description:
5
+ List proposal queue entries.
6
+
7
+ Options:
8
+ --status <status> Filter by pending, accepted, or rejected
9
+ --type <type> Filter by asset type
10
+ --ref <ref> Filter by exact asset ref
11
+
12
+ Examples:
13
+ akm proposals
14
+ akm proposals --status pending
15
+ akm proposals --type skill
@@ -0,0 +1,17 @@
1
+ Usage:
2
+ akm propose <type> <name> --task "..."
3
+ akm propose <type> <name> --file <path>
4
+
5
+ Description:
6
+ Create a proposal for a brand-new AKM asset.
7
+
8
+ Input:
9
+ --task <text> Inline task or prompt text
10
+ --file <path> Read task or prompt text from a file
11
+
12
+ Rules:
13
+ Exactly one of --task or --file is required.
14
+
15
+ Examples:
16
+ akm propose skill release-auditor --task "review release artifacts before publish"
17
+ akm propose workflow hotfix-triage --file ./prompts/hotfix-triage.md
@@ -0,0 +1,8 @@
1
+ Usage:
2
+ akm reject <id> --reason "..."
3
+
4
+ Description:
5
+ Reject a proposal and record the reason.
6
+
7
+ Examples:
8
+ akm reject proposal_123 --reason "duplicates existing workflow"