akm-cli 0.8.0-rc2 → 0.8.1

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 (313) hide show
  1. package/{.github/CHANGELOG.md → CHANGELOG.md} +238 -3
  2. package/README.md +22 -6
  3. package/SECURITY.md +93 -0
  4. package/dist/assets/help/help-accept.md +12 -0
  5. package/dist/assets/help/help-improve.md +81 -0
  6. package/dist/{commands → assets}/help/help-proposals.md +7 -4
  7. package/dist/assets/help/help-reject.md +11 -0
  8. package/dist/{output → assets/hints}/cli-hints-full.md +60 -32
  9. package/dist/{output → assets/hints}/cli-hints-short.md +10 -7
  10. package/dist/assets/profiles/default.json +15 -0
  11. package/dist/assets/profiles/graph-refresh.json +13 -0
  12. package/dist/assets/profiles/memory-focus.json +12 -0
  13. package/dist/assets/profiles/quick.json +15 -0
  14. package/dist/assets/profiles/thorough.json +15 -0
  15. package/dist/assets/prompts/extract-session.md +80 -0
  16. package/dist/assets/prompts/graph-extract-user-prompt.md +35 -0
  17. package/dist/assets/tasks/graph-refresh-weekly.yml +10 -0
  18. package/dist/cli/config-migrate.js +144 -0
  19. package/dist/cli/config-validate.js +39 -0
  20. package/dist/cli/confirm.js +73 -0
  21. package/dist/cli/parse-args.js +93 -3
  22. package/dist/cli/shared.js +129 -0
  23. package/dist/cli.js +2141 -1268
  24. package/dist/commands/add-cli.js +279 -0
  25. package/dist/commands/agent-dispatch.js +20 -12
  26. package/dist/commands/agent-support.js +11 -5
  27. package/dist/commands/completions.js +3 -0
  28. package/dist/commands/config-cli.js +129 -517
  29. package/dist/commands/consolidate.js +1557 -147
  30. package/dist/commands/curate.js +44 -3
  31. package/dist/commands/db-cli.js +23 -0
  32. package/dist/commands/distill-promotion-policy.js +5 -3
  33. package/dist/commands/distill.js +906 -100
  34. package/dist/commands/env.js +213 -0
  35. package/dist/commands/eval-cases.js +3 -0
  36. package/dist/commands/events.js +3 -0
  37. package/dist/commands/extract-cli.js +127 -0
  38. package/dist/commands/extract-prompt.js +217 -0
  39. package/dist/commands/extract.js +477 -0
  40. package/dist/commands/feedback-cli.js +331 -0
  41. package/dist/commands/graph.js +260 -5
  42. package/dist/commands/health.js +1042 -55
  43. package/dist/commands/history.js +51 -16
  44. package/dist/commands/improve-auto-accept.js +97 -0
  45. package/dist/commands/improve-cli.js +236 -0
  46. package/dist/commands/improve-profiles.js +138 -0
  47. package/dist/commands/improve-result-file.js +167 -0
  48. package/dist/commands/improve.js +1736 -346
  49. package/dist/commands/info.js +26 -28
  50. package/dist/commands/init.js +49 -1
  51. package/dist/commands/installed-stashes.js +6 -23
  52. package/dist/commands/knowledge.js +3 -0
  53. package/dist/commands/lint/agent-linter.js +3 -0
  54. package/dist/commands/lint/base-linter.js +199 -5
  55. package/dist/commands/lint/command-linter.js +3 -0
  56. package/dist/commands/lint/default-linter.js +3 -0
  57. package/dist/commands/lint/env-key-rules.js +154 -0
  58. package/dist/commands/lint/index.js +92 -3
  59. package/dist/commands/lint/knowledge-linter.js +3 -0
  60. package/dist/commands/lint/markdown-insertion.js +343 -0
  61. package/dist/commands/lint/memory-linter.js +3 -0
  62. package/dist/commands/lint/registry.js +3 -0
  63. package/dist/commands/lint/skill-linter.js +3 -0
  64. package/dist/commands/lint/task-linter.js +15 -12
  65. package/dist/commands/lint/types.js +3 -0
  66. package/dist/commands/lint/workflow-linter.js +3 -0
  67. package/dist/commands/lint.js +3 -0
  68. package/dist/commands/migration-help.js +5 -2
  69. package/dist/commands/proposal-drain-policies.js +128 -0
  70. package/dist/commands/proposal-drain.js +477 -0
  71. package/dist/commands/proposal.js +60 -6
  72. package/dist/commands/propose.js +24 -19
  73. package/dist/commands/reflect.js +1004 -94
  74. package/dist/commands/registry-cli.js +150 -0
  75. package/dist/commands/registry-search.js +3 -0
  76. package/dist/commands/remember-cli.js +257 -0
  77. package/dist/commands/remember.js +15 -6
  78. package/dist/commands/schema-repair.js +88 -15
  79. package/dist/commands/search.js +99 -14
  80. package/dist/commands/secret.js +173 -0
  81. package/dist/commands/self-update.js +3 -0
  82. package/dist/commands/show.js +32 -13
  83. package/dist/commands/source-add.js +7 -35
  84. package/dist/commands/source-clone.js +3 -0
  85. package/dist/commands/source-manage.js +3 -0
  86. package/dist/commands/tasks.js +161 -95
  87. package/dist/commands/url-checker.js +3 -0
  88. package/dist/core/action-contributors.js +3 -0
  89. package/dist/core/asset-ref.js +13 -2
  90. package/dist/core/asset-registry.js +9 -2
  91. package/dist/core/asset-serialize.js +88 -0
  92. package/dist/core/asset-spec.js +61 -5
  93. package/dist/core/common.js +93 -5
  94. package/dist/core/concurrent.js +3 -0
  95. package/dist/core/config-io.js +347 -0
  96. package/dist/core/config-migration.js +622 -0
  97. package/dist/core/config-schema.js +558 -0
  98. package/dist/core/config-sources.js +108 -0
  99. package/dist/core/config-types.js +4 -0
  100. package/dist/core/config-walker.js +337 -0
  101. package/dist/core/config.js +366 -1077
  102. package/dist/core/errors.js +42 -20
  103. package/dist/core/events.js +31 -25
  104. package/dist/core/file-lock.js +104 -0
  105. package/dist/core/frontmatter.js +75 -10
  106. package/dist/core/lesson-lint.js +3 -0
  107. package/dist/core/markdown.js +3 -0
  108. package/dist/core/memory-belief.js +62 -0
  109. package/dist/core/memory-contradiction-detect.js +274 -0
  110. package/dist/core/memory-improve.js +142 -14
  111. package/dist/core/parse.js +3 -0
  112. package/dist/core/paths.js +218 -50
  113. package/dist/core/proposal-quality-validators.js +380 -0
  114. package/dist/core/proposal-validators.js +11 -3
  115. package/dist/core/proposals.js +464 -5
  116. package/dist/core/state-db.js +349 -56
  117. package/dist/core/text-truncation.js +107 -0
  118. package/dist/core/time.js +3 -0
  119. package/dist/core/tty.js +59 -0
  120. package/dist/core/warn.js +7 -2
  121. package/dist/core/write-source.js +12 -0
  122. package/dist/indexer/db-backup.js +391 -0
  123. package/dist/indexer/db-search.js +136 -28
  124. package/dist/indexer/db.js +661 -166
  125. package/dist/indexer/ensure-index.js +3 -0
  126. package/dist/indexer/file-context.js +3 -0
  127. package/dist/indexer/graph-boost.js +162 -40
  128. package/dist/indexer/graph-db.js +241 -51
  129. package/dist/indexer/graph-dedup.js +3 -7
  130. package/dist/indexer/graph-extraction.js +242 -149
  131. package/dist/indexer/index-context.js +3 -9
  132. package/dist/indexer/indexer.js +86 -16
  133. package/dist/indexer/llm-cache.js +24 -19
  134. package/dist/indexer/manifest.js +3 -0
  135. package/dist/indexer/matchers.js +184 -11
  136. package/dist/indexer/memory-inference.js +94 -50
  137. package/dist/indexer/metadata-contributors.js +3 -0
  138. package/dist/indexer/metadata.js +110 -50
  139. package/dist/indexer/path-resolver.js +3 -0
  140. package/dist/indexer/project-context.js +192 -0
  141. package/dist/indexer/ranking-contributors.js +134 -7
  142. package/dist/indexer/ranking.js +8 -1
  143. package/dist/indexer/search-fields.js +5 -9
  144. package/dist/indexer/search-hit-enrichers.js +91 -2
  145. package/dist/indexer/search-source.js +20 -1
  146. package/dist/indexer/semantic-status.js +4 -1
  147. package/dist/indexer/staleness-detect.js +447 -0
  148. package/dist/indexer/usage-events.js +12 -9
  149. package/dist/indexer/walker.js +3 -0
  150. package/dist/integrations/agent/builders.js +135 -0
  151. package/dist/integrations/agent/config.js +121 -401
  152. package/dist/integrations/agent/detect.js +3 -0
  153. package/dist/integrations/agent/index.js +6 -14
  154. package/dist/integrations/agent/model-aliases.js +55 -0
  155. package/dist/integrations/agent/profiles.js +3 -0
  156. package/dist/integrations/agent/prompts.js +137 -8
  157. package/dist/integrations/agent/runner.js +208 -0
  158. package/dist/integrations/agent/sdk-runner.js +8 -2
  159. package/dist/integrations/agent/spawn.js +54 -14
  160. package/dist/integrations/github.js +3 -0
  161. package/dist/integrations/lockfile.js +22 -51
  162. package/dist/integrations/session-logs/index.js +4 -0
  163. package/dist/integrations/session-logs/inline-refs.js +35 -0
  164. package/dist/integrations/session-logs/pre-filter.js +152 -0
  165. package/dist/integrations/session-logs/providers/claude-code.js +226 -0
  166. package/dist/integrations/session-logs/providers/opencode.js +231 -25
  167. package/dist/integrations/session-logs/types.js +3 -0
  168. package/dist/llm/call-ai.js +14 -26
  169. package/dist/llm/client.js +16 -2
  170. package/dist/llm/embedder.js +20 -29
  171. package/dist/llm/embedders/cache.js +3 -7
  172. package/dist/llm/embedders/local.js +42 -1
  173. package/dist/llm/embedders/remote.js +20 -8
  174. package/dist/llm/embedders/types.js +3 -7
  175. package/dist/llm/feature-gate.js +92 -56
  176. package/dist/llm/graph-extract.js +402 -31
  177. package/dist/llm/index-passes.js +44 -29
  178. package/dist/llm/memory-infer.js +30 -2
  179. package/dist/llm/metadata-enhance.js +3 -7
  180. package/dist/output/cli-hints.js +7 -4
  181. package/dist/output/context.js +60 -8
  182. package/dist/output/renderers.js +170 -194
  183. package/dist/output/shapes/curate.js +56 -0
  184. package/dist/output/shapes/distill.js +10 -0
  185. package/dist/output/shapes/env-list.js +19 -0
  186. package/dist/output/shapes/events.js +11 -0
  187. package/dist/output/shapes/helpers.js +424 -0
  188. package/dist/output/shapes/history.js +7 -0
  189. package/dist/output/shapes/passthrough.js +105 -0
  190. package/dist/output/shapes/proposal-accept.js +7 -0
  191. package/dist/output/shapes/proposal-diff.js +7 -0
  192. package/dist/output/shapes/proposal-list.js +7 -0
  193. package/dist/output/shapes/proposal-producer.js +11 -0
  194. package/dist/output/shapes/proposal-reject.js +7 -0
  195. package/dist/output/shapes/proposal-show.js +7 -0
  196. package/dist/output/shapes/registry-search.js +6 -0
  197. package/dist/output/shapes/registry.js +30 -0
  198. package/dist/output/shapes/search.js +6 -0
  199. package/dist/output/shapes/secret-list.js +19 -0
  200. package/dist/output/shapes/show.js +6 -0
  201. package/dist/output/shapes/vault-list.js +19 -0
  202. package/dist/output/shapes.js +51 -549
  203. package/dist/output/text/add.js +6 -0
  204. package/dist/output/text/clone.js +6 -0
  205. package/dist/output/text/config.js +6 -0
  206. package/dist/output/text/curate.js +6 -0
  207. package/dist/output/text/distill.js +7 -0
  208. package/dist/output/text/enable-disable.js +7 -0
  209. package/dist/output/text/events.js +10 -0
  210. package/dist/output/text/feedback.js +6 -0
  211. package/dist/output/text/helpers.js +1059 -0
  212. package/dist/output/text/history.js +7 -0
  213. package/dist/output/text/import.js +6 -0
  214. package/dist/output/text/index.js +6 -0
  215. package/dist/output/text/info.js +6 -0
  216. package/dist/output/text/init.js +6 -0
  217. package/dist/output/text/list.js +6 -0
  218. package/dist/output/text/proposal-producer.js +8 -0
  219. package/dist/output/text/proposal.js +12 -0
  220. package/dist/output/text/registry-commands.js +11 -0
  221. package/dist/output/text/registry.js +30 -0
  222. package/dist/output/text/remember.js +6 -0
  223. package/dist/output/text/remove.js +6 -0
  224. package/dist/output/text/save.js +6 -0
  225. package/dist/output/text/search.js +6 -0
  226. package/dist/output/text/show.js +6 -0
  227. package/dist/output/text/update.js +6 -0
  228. package/dist/output/text/upgrade.js +6 -0
  229. package/dist/output/text/vault.js +16 -0
  230. package/dist/output/text/wiki.js +15 -0
  231. package/dist/output/text/workflow.js +14 -0
  232. package/dist/output/text.js +44 -1329
  233. package/dist/registry/build-index.js +3 -0
  234. package/dist/registry/create-provider-registry.js +3 -0
  235. package/dist/registry/factory.js +4 -1
  236. package/dist/registry/origin-resolve.js +3 -0
  237. package/dist/registry/providers/index.js +3 -0
  238. package/dist/registry/providers/skills-sh.js +11 -2
  239. package/dist/registry/providers/static-index.js +10 -1
  240. package/dist/registry/providers/types.js +3 -24
  241. package/dist/registry/resolve.js +11 -16
  242. package/dist/registry/types.js +3 -0
  243. package/dist/scripts/migrate-storage.js +17767 -0
  244. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
  245. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  246. package/dist/setup/detect.js +3 -0
  247. package/dist/setup/ripgrep-install.js +3 -0
  248. package/dist/setup/ripgrep-resolve.js +3 -0
  249. package/dist/setup/setup.js +306 -67
  250. package/dist/setup/steps.js +3 -15
  251. package/dist/sources/include.js +3 -0
  252. package/dist/sources/provider-factory.js +3 -11
  253. package/dist/sources/provider.js +3 -20
  254. package/dist/sources/providers/filesystem.js +19 -23
  255. package/dist/sources/providers/git.js +171 -21
  256. package/dist/sources/providers/index.js +3 -0
  257. package/dist/sources/providers/install-types.js +3 -13
  258. package/dist/sources/providers/npm.js +3 -4
  259. package/dist/sources/providers/provider-utils.js +3 -0
  260. package/dist/sources/providers/sync-from-ref.js +3 -11
  261. package/dist/sources/providers/tar-utils.js +3 -0
  262. package/dist/sources/providers/website.js +18 -22
  263. package/dist/sources/resolve.js +3 -0
  264. package/dist/sources/types.js +3 -0
  265. package/dist/sources/website-ingest.js +3 -0
  266. package/dist/tasks/backends/cron.js +3 -0
  267. package/dist/tasks/backends/exec-utils.js +3 -0
  268. package/dist/tasks/backends/index.js +3 -11
  269. package/dist/tasks/backends/launchd.js +4 -1
  270. package/dist/tasks/backends/schtasks.js +4 -1
  271. package/dist/tasks/parser.js +51 -38
  272. package/dist/tasks/resolveAkmBin.js +3 -0
  273. package/dist/tasks/runner.js +35 -9
  274. package/dist/tasks/schedule.js +20 -1
  275. package/dist/tasks/schema.js +5 -3
  276. package/dist/tasks/validator.js +6 -3
  277. package/dist/version.js +3 -0
  278. package/dist/wiki/wiki-templates.js +6 -3
  279. package/dist/wiki/wiki.js +4 -1
  280. package/dist/workflows/authoring.js +4 -1
  281. package/dist/workflows/cli.js +3 -0
  282. package/dist/workflows/db.js +140 -10
  283. package/dist/workflows/document-cache.js +3 -10
  284. package/dist/workflows/parser.js +3 -0
  285. package/dist/workflows/renderer.js +3 -0
  286. package/dist/workflows/runs.js +18 -1
  287. package/dist/workflows/schema.js +3 -0
  288. package/dist/workflows/scope-key.js +3 -0
  289. package/dist/workflows/validator.js +5 -9
  290. package/docs/README.md +7 -2
  291. package/docs/data-and-telemetry.md +225 -0
  292. package/docs/migration/release-notes/0.7.5.md +2 -2
  293. package/docs/migration/release-notes/0.8.0.md +57 -5
  294. package/docs/migration/v0.7-to-v0.8.md +1378 -0
  295. package/package.json +28 -11
  296. package/.github/LICENSE +0 -374
  297. package/dist/commands/help/help-accept.md +0 -9
  298. package/dist/commands/help/help-improve.md +0 -53
  299. package/dist/commands/help/help-reject.md +0 -8
  300. package/dist/commands/install-audit.js +0 -385
  301. package/dist/commands/vault.js +0 -310
  302. package/dist/indexer/match-contributors.js +0 -141
  303. package/dist/integrations/agent/pipeline.js +0 -39
  304. package/dist/integrations/agent/runners.js +0 -31
  305. package/dist/llm/prompts/graph-extract-user-prompt.md +0 -12
  306. /package/dist/{tasks → assets}/backends/launchd-template.xml +0 -0
  307. /package/dist/{tasks → assets}/backends/schtasks-template.xml +0 -0
  308. /package/dist/{commands → assets}/help/help-propose.md +0 -0
  309. /package/dist/{wiki → assets/wiki}/index-template.md +0 -0
  310. /package/dist/{wiki → assets/wiki}/ingest-workflow-template.md +0 -0
  311. /package/dist/{wiki → assets/wiki}/log-template.md +0 -0
  312. /package/dist/{wiki → assets/wiki}/schema-template.md +0 -0
  313. /package/dist/{workflows → assets/workflows}/workflow-template.md +0 -0
@@ -0,0 +1,424 @@
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
+ export const NORMAL_DESCRIPTION_LIMIT = 250;
5
+ export function shapeProposalProducerOutput(result, detail) {
6
+ if (result.ok === false) {
7
+ const base = {
8
+ ok: false,
9
+ reason: result.reason,
10
+ error: result.error,
11
+ ...(result.ref !== undefined ? { ref: result.ref } : {}),
12
+ ...(result.type !== undefined ? { type: result.type } : {}),
13
+ ...(result.name !== undefined ? { name: result.name } : {}),
14
+ ...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
15
+ };
16
+ if (detail === "full") {
17
+ return {
18
+ schemaVersion: result.schemaVersion ?? 1,
19
+ ...base,
20
+ ...(result.stdout !== undefined ? { stdout: result.stdout } : {}),
21
+ ...(result.stderr !== undefined ? { stderr: result.stderr } : {}),
22
+ };
23
+ }
24
+ return base;
25
+ }
26
+ const proposal = result.proposal ?? {};
27
+ const base = {
28
+ ok: true,
29
+ ref: result.ref,
30
+ ...(result.agentProfile !== undefined ? { agentProfile: result.agentProfile } : {}),
31
+ ...(typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {}),
32
+ proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
33
+ };
34
+ if (detail === "full") {
35
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
36
+ }
37
+ return base;
38
+ }
39
+ export function shapeProposalEntry(entry, detail) {
40
+ if (detail === "brief") {
41
+ return pickFields(entry, ["id", "ref", "status", "source", "createdAt"]);
42
+ }
43
+ if (detail === "normal") {
44
+ return pickFields(entry, ["id", "ref", "status", "source", "sourceRun", "createdAt", "updatedAt", "review"]);
45
+ }
46
+ // full: project everything including the payload.
47
+ return pickFields(entry, [
48
+ "id",
49
+ "ref",
50
+ "status",
51
+ "source",
52
+ "sourceRun",
53
+ "createdAt",
54
+ "updatedAt",
55
+ "payload",
56
+ "review",
57
+ ]);
58
+ }
59
+ export function shapeProposalListOutput(result, detail) {
60
+ const proposals = Array.isArray(result.proposals) ? result.proposals : [];
61
+ const shaped = proposals.map((p) => shapeProposalEntry(p, detail));
62
+ const base = {
63
+ totalCount: result.totalCount ?? shaped.length,
64
+ proposals: shaped,
65
+ };
66
+ if (detail === "full") {
67
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
68
+ }
69
+ return base;
70
+ }
71
+ export function shapeProposalShowOutput(result, detail) {
72
+ const proposal = result.proposal ?? {};
73
+ const validation = result.validation;
74
+ const base = {
75
+ proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
76
+ ...(validation ? { validation } : {}),
77
+ };
78
+ if (detail === "full") {
79
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
80
+ }
81
+ return base;
82
+ }
83
+ export function shapeProposalAcceptOutput(result, detail) {
84
+ const proposal = result.proposal ?? {};
85
+ const base = {
86
+ ok: result.ok ?? true,
87
+ id: result.id,
88
+ ref: result.ref,
89
+ assetPath: result.assetPath,
90
+ proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
91
+ };
92
+ if (detail === "full") {
93
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
94
+ }
95
+ return base;
96
+ }
97
+ export function shapeProposalRejectOutput(result, detail) {
98
+ const proposal = result.proposal ?? {};
99
+ const base = {
100
+ ok: result.ok ?? true,
101
+ id: result.id,
102
+ ref: result.ref,
103
+ ...(result.reason !== undefined ? { reason: result.reason } : {}),
104
+ proposal: shapeProposalEntry(proposal, detail === "brief" ? "normal" : detail),
105
+ };
106
+ if (detail === "full") {
107
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
108
+ }
109
+ return base;
110
+ }
111
+ export function shapeDistillOutput(result, detail) {
112
+ const proposal = result.proposal;
113
+ if (detail === "brief") {
114
+ return pickFields(result, ["ok", "outcome", "inputRef", "lessonRef", "proposalId", "message"]);
115
+ }
116
+ const base = {
117
+ ok: result.ok ?? true,
118
+ outcome: result.outcome,
119
+ inputRef: result.inputRef,
120
+ lessonRef: result.lessonRef,
121
+ ...(result.proposalId !== undefined ? { proposalId: result.proposalId } : {}),
122
+ ...(result.message !== undefined ? { message: result.message } : {}),
123
+ ...(Array.isArray(result.findings) && result.findings.length > 0 ? { findings: result.findings } : {}),
124
+ ...(proposal ? { proposal: shapeProposalEntry(proposal, detail) } : {}),
125
+ };
126
+ if (detail === "full") {
127
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
128
+ }
129
+ return base;
130
+ }
131
+ export function shapeProposalDiffOutput(result, detail) {
132
+ const base = {
133
+ id: result.id,
134
+ ref: result.ref,
135
+ isNew: result.isNew,
136
+ unified: result.unified,
137
+ ...(result.targetPath !== undefined ? { targetPath: result.targetPath } : {}),
138
+ };
139
+ if (detail === "full") {
140
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
141
+ }
142
+ return base;
143
+ }
144
+ export function shapeEventsOutput(result, detail) {
145
+ const events = Array.isArray(result.events) ? result.events : [];
146
+ const shapedEvents = events.map((event) => shapeEventEntry(event, detail));
147
+ const base = {
148
+ ...(result.ref !== undefined ? { ref: result.ref } : {}),
149
+ ...(result.type !== undefined ? { type: result.type } : {}),
150
+ ...(result.since !== undefined ? { since: result.since } : {}),
151
+ ...(typeof result.sinceOffset === "number" ? { sinceOffset: result.sinceOffset } : {}),
152
+ totalCount: result.totalCount ?? shapedEvents.length,
153
+ events: shapedEvents,
154
+ };
155
+ if (typeof result.nextOffset === "number") {
156
+ base.nextOffset = result.nextOffset;
157
+ }
158
+ if (typeof result.reason === "string") {
159
+ base.reason = result.reason;
160
+ }
161
+ if (detail === "full") {
162
+ return { schemaVersion: result.schemaVersion ?? 1, ...base };
163
+ }
164
+ return base;
165
+ }
166
+ export function shapeEventEntry(entry, detail) {
167
+ if (detail === "brief") {
168
+ return pickFields(entry, ["eventType", "ref", "ts"]);
169
+ }
170
+ if (detail === "normal") {
171
+ return pickFields(entry, ["eventType", "ref", "ts"]);
172
+ }
173
+ // full: project everything the reader emits.
174
+ return pickFields(entry, ["id", "schemaVersion", "eventType", "ref", "ts", "metadata"]);
175
+ }
176
+ export function shapeHistoryOutput(result, detail) {
177
+ const entries = Array.isArray(result.entries) ? result.entries : [];
178
+ const shapedEntries = entries.map((entry) => shapeHistoryEntry(entry, detail));
179
+ if (detail === "full") {
180
+ return {
181
+ schemaVersion: result.schemaVersion ?? 1,
182
+ ...(result.ref !== undefined ? { ref: result.ref } : {}),
183
+ ...(result.since !== undefined ? { since: result.since } : {}),
184
+ totalCount: result.totalCount ?? shapedEntries.length,
185
+ entries: shapedEntries,
186
+ // `sources` lists the event sources included in this response.
187
+ // Always contains "usage_events"; also "events.jsonl" when
188
+ // --include-proposals was specified.
189
+ ...(Array.isArray(result.sources) ? { sources: result.sources } : {}),
190
+ ...(Array.isArray(result.warnings) && result.warnings.length > 0 ? { warnings: result.warnings } : {}),
191
+ };
192
+ }
193
+ return {
194
+ ...(result.ref !== undefined ? { ref: result.ref } : {}),
195
+ ...(result.since !== undefined ? { since: result.since } : {}),
196
+ totalCount: result.totalCount ?? shapedEntries.length,
197
+ entries: shapedEntries,
198
+ ...(Array.isArray(result.sources) ? { sources: result.sources } : {}),
199
+ ...(Array.isArray(result.warnings) && result.warnings.length > 0 ? { warnings: result.warnings } : {}),
200
+ };
201
+ }
202
+ export function shapeHistoryEntry(entry, detail) {
203
+ if (detail === "brief") {
204
+ // signal is load-bearing for feedback rows (positive/negative) so we
205
+ // project it even at brief — without it the entry is ambiguous.
206
+ // source lets callers verify filter correctness (e.g. --source user).
207
+ return pickFields(entry, ["eventType", "ref", "signal", "source", "createdAt"]);
208
+ }
209
+ if (detail === "normal") {
210
+ return pickFields(entry, ["eventType", "ref", "signal", "source", "query", "createdAt"]);
211
+ }
212
+ // full: return everything the reader emits.
213
+ return pickFields(entry, ["id", "eventType", "ref", "entryId", "query", "signal", "source", "metadata", "createdAt"]);
214
+ }
215
+ export function shapeSearchOutput(result, detail, shape = "human") {
216
+ const forAgent = shape === "agent";
217
+ const hits = Array.isArray(result.hits) ? result.hits : [];
218
+ const registryHits = Array.isArray(result.registryHits) ? result.registryHits : [];
219
+ const shapedHits = forAgent
220
+ ? hits.map((hit) => shapeSearchHitForAgent(hit))
221
+ : hits.map((hit) => shapeSearchHit(hit, detail));
222
+ const shapedRegistryHits = forAgent
223
+ ? registryHits.map((hit) => shapeSearchHitForAgent(hit))
224
+ : registryHits.map((hit) => shapeSearchHit(hit, detail));
225
+ if (forAgent) {
226
+ return {
227
+ hits: shapedHits,
228
+ ...(shapedRegistryHits.length > 0 ? { registryHits: shapedRegistryHits } : {}),
229
+ ...(result.tip ? { tip: result.tip } : {}),
230
+ };
231
+ }
232
+ if (detail === "full") {
233
+ return {
234
+ schemaVersion: result.schemaVersion,
235
+ stashDir: result.stashDir,
236
+ source: result.source,
237
+ hits: shapedHits,
238
+ ...(shapedRegistryHits.length > 0 ? { registryHits: shapedRegistryHits } : {}),
239
+ ...(result.semanticSearch ? { semanticSearch: result.semanticSearch } : {}),
240
+ ...(result.tip ? { tip: result.tip } : {}),
241
+ ...(result.warnings ? { warnings: result.warnings } : {}),
242
+ ...(result.timing ? { timing: result.timing } : {}),
243
+ };
244
+ }
245
+ return {
246
+ hits: shapedHits,
247
+ ...(shapedRegistryHits.length > 0 ? { registryHits: shapedRegistryHits } : {}),
248
+ ...(Array.isArray(result.warnings) && result.warnings.length > 0 ? { warnings: result.warnings } : {}),
249
+ ...(result.tip ? { tip: result.tip } : {}),
250
+ };
251
+ }
252
+ export function shapeRegistrySearchOutput(result, detail) {
253
+ const hits = Array.isArray(result.hits) ? result.hits : [];
254
+ const assetHits = Array.isArray(result.assetHits) ? result.assetHits : [];
255
+ // Shape stash hits as registry type
256
+ const shapedKitHits = hits.map((hit) => shapeSearchHit({ ...hit, type: "registry" }, detail));
257
+ // Shape asset hits by detail level
258
+ const shapedAssetHits = assetHits.map((hit) => shapeAssetHit(hit, detail));
259
+ const shaped = {
260
+ hits: shapedKitHits,
261
+ ...(shapedAssetHits.length > 0 ? { assetHits: shapedAssetHits } : {}),
262
+ ...(Array.isArray(result.warnings) && result.warnings.length > 0 ? { warnings: result.warnings } : {}),
263
+ };
264
+ if (detail === "full") {
265
+ shaped.query = result.query;
266
+ }
267
+ return shaped;
268
+ }
269
+ export function shapeAssetHit(hit, detail) {
270
+ if (detail === "brief")
271
+ return pickFields(hit, ["assetName", "assetType", "action", "estimatedTokens"]);
272
+ if (detail === "normal") {
273
+ return capDescription(pickFields(hit, ["assetName", "assetType", "description", "stash", "action", "estimatedTokens"]), NORMAL_DESCRIPTION_LIMIT);
274
+ }
275
+ return hit;
276
+ }
277
+ export function shapeSearchHit(hit, detail) {
278
+ if (hit.type === "registry") {
279
+ if (detail === "brief") {
280
+ // RegistrySearchHit uses `title` (not `name`); always project installRef
281
+ // and score so callers can use the result without --detail full (QA #28).
282
+ const out = pickFields(hit, ["title", "name", "installRef", "score"]);
283
+ // Normalise: if only title exists, expose it as `name` for consistency
284
+ if (out.title && !out.name)
285
+ out.name = out.title;
286
+ return out;
287
+ }
288
+ if (detail === "normal") {
289
+ // `curated` was removed in v1 (spec §4.2). Renderers project optional
290
+ // hit-level `warnings` instead so providers can surface non-fatal issues.
291
+ const out = capDescription(pickFields(hit, ["title", "name", "description", "action", "installRef", "score", "warnings"]), NORMAL_DESCRIPTION_LIMIT);
292
+ if (out.title && !out.name)
293
+ out.name = out.title;
294
+ return out;
295
+ }
296
+ return hit;
297
+ }
298
+ // Stash hit (local or remote)
299
+ // `ref` is included at `brief` so agents can run `akm show <ref>` without
300
+ // needing --detail full or --for-agent (REC-03).
301
+ if (detail === "brief")
302
+ return pickFields(hit, ["type", "name", "ref", "action", "estimatedTokens", "keys"]);
303
+ if (detail === "normal") {
304
+ // `warnings` is projected at `normal` so non-fatal hit-level issues are
305
+ // visible without forcing callers up to `--detail full`. Optional
306
+ // `quality` (v1 spec §4.2) is also surfaced when present so callers
307
+ // can see why a `proposed` entry showed up under `--include-proposed`.
308
+ const shaped = capDescription(pickFields(hit, ["type", "name", "description", "action", "score", "estimatedTokens", "warnings", "quality"]), NORMAL_DESCRIPTION_LIMIT);
309
+ if (Array.isArray(hit.keys) && hit.keys.length > 0)
310
+ shaped.keys = hit.keys;
311
+ return shaped;
312
+ }
313
+ return hit;
314
+ }
315
+ /** Agent-optimized search hit: only fields an LLM agent needs to decide and act */
316
+ export function shapeSearchHitForAgent(hit) {
317
+ const picked = pickFields(hit, ["name", "ref", "type", "description", "action", "score", "estimatedTokens", "keys"]);
318
+ return capDescription(picked, NORMAL_DESCRIPTION_LIMIT);
319
+ }
320
+ export function capDescription(hit, limit) {
321
+ if (typeof hit.description !== "string")
322
+ return hit;
323
+ return { ...hit, description: truncateDescription(hit.description, limit) };
324
+ }
325
+ export function truncateDescription(description, limit) {
326
+ const normalized = description.replace(/\s+/g, " ").trim();
327
+ if (normalized.length <= limit)
328
+ return normalized;
329
+ const truncated = normalized.slice(0, limit - 1);
330
+ const lastSpace = truncated.lastIndexOf(" ");
331
+ const safe = lastSpace >= Math.floor(limit * 0.6) ? truncated.slice(0, lastSpace) : truncated;
332
+ return `${safe.trimEnd()}...`;
333
+ }
334
+ export function shapeShowOutput(result, detail, shape = "human") {
335
+ if (shape === "agent") {
336
+ return pickFields(result, [
337
+ "type",
338
+ "name",
339
+ "description",
340
+ "action",
341
+ "content",
342
+ "template",
343
+ "prompt",
344
+ "run",
345
+ "setup",
346
+ "cwd",
347
+ "activeRun",
348
+ "toolPolicy",
349
+ "modelHint",
350
+ "agent",
351
+ "parameters",
352
+ "workflowTitle",
353
+ "workflowParameters",
354
+ "steps",
355
+ "keys",
356
+ "comments",
357
+ "related",
358
+ ]);
359
+ }
360
+ if (shape === "summary") {
361
+ return pickFields(result, [
362
+ "type",
363
+ "name",
364
+ "description",
365
+ "tags",
366
+ "parameters",
367
+ "workflowTitle",
368
+ "action",
369
+ "run",
370
+ "origin",
371
+ "keys",
372
+ "comments",
373
+ "related",
374
+ ]);
375
+ }
376
+ const base = pickFields(result, [
377
+ "type",
378
+ "name",
379
+ "origin",
380
+ "action",
381
+ "description",
382
+ "tags",
383
+ "content",
384
+ "template",
385
+ "prompt",
386
+ "toolPolicy",
387
+ "modelHint",
388
+ "agent",
389
+ "parameters",
390
+ "workflowTitle",
391
+ "workflowParameters",
392
+ "steps",
393
+ "run",
394
+ "setup",
395
+ "cwd",
396
+ "activeRun",
397
+ "keys",
398
+ "comments",
399
+ "related",
400
+ // path and editable are always projected so JSON consumers can locate and
401
+ // edit the asset without needing --detail full (QA #7).
402
+ // Exception: vault assets omit path to avoid leaking absolute disk paths
403
+ // into structured JSON output (security fix M3).
404
+ ...(result.type === "vault" ? [] : ["path"]),
405
+ "editable",
406
+ ]);
407
+ if (detail !== "full") {
408
+ return base;
409
+ }
410
+ return {
411
+ schemaVersion: 1,
412
+ ...base,
413
+ ...pickFields(result, ["editHint"]),
414
+ };
415
+ }
416
+ export function pickFields(source, fields) {
417
+ const result = {};
418
+ for (const field of fields) {
419
+ if (source[field] !== undefined) {
420
+ result[field] = source[field];
421
+ }
422
+ }
423
+ return result;
424
+ }
@@ -0,0 +1,7 @@
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
+ // Output shape registration for `akm history` — paired with the text renderer in text.ts.
5
+ import { shapeHistoryOutput } from "./helpers";
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("history", (result, detail) => shapeHistoryOutput(result, detail));
@@ -0,0 +1,105 @@
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
+ // Identity-passthrough commands — registered here so the registry stays
5
+ // exhaustive (v1 spec §9). Each result object is already shaped at the
6
+ // command boundary; the registry just confirms there's no surprise
7
+ // command name slipping through.
8
+ import { registerOutputShape } from "./registry";
9
+ // #484: stamp schemaVersion + shape discriminator on passthrough envelopes so
10
+ // third-party consumers can pin a schema version and dispatch on shape uniformly.
11
+ // Idempotent — never overwrites an existing schemaVersion or shape field.
12
+ function makeStampHandler(command) {
13
+ return (result) => {
14
+ if (result === null || result === undefined)
15
+ return result;
16
+ if (typeof result !== "object" || Array.isArray(result))
17
+ return result;
18
+ const obj = result;
19
+ if (obj.shape === undefined)
20
+ obj.shape = command;
21
+ if (obj.schemaVersion === undefined)
22
+ obj.schemaVersion = 1;
23
+ return obj;
24
+ };
25
+ }
26
+ const PASSTHROUGH_COMMANDS = [
27
+ "add",
28
+ "agent-result",
29
+ "clone",
30
+ "config",
31
+ "db-backups",
32
+ "disable",
33
+ "enable",
34
+ "env-create",
35
+ "env-export",
36
+ "env-remove",
37
+ "feedback",
38
+ "graph-entities",
39
+ "graph-entity",
40
+ "graph-export",
41
+ "graph-orphans",
42
+ "graph-related",
43
+ "graph-relations",
44
+ "graph-summary",
45
+ "graph-update",
46
+ "extract",
47
+ "health",
48
+ "improve",
49
+ "lessons-coverage",
50
+ "import",
51
+ "index",
52
+ "info",
53
+ "init",
54
+ "lint",
55
+ "list",
56
+ "proposal-accept-batch",
57
+ "proposal-drain",
58
+ "proposal-reject-batch",
59
+ "proposal-revert",
60
+ "registry-add",
61
+ "registry-build-index",
62
+ "registry-list",
63
+ "registry-remove",
64
+ "remember",
65
+ "remove",
66
+ "save",
67
+ "secret-set",
68
+ "secret-remove",
69
+ "setup",
70
+ "tasks-add",
71
+ "tasks-disable",
72
+ "tasks-doctor",
73
+ "tasks-enable",
74
+ "tasks-history",
75
+ "tasks-list",
76
+ "tasks-remove",
77
+ "tasks-run",
78
+ "tasks-show",
79
+ "tasks-sync",
80
+ "update",
81
+ "upgrade",
82
+ "vault-create",
83
+ "vault-set",
84
+ "vault-unset",
85
+ "wiki-create",
86
+ "wiki-ingest",
87
+ "wiki-lint",
88
+ "wiki-list",
89
+ "wiki-pages",
90
+ "wiki-register",
91
+ "wiki-remove",
92
+ "wiki-show",
93
+ "wiki-stash",
94
+ "workflow-complete",
95
+ "workflow-create",
96
+ "workflow-list",
97
+ "workflow-next",
98
+ "workflow-resume",
99
+ "workflow-start",
100
+ "workflow-status",
101
+ "workflow-validate",
102
+ ];
103
+ for (const command of PASSTHROUGH_COMMANDS) {
104
+ registerOutputShape(command, makeStampHandler(command));
105
+ }
@@ -0,0 +1,7 @@
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
+ // Output shape registration for `akm proposal accept` (#225).
5
+ import { shapeProposalAcceptOutput } from "./helpers";
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("proposal-accept", (result, detail) => shapeProposalAcceptOutput(result, detail));
@@ -0,0 +1,7 @@
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
+ // Output shape registration for `akm proposal diff` (#225).
5
+ import { shapeProposalDiffOutput } from "./helpers";
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("proposal-diff", (result, detail) => shapeProposalDiffOutput(result, detail));
@@ -0,0 +1,7 @@
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
+ // Output shape registration for `akm proposal list` (#225).
5
+ import { shapeProposalListOutput } from "./helpers";
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("proposal-list", (result, detail) => shapeProposalListOutput(result, detail));
@@ -0,0 +1,11 @@
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
+ // Output shape registration for `akm reflect` and `akm propose` (#226).
5
+ // Both share the proposal-producer envelope shape (success carries a proposal
6
+ // entry; failure carries an AgentFailureReason discriminant).
7
+ import { shapeProposalProducerOutput } from "./helpers";
8
+ import { registerOutputShape } from "./registry";
9
+ const handler = (result, detail) => shapeProposalProducerOutput(result, detail);
10
+ registerOutputShape("reflect", handler);
11
+ registerOutputShape("propose", handler);
@@ -0,0 +1,7 @@
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
+ // Output shape registration for `akm proposal reject` (#225).
5
+ import { shapeProposalRejectOutput } from "./helpers";
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("proposal-reject", (result, detail) => shapeProposalRejectOutput(result, detail));
@@ -0,0 +1,7 @@
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
+ // Output shape registration for `akm proposal show` (#225).
5
+ import { shapeProposalShowOutput } from "./helpers";
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("proposal-show", (result, detail) => shapeProposalShowOutput(result, detail));
@@ -0,0 +1,6 @@
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 { shapeRegistrySearchOutput } from "./helpers";
5
+ import { registerOutputShape } from "./registry";
6
+ registerOutputShape("registry-search", (result, detail) => shapeRegistrySearchOutput(result, detail));
@@ -0,0 +1,30 @@
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
+ const OUTPUT_SHAPE_REGISTRY = new Map();
5
+ /**
6
+ * Register an output shape handler for a command name.
7
+ *
8
+ * Call this at module load time from a `src/output/shapes/<command>.ts` file.
9
+ * Multiple command names may share the same handler — call once per name.
10
+ *
11
+ * ```ts
12
+ * registerOutputShape("my-command", (result, detail) => result);
13
+ * ```
14
+ */
15
+ export function registerOutputShape(command, handler) {
16
+ OUTPUT_SHAPE_REGISTRY.set(command, handler);
17
+ }
18
+ /**
19
+ * Remove a previously-registered output shape. Test-only utility.
20
+ */
21
+ export function deregisterOutputShape(command) {
22
+ OUTPUT_SHAPE_REGISTRY.delete(command);
23
+ }
24
+ /**
25
+ * Look up a registered output shape handler by command name.
26
+ * Returns `undefined` if not registered.
27
+ */
28
+ export function getOutputShapeHandler(command) {
29
+ return OUTPUT_SHAPE_REGISTRY.get(command);
30
+ }
@@ -0,0 +1,6 @@
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 { shapeSearchOutput } from "./helpers";
5
+ import { registerOutputShape } from "./registry";
6
+ registerOutputShape("search", (result, detail, shape) => shapeSearchOutput(result, detail, shape));
@@ -0,0 +1,19 @@
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
+ // secret-list strips `path` from each secret object (same as vault-list: avoid
5
+ // leaking absolute disk paths) then stamps the envelope.
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("secret-list", (result) => {
8
+ const r = result;
9
+ const secrets = Array.isArray(r.secrets) ? r.secrets : [];
10
+ return {
11
+ ...r,
12
+ shape: r.shape ?? "secret-list",
13
+ schemaVersion: r.schemaVersion ?? 1,
14
+ secrets: secrets.map((s) => {
15
+ const { path: _path, ...rest } = s;
16
+ return rest;
17
+ }),
18
+ };
19
+ });
@@ -0,0 +1,6 @@
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 { shapeShowOutput } from "./helpers";
5
+ import { registerOutputShape } from "./registry";
6
+ registerOutputShape("show", (result, detail, shape) => shapeShowOutput(result, detail, shape));