akm-cli 0.8.0-rc2 → 0.8.0

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 (295) hide show
  1. package/{.github/CHANGELOG.md → CHANGELOG.md} +191 -3
  2. package/README.md +22 -6
  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 +93 -3
  8. package/dist/cli/shared.js +129 -0
  9. package/dist/cli.js +2141 -1268
  10. package/dist/commands/add-cli.js +279 -0
  11. package/dist/commands/agent-dispatch.js +20 -12
  12. package/dist/commands/agent-support.js +11 -5
  13. package/dist/commands/completions.js +3 -0
  14. package/dist/commands/config-cli.js +129 -517
  15. package/dist/commands/consolidate.js +1533 -144
  16. package/dist/commands/curate.js +44 -3
  17. package/dist/commands/db-cli.js +23 -0
  18. package/dist/commands/distill-promotion-policy.js +5 -3
  19. package/dist/commands/distill.js +906 -100
  20. package/dist/commands/env.js +213 -0
  21. package/dist/commands/eval-cases.js +3 -0
  22. package/dist/commands/events.js +3 -0
  23. package/dist/commands/extract-cli.js +127 -0
  24. package/dist/commands/extract-prompt.js +204 -0
  25. package/dist/commands/extract.js +477 -0
  26. package/dist/commands/feedback-cli.js +331 -0
  27. package/dist/commands/graph.js +260 -5
  28. package/dist/commands/health.js +977 -51
  29. package/dist/commands/help/help-accept.md +6 -3
  30. package/dist/commands/help/help-improve.md +36 -8
  31. package/dist/commands/help/help-proposals.md +7 -4
  32. package/dist/commands/help/help-reject.md +5 -2
  33. package/dist/commands/history.js +51 -16
  34. package/dist/commands/improve-auto-accept.js +97 -0
  35. package/dist/commands/improve-cli.js +236 -0
  36. package/dist/commands/improve-profiles.js +184 -0
  37. package/dist/commands/improve-result-file.js +167 -0
  38. package/dist/commands/improve.js +1725 -332
  39. package/dist/commands/info.js +3 -0
  40. package/dist/commands/init.js +49 -1
  41. package/dist/commands/installed-stashes.js +6 -23
  42. package/dist/commands/knowledge.js +3 -0
  43. package/dist/commands/lint/agent-linter.js +3 -0
  44. package/dist/commands/lint/base-linter.js +199 -5
  45. package/dist/commands/lint/command-linter.js +3 -0
  46. package/dist/commands/lint/default-linter.js +3 -0
  47. package/dist/commands/lint/env-key-rules.js +154 -0
  48. package/dist/commands/lint/index.js +92 -3
  49. package/dist/commands/lint/knowledge-linter.js +3 -0
  50. package/dist/commands/lint/markdown-insertion.js +343 -0
  51. package/dist/commands/lint/memory-linter.js +3 -0
  52. package/dist/commands/lint/registry.js +3 -0
  53. package/dist/commands/lint/skill-linter.js +3 -0
  54. package/dist/commands/lint/task-linter.js +15 -12
  55. package/dist/commands/lint/types.js +3 -0
  56. package/dist/commands/lint/workflow-linter.js +3 -0
  57. package/dist/commands/lint.js +3 -0
  58. package/dist/commands/migration-help.js +5 -2
  59. package/dist/commands/proposal-drain-policies.js +128 -0
  60. package/dist/commands/proposal-drain.js +477 -0
  61. package/dist/commands/proposal.js +60 -6
  62. package/dist/commands/propose.js +24 -19
  63. package/dist/commands/reflect.js +1004 -94
  64. package/dist/commands/registry-cli.js +150 -0
  65. package/dist/commands/registry-search.js +3 -0
  66. package/dist/commands/remember-cli.js +257 -0
  67. package/dist/commands/remember.js +15 -6
  68. package/dist/commands/schema-repair.js +88 -15
  69. package/dist/commands/search.js +99 -14
  70. package/dist/commands/secret.js +173 -0
  71. package/dist/commands/self-update.js +3 -0
  72. package/dist/commands/show.js +32 -13
  73. package/dist/commands/source-add.js +7 -35
  74. package/dist/commands/source-clone.js +3 -0
  75. package/dist/commands/source-manage.js +3 -0
  76. package/dist/commands/tasks.js +161 -95
  77. package/dist/commands/url-checker.js +3 -0
  78. package/dist/core/action-contributors.js +3 -0
  79. package/dist/core/asset-ref.js +13 -2
  80. package/dist/core/asset-registry.js +9 -2
  81. package/dist/core/asset-serialize.js +88 -0
  82. package/dist/core/asset-spec.js +61 -5
  83. package/dist/core/common.js +93 -5
  84. package/dist/core/concurrent.js +3 -0
  85. package/dist/core/config-io.js +347 -0
  86. package/dist/core/config-migration.js +622 -0
  87. package/dist/core/config-schema.js +558 -0
  88. package/dist/core/config-sources.js +108 -0
  89. package/dist/core/config-types.js +4 -0
  90. package/dist/core/config-walker.js +337 -0
  91. package/dist/core/config.js +366 -1077
  92. package/dist/core/errors.js +42 -20
  93. package/dist/core/events.js +31 -25
  94. package/dist/core/file-lock.js +104 -0
  95. package/dist/core/frontmatter.js +75 -10
  96. package/dist/core/lesson-lint.js +3 -0
  97. package/dist/core/markdown.js +3 -0
  98. package/dist/core/memory-belief.js +62 -0
  99. package/dist/core/memory-contradiction-detect.js +274 -0
  100. package/dist/core/memory-improve.js +142 -14
  101. package/dist/core/parse.js +3 -0
  102. package/dist/core/paths.js +218 -50
  103. package/dist/core/proposal-quality-validators.js +380 -0
  104. package/dist/core/proposal-validators.js +11 -3
  105. package/dist/core/proposals.js +464 -5
  106. package/dist/core/state-db.js +349 -56
  107. package/dist/core/text-truncation.js +107 -0
  108. package/dist/core/time.js +3 -0
  109. package/dist/core/tty.js +59 -0
  110. package/dist/core/warn.js +7 -2
  111. package/dist/core/write-source.js +12 -0
  112. package/dist/indexer/db-backup.js +391 -0
  113. package/dist/indexer/db-search.js +136 -28
  114. package/dist/indexer/db.js +661 -166
  115. package/dist/indexer/ensure-index.js +3 -0
  116. package/dist/indexer/file-context.js +3 -0
  117. package/dist/indexer/graph-boost.js +162 -40
  118. package/dist/indexer/graph-db.js +241 -51
  119. package/dist/indexer/graph-dedup.js +3 -7
  120. package/dist/indexer/graph-extraction.js +242 -149
  121. package/dist/indexer/index-context.js +3 -9
  122. package/dist/indexer/indexer.js +84 -14
  123. package/dist/indexer/llm-cache.js +24 -19
  124. package/dist/indexer/manifest.js +3 -0
  125. package/dist/indexer/matchers.js +184 -11
  126. package/dist/indexer/memory-inference.js +94 -50
  127. package/dist/indexer/metadata-contributors.js +3 -0
  128. package/dist/indexer/metadata.js +110 -50
  129. package/dist/indexer/path-resolver.js +3 -0
  130. package/dist/indexer/project-context.js +192 -0
  131. package/dist/indexer/ranking-contributors.js +134 -7
  132. package/dist/indexer/ranking.js +8 -1
  133. package/dist/indexer/search-fields.js +5 -9
  134. package/dist/indexer/search-hit-enrichers.js +91 -2
  135. package/dist/indexer/search-source.js +20 -1
  136. package/dist/indexer/semantic-status.js +4 -1
  137. package/dist/indexer/staleness-detect.js +447 -0
  138. package/dist/indexer/usage-events.js +12 -9
  139. package/dist/indexer/walker.js +3 -0
  140. package/dist/integrations/agent/builders.js +135 -0
  141. package/dist/integrations/agent/config.js +121 -401
  142. package/dist/integrations/agent/detect.js +3 -0
  143. package/dist/integrations/agent/index.js +6 -14
  144. package/dist/integrations/agent/model-aliases.js +55 -0
  145. package/dist/integrations/agent/profiles.js +3 -0
  146. package/dist/integrations/agent/prompts.js +137 -8
  147. package/dist/integrations/agent/runner.js +208 -0
  148. package/dist/integrations/agent/sdk-runner.js +8 -2
  149. package/dist/integrations/agent/spawn.js +54 -14
  150. package/dist/integrations/github.js +3 -0
  151. package/dist/integrations/lockfile.js +22 -51
  152. package/dist/integrations/session-logs/index.js +4 -0
  153. package/dist/integrations/session-logs/inline-refs.js +35 -0
  154. package/dist/integrations/session-logs/pre-filter.js +152 -0
  155. package/dist/integrations/session-logs/providers/claude-code.js +226 -0
  156. package/dist/integrations/session-logs/providers/opencode.js +231 -25
  157. package/dist/integrations/session-logs/types.js +3 -0
  158. package/dist/llm/call-ai.js +14 -26
  159. package/dist/llm/client.js +16 -2
  160. package/dist/llm/embedder.js +20 -29
  161. package/dist/llm/embedders/cache.js +3 -7
  162. package/dist/llm/embedders/local.js +42 -1
  163. package/dist/llm/embedders/remote.js +20 -8
  164. package/dist/llm/embedders/types.js +3 -7
  165. package/dist/llm/feature-gate.js +92 -56
  166. package/dist/llm/graph-extract.js +401 -30
  167. package/dist/llm/index-passes.js +44 -29
  168. package/dist/llm/memory-infer.js +30 -2
  169. package/dist/llm/metadata-enhance.js +3 -7
  170. package/dist/llm/prompts/extract-session.md +80 -0
  171. package/dist/llm/prompts/graph-extract-user-prompt.md +24 -1
  172. package/dist/output/cli-hints-full.md +60 -32
  173. package/dist/output/cli-hints-short.md +10 -7
  174. package/dist/output/cli-hints.js +5 -2
  175. package/dist/output/context.js +60 -8
  176. package/dist/output/renderers.js +170 -194
  177. package/dist/output/shapes/curate.js +56 -0
  178. package/dist/output/shapes/distill.js +10 -0
  179. package/dist/output/shapes/env-list.js +19 -0
  180. package/dist/output/shapes/events.js +11 -0
  181. package/dist/output/shapes/helpers.js +424 -0
  182. package/dist/output/shapes/history.js +7 -0
  183. package/dist/output/shapes/passthrough.js +105 -0
  184. package/dist/output/shapes/proposal-accept.js +7 -0
  185. package/dist/output/shapes/proposal-diff.js +7 -0
  186. package/dist/output/shapes/proposal-list.js +7 -0
  187. package/dist/output/shapes/proposal-producer.js +11 -0
  188. package/dist/output/shapes/proposal-reject.js +7 -0
  189. package/dist/output/shapes/proposal-show.js +7 -0
  190. package/dist/output/shapes/registry-search.js +6 -0
  191. package/dist/output/shapes/registry.js +30 -0
  192. package/dist/output/shapes/search.js +6 -0
  193. package/dist/output/shapes/secret-list.js +19 -0
  194. package/dist/output/shapes/show.js +6 -0
  195. package/dist/output/shapes/vault-list.js +19 -0
  196. package/dist/output/shapes.js +51 -549
  197. package/dist/output/text/add.js +6 -0
  198. package/dist/output/text/clone.js +6 -0
  199. package/dist/output/text/config.js +6 -0
  200. package/dist/output/text/curate.js +6 -0
  201. package/dist/output/text/distill.js +7 -0
  202. package/dist/output/text/enable-disable.js +7 -0
  203. package/dist/output/text/events.js +10 -0
  204. package/dist/output/text/feedback.js +6 -0
  205. package/dist/output/text/helpers.js +1059 -0
  206. package/dist/output/text/history.js +7 -0
  207. package/dist/output/text/import.js +6 -0
  208. package/dist/output/text/index.js +6 -0
  209. package/dist/output/text/info.js +6 -0
  210. package/dist/output/text/init.js +6 -0
  211. package/dist/output/text/list.js +6 -0
  212. package/dist/output/text/proposal-producer.js +8 -0
  213. package/dist/output/text/proposal.js +12 -0
  214. package/dist/output/text/registry-commands.js +11 -0
  215. package/dist/output/text/registry.js +30 -0
  216. package/dist/output/text/remember.js +6 -0
  217. package/dist/output/text/remove.js +6 -0
  218. package/dist/output/text/save.js +6 -0
  219. package/dist/output/text/search.js +6 -0
  220. package/dist/output/text/show.js +6 -0
  221. package/dist/output/text/update.js +6 -0
  222. package/dist/output/text/upgrade.js +6 -0
  223. package/dist/output/text/vault.js +16 -0
  224. package/dist/output/text/wiki.js +15 -0
  225. package/dist/output/text/workflow.js +14 -0
  226. package/dist/output/text.js +44 -1329
  227. package/dist/registry/build-index.js +3 -0
  228. package/dist/registry/create-provider-registry.js +3 -0
  229. package/dist/registry/factory.js +4 -1
  230. package/dist/registry/origin-resolve.js +3 -0
  231. package/dist/registry/providers/index.js +3 -0
  232. package/dist/registry/providers/skills-sh.js +11 -2
  233. package/dist/registry/providers/static-index.js +10 -1
  234. package/dist/registry/providers/types.js +3 -24
  235. package/dist/registry/resolve.js +11 -16
  236. package/dist/registry/types.js +3 -0
  237. package/dist/scripts/migrate-storage.js +17767 -0
  238. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
  239. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  240. package/dist/setup/detect.js +3 -0
  241. package/dist/setup/ripgrep-install.js +3 -0
  242. package/dist/setup/ripgrep-resolve.js +3 -0
  243. package/dist/setup/setup.js +306 -67
  244. package/dist/setup/steps.js +3 -15
  245. package/dist/sources/include.js +3 -0
  246. package/dist/sources/provider-factory.js +3 -11
  247. package/dist/sources/provider.js +3 -20
  248. package/dist/sources/providers/filesystem.js +19 -23
  249. package/dist/sources/providers/git.js +171 -21
  250. package/dist/sources/providers/index.js +3 -0
  251. package/dist/sources/providers/install-types.js +3 -13
  252. package/dist/sources/providers/npm.js +3 -4
  253. package/dist/sources/providers/provider-utils.js +3 -0
  254. package/dist/sources/providers/sync-from-ref.js +3 -11
  255. package/dist/sources/providers/tar-utils.js +3 -0
  256. package/dist/sources/providers/website.js +18 -22
  257. package/dist/sources/resolve.js +3 -0
  258. package/dist/sources/types.js +3 -0
  259. package/dist/sources/website-ingest.js +3 -0
  260. package/dist/tasks/backends/cron.js +3 -0
  261. package/dist/tasks/backends/exec-utils.js +3 -0
  262. package/dist/tasks/backends/index.js +3 -11
  263. package/dist/tasks/backends/launchd.js +3 -0
  264. package/dist/tasks/backends/schtasks.js +3 -0
  265. package/dist/tasks/parser.js +51 -38
  266. package/dist/tasks/resolveAkmBin.js +3 -0
  267. package/dist/tasks/runner.js +35 -9
  268. package/dist/tasks/schedule.js +20 -1
  269. package/dist/tasks/schema.js +5 -3
  270. package/dist/tasks/validator.js +6 -3
  271. package/dist/version.js +3 -0
  272. package/dist/wiki/wiki-templates.js +3 -0
  273. package/dist/wiki/wiki.js +3 -0
  274. package/dist/workflows/authoring.js +3 -0
  275. package/dist/workflows/cli.js +3 -0
  276. package/dist/workflows/db.js +140 -10
  277. package/dist/workflows/document-cache.js +3 -10
  278. package/dist/workflows/parser.js +3 -0
  279. package/dist/workflows/renderer.js +3 -0
  280. package/dist/workflows/runs.js +18 -1
  281. package/dist/workflows/schema.js +3 -0
  282. package/dist/workflows/scope-key.js +3 -0
  283. package/dist/workflows/validator.js +5 -9
  284. package/docs/README.md +7 -2
  285. package/docs/data-and-telemetry.md +225 -0
  286. package/docs/migration/release-notes/0.7.5.md +2 -2
  287. package/docs/migration/release-notes/0.8.0.md +57 -5
  288. package/docs/migration/v0.7-to-v0.8.md +1378 -0
  289. package/package.json +28 -11
  290. package/.github/LICENSE +0 -374
  291. package/dist/commands/install-audit.js +0 -385
  292. package/dist/commands/vault.js +0 -310
  293. package/dist/indexer/match-contributors.js +0 -141
  294. package/dist/integrations/agent/pipeline.js +0 -39
  295. package/dist/integrations/agent/runners.js +0 -31
@@ -1,3 +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/.
1
4
  /**
2
5
  * Built-in asset renderers.
3
6
  *
@@ -8,9 +11,9 @@
8
11
  */
9
12
  import fs from "node:fs";
10
13
  import path from "node:path";
11
- import { listKeys as listVaultKeys } from "../commands/vault";
12
- import { hasErrnoCode } from "../core/common";
13
- import { parseFrontmatter, toStringOrUndefined } from "../core/frontmatter";
14
+ import { listKeys as listVaultKeys } from "../commands/env";
15
+ import { asNonEmptyString, hasErrnoCode } from "../core/common";
16
+ import { parseFrontmatter } from "../core/frontmatter";
14
17
  import { extractFrontmatterOnly, extractLineRange, extractSection, formatToc, parseMarkdownToc, } from "../core/markdown";
15
18
  import { registerRenderer } from "../indexer/file-context";
16
19
  import { extractCommentMetadata, extractDescriptionFromComments } from "../indexer/metadata";
@@ -166,7 +169,7 @@ const skillMdRenderer = {
166
169
  name,
167
170
  path: ctx.absPath,
168
171
  action: "Read and follow the instructions below",
169
- description: toStringOrUndefined(parsed.data.description),
172
+ description: asNonEmptyString(parsed.data.description),
170
173
  ...(tags ? { tags } : {}),
171
174
  content: parsed.content,
172
175
  };
@@ -185,11 +188,11 @@ const commandMdRenderer = {
185
188
  name,
186
189
  path: ctx.absPath,
187
190
  action: "Fill $ARGUMENTS placeholders in the template, then dispatch",
188
- description: toStringOrUndefined(parsedMd.data.description),
191
+ description: asNonEmptyString(parsedMd.data.description),
189
192
  ...(tags ? { tags } : {}),
190
193
  template,
191
194
  modelHint: typeof parsedMd.data.model === "string" ? parsedMd.data.model : undefined,
192
- agent: toStringOrUndefined(parsedMd.data.agent),
195
+ agent: asNonEmptyString(parsedMd.data.agent),
193
196
  parameters: extractParameters(template),
194
197
  };
195
198
  },
@@ -205,148 +208,67 @@ const agentMdRenderer = {
205
208
  name,
206
209
  path: ctx.absPath,
207
210
  action: "Dispatch using the prompt below verbatim. Use modelHint and toolPolicy if present.",
208
- description: toStringOrUndefined(parsedMd.data.description),
211
+ description: asNonEmptyString(parsedMd.data.description),
209
212
  prompt: parsedMd.content,
210
213
  toolPolicy: parsedMd.data.tools,
211
214
  modelHint: typeof parsedMd.data.model === "string" ? parsedMd.data.model : undefined,
212
215
  };
213
216
  },
214
217
  };
218
+ // ── 4. knowledge-md / wiki-md shared helper ───────────────────────────────────
219
+ const KNOWLEDGE_ACTION = "Reference material - read the content below. Use 'toc' view for large documents.";
220
+ const WIKI_PAGE_ACTION = "Wiki page — read below. Use 'toc' to scan, 'section <heading>' for depth.";
221
+ /**
222
+ * Shared implementation for knowledge-md and wiki-md `buildShowResponse`.
223
+ *
224
+ * Both renderers handle the same set of view modes (toc, frontmatter, section,
225
+ * lines, full). The only differences are the `type` discriminant and the
226
+ * section-not-found message. Extracting this helper eliminates ~90 lines of
227
+ * byte-for-byte duplication.
228
+ */
229
+ function buildMarkdownViewResponse(ctx, type, action) {
230
+ const name = deriveName(ctx);
231
+ const v = ctx.matchResult.meta?.view ?? { mode: "full" };
232
+ const content = ctx.content();
233
+ switch (v.mode) {
234
+ case "toc": {
235
+ const toc = parseMarkdownToc(content);
236
+ return { type, name, path: ctx.absPath, action, content: formatToc(toc) };
237
+ }
238
+ case "frontmatter": {
239
+ const fm = extractFrontmatterOnly(content);
240
+ return { type, name, path: ctx.absPath, action, content: fm ?? "(no frontmatter)" };
241
+ }
242
+ case "section": {
243
+ const section = extractSection(content, v.heading);
244
+ if (!section) {
245
+ const notFoundMsg = type === "wiki"
246
+ ? `Section "${v.heading}" not found in ${name}. Try \`akm show wiki:${name} toc\` to discover available headings.`
247
+ : `Section "${v.heading}" not found in ${name}. Try \`akm show <ref> toc\` to discover available headings.`;
248
+ return { type, name, path: ctx.absPath, action, content: notFoundMsg };
249
+ }
250
+ return { type, name, path: ctx.absPath, action, content: section.content };
251
+ }
252
+ case "lines": {
253
+ return { type, name, path: ctx.absPath, action, content: extractLineRange(content, v.start, v.end) };
254
+ }
255
+ default: {
256
+ return { type, name, path: ctx.absPath, action, content };
257
+ }
258
+ }
259
+ }
215
260
  // ── 4. knowledge-md ──────────────────────────────────────────────────────────
216
261
  const knowledgeMdRenderer = {
217
262
  name: "knowledge-md",
218
263
  buildShowResponse(ctx) {
219
- const name = deriveName(ctx);
220
- const v = ctx.matchResult.meta?.view ?? { mode: "full" };
221
- const content = ctx.content();
222
- switch (v.mode) {
223
- case "toc": {
224
- const toc = parseMarkdownToc(content);
225
- return {
226
- type: "knowledge",
227
- name,
228
- path: ctx.absPath,
229
- action: "Reference material - read the content below. Use 'toc' view for large documents.",
230
- content: formatToc(toc),
231
- };
232
- }
233
- case "frontmatter": {
234
- const fm = extractFrontmatterOnly(content);
235
- return {
236
- type: "knowledge",
237
- name,
238
- path: ctx.absPath,
239
- action: "Reference material - read the content below. Use 'toc' view for large documents.",
240
- content: fm ?? "(no frontmatter)",
241
- };
242
- }
243
- case "section": {
244
- const section = extractSection(content, v.heading);
245
- if (!section) {
246
- return {
247
- type: "knowledge",
248
- name,
249
- path: ctx.absPath,
250
- action: "Reference material - read the content below. Use 'toc' view for large documents.",
251
- content: `Section "${v.heading}" not found in ${name}. Try \`akm show <ref> toc\` to discover available headings.`,
252
- };
253
- }
254
- return {
255
- type: "knowledge",
256
- name,
257
- path: ctx.absPath,
258
- action: "Reference material - read the content below. Use 'toc' view for large documents.",
259
- content: section.content,
260
- };
261
- }
262
- case "lines": {
263
- return {
264
- type: "knowledge",
265
- name,
266
- path: ctx.absPath,
267
- action: "Reference material - read the content below. Use 'toc' view for large documents.",
268
- content: extractLineRange(content, v.start, v.end),
269
- };
270
- }
271
- default: {
272
- return {
273
- type: "knowledge",
274
- name,
275
- path: ctx.absPath,
276
- action: "Reference material - read the content below. Use 'toc' view for large documents.",
277
- content,
278
- };
279
- }
280
- }
264
+ return buildMarkdownViewResponse(ctx, "knowledge", KNOWLEDGE_ACTION);
281
265
  },
282
266
  };
283
267
  // ── 4b. wiki-md ──────────────────────────────────────────────────────────────
284
- const WIKI_PAGE_ACTION = "Wiki page — read below. Use 'toc' to scan, 'section <heading>' for depth.";
285
268
  const wikiMdRenderer = {
286
269
  name: "wiki-md",
287
270
  buildShowResponse(ctx) {
288
- const name = deriveName(ctx);
289
- const v = ctx.matchResult.meta?.view ?? { mode: "full" };
290
- const content = ctx.content();
291
- switch (v.mode) {
292
- case "toc": {
293
- const toc = parseMarkdownToc(content);
294
- return {
295
- type: "wiki",
296
- name,
297
- path: ctx.absPath,
298
- action: WIKI_PAGE_ACTION,
299
- content: formatToc(toc),
300
- };
301
- }
302
- case "frontmatter": {
303
- const fm = extractFrontmatterOnly(content);
304
- return {
305
- type: "wiki",
306
- name,
307
- path: ctx.absPath,
308
- action: WIKI_PAGE_ACTION,
309
- content: fm ?? "(no frontmatter)",
310
- };
311
- }
312
- case "section": {
313
- const section = extractSection(content, v.heading);
314
- if (!section) {
315
- return {
316
- type: "wiki",
317
- name,
318
- path: ctx.absPath,
319
- action: WIKI_PAGE_ACTION,
320
- content: `Section "${v.heading}" not found in ${name}. Try \`akm show wiki:${name} toc\` to discover available headings.`,
321
- };
322
- }
323
- return {
324
- type: "wiki",
325
- name,
326
- path: ctx.absPath,
327
- action: WIKI_PAGE_ACTION,
328
- content: section.content,
329
- };
330
- }
331
- case "lines": {
332
- return {
333
- type: "wiki",
334
- name,
335
- path: ctx.absPath,
336
- action: WIKI_PAGE_ACTION,
337
- content: extractLineRange(content, v.start, v.end),
338
- };
339
- }
340
- default: {
341
- return {
342
- type: "wiki",
343
- name,
344
- path: ctx.absPath,
345
- action: WIKI_PAGE_ACTION,
346
- content,
347
- };
348
- }
349
- }
271
+ return buildMarkdownViewResponse(ctx, "wiki", WIKI_PAGE_ACTION);
350
272
  },
351
273
  };
352
274
  // ── 4c. lesson-md ────────────────────────────────────────────────────────────
@@ -365,8 +287,8 @@ const lessonMdRenderer = {
365
287
  buildShowResponse(ctx) {
366
288
  const name = deriveName(ctx);
367
289
  const parsed = parseFrontmatter(ctx.content());
368
- const description = toStringOrUndefined(parsed.data.description);
369
- const whenToUse = toStringOrUndefined(parsed.data.when_to_use);
290
+ const description = asNonEmptyString(parsed.data.description);
291
+ const whenToUse = asNonEmptyString(parsed.data.when_to_use);
370
292
  const action = whenToUse
371
293
  ? `Apply this lesson when: ${whenToUse}`
372
294
  : "Apply this lesson when its `when_to_use` trigger matches the current task.";
@@ -467,10 +389,55 @@ const vaultEnvRenderer = {
467
389
  hit.keys = keys;
468
390
  },
469
391
  };
392
+ // ── 8b. env-file ───────────────────────────────────────────────────────────────
393
+ /**
394
+ * Env renderer. Like the (deprecated) vault renderer, returns ONLY key names
395
+ * and start-of-line comments — never values. Deliberately omits
396
+ * content/template/prompt so env values cannot leak through `akm show`.
397
+ */
398
+ const envFileRenderer = {
399
+ name: "env-file",
400
+ buildShowResponse(ctx) {
401
+ const name = deriveName(ctx);
402
+ const { keys, comments } = listVaultKeys(ctx.absPath);
403
+ return {
404
+ type: "env",
405
+ name,
406
+ path: ctx.absPath,
407
+ action: "Environment — keys + comments only. Use `akm env run <ref> -- <command>` to run with the whole .env injected (the safe path — values never reach stdout). `akm env export <ref> --out <file>` writes a sourceable script to a file. Never `source` the raw file. Values stay on disk and are never written to akm's stdout.",
408
+ description: comments.length > 0 ? comments.join("\n") : undefined,
409
+ keys,
410
+ comments,
411
+ };
412
+ },
413
+ enrichSearchHit(hit, _stashDir) {
414
+ const { keys } = listVaultKeys(hit.path);
415
+ if (keys.length > 0)
416
+ hit.keys = keys;
417
+ },
418
+ };
419
+ // ── 9. secret-file ─────────────────────────────────────────────────────────────
420
+ /**
421
+ * Secret renderer. The ENTIRE file is the secret value, so this surfaces ONLY
422
+ * the name + path + a usage hint — never content/template/prompt/keys. There
423
+ * is no `enrichSearchHit`: secrets are discoverable by name alone.
424
+ */
425
+ const secretFileRenderer = {
426
+ name: "secret-file",
427
+ buildShowResponse(ctx) {
428
+ const name = deriveName(ctx);
429
+ return {
430
+ type: "secret",
431
+ name,
432
+ path: ctx.absPath,
433
+ action: "Secret — name only; the file contents are the value and are never written to akm's stdout. Use `akm secret path <ref>` for the file path, or `akm secret run <ref> <VAR> -- <command>` to run with the value injected into $VAR.",
434
+ };
435
+ },
436
+ };
470
437
  // ── 7. task-md ───────────────────────────────────────────────────────────────
471
438
  const TASK_PAGE_ACTION = "Scheduled task — `akm tasks show <id>` for parsed details, `akm tasks run <id>` to invoke now.";
472
439
  const taskMdRenderer = {
473
- name: "task-md",
440
+ name: "task-yaml",
474
441
  buildShowResponse(ctx) {
475
442
  const name = deriveName(ctx);
476
443
  return {
@@ -492,28 +459,37 @@ function applyTocMetadata(entry, ctx) {
492
459
  // Non-fatal: skip TOC if file can't be read
493
460
  }
494
461
  }
462
+ /**
463
+ * Parse frontmatter, apply description (if not already set) and merge tags
464
+ * into `entry`. Returns the raw frontmatter data object so callers can access
465
+ * type-specific fields without re-parsing.
466
+ */
467
+ function applyFrontmatterDescriptionAndTags(entry, ctx) {
468
+ const parsed = parseFrontmatter(ctx.content());
469
+ const fm = parsed.data;
470
+ const desc = asNonEmptyString(fm.description);
471
+ if (desc && !entry.description) {
472
+ entry.description = desc;
473
+ entry.source = "frontmatter";
474
+ entry.confidence = 0.9;
475
+ }
476
+ if (Array.isArray(fm.tags) && fm.tags.length > 0) {
477
+ const fmTags = fm.tags.filter((t) => typeof t === "string" && t.trim().length > 0);
478
+ if (fmTags.length > 0) {
479
+ entry.tags = Array.from(new Set([...(entry.tags ?? []), ...fmTags]));
480
+ }
481
+ }
482
+ return fm;
483
+ }
495
484
  function applyLessonMetadata(entry, ctx) {
496
485
  try {
497
- const parsed = parseFrontmatter(ctx.content());
498
- const fm = parsed.data;
499
- const desc = toStringOrUndefined(fm.description);
500
- if (desc && !entry.description) {
501
- entry.description = desc;
502
- entry.source = "frontmatter";
503
- entry.confidence = 0.9;
504
- }
505
- const whenToUse = toStringOrUndefined(fm.when_to_use);
486
+ const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
487
+ const whenToUse = asNonEmptyString(fm.when_to_use);
506
488
  if (whenToUse) {
507
489
  const hints = new Set(entry.searchHints ?? []);
508
490
  hints.add(`when_to_use:${whenToUse}`);
509
491
  entry.searchHints = Array.from(hints).filter(Boolean);
510
492
  }
511
- if (Array.isArray(fm.tags) && fm.tags.length > 0) {
512
- const fmTags = fm.tags.filter((t) => typeof t === "string" && t.trim().length > 0);
513
- if (fmTags.length > 0) {
514
- entry.tags = Array.from(new Set([...(entry.tags ?? []), ...fmTags]));
515
- }
516
- }
517
493
  }
518
494
  catch {
519
495
  // Non-fatal: skip metadata extraction on parse error
@@ -521,25 +497,12 @@ function applyLessonMetadata(entry, ctx) {
521
497
  }
522
498
  function applyMemoryMetadata(entry, ctx) {
523
499
  try {
524
- const parsed = parseFrontmatter(ctx.content());
525
- const fm = parsed.data;
526
- const desc = toStringOrUndefined(fm.description);
527
- if (desc && !entry.description) {
528
- entry.description = desc;
529
- entry.source = "frontmatter";
530
- entry.confidence = 0.9;
531
- }
532
- if (Array.isArray(fm.tags) && fm.tags.length > 0) {
533
- const fmTags = fm.tags.filter((t) => typeof t === "string" && t.trim().length > 0);
534
- if (fmTags.length > 0) {
535
- entry.tags = Array.from(new Set([...(entry.tags ?? []), ...fmTags]));
536
- }
537
- }
500
+ const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
538
501
  const hints = new Set(entry.searchHints ?? []);
539
- const source = toStringOrUndefined(fm.source);
502
+ const source = asNonEmptyString(fm.source);
540
503
  if (source)
541
504
  hints.add(source);
542
- const fmObservedAt = toStringOrUndefined(fm.observed_at);
505
+ const fmObservedAt = asNonEmptyString(fm.observed_at);
543
506
  if (fmObservedAt) {
544
507
  hints.add(`observed_at:${fmObservedAt}`);
545
508
  }
@@ -552,7 +515,7 @@ function applyMemoryMetadata(entry, ctx) {
552
515
  // Non-fatal: skip mtime fallback on stat error
553
516
  }
554
517
  }
555
- const expires = toStringOrUndefined(fm.expires);
518
+ const expires = asNonEmptyString(fm.expires);
556
519
  if (expires)
557
520
  hints.add(`expires:${expires}`);
558
521
  if (fm.subjective === true)
@@ -587,31 +550,37 @@ function applyVaultMetadata(entry, ctx) {
587
550
  }
588
551
  entry.tags = Array.from(new Set([...(entry.tags ?? []), "vault", "secrets"]));
589
552
  }
553
+ function applyEnvMetadata(entry, ctx) {
554
+ const { keys, comments } = listVaultKeys(ctx.absPath);
555
+ if (comments.length > 0 && !entry.description) {
556
+ entry.description = comments.join(" ").slice(0, 500);
557
+ entry.source = "comments";
558
+ entry.confidence = 0.7;
559
+ }
560
+ if (keys.length > 0) {
561
+ entry.searchHints = keys;
562
+ }
563
+ entry.tags = Array.from(new Set([...(entry.tags ?? []), "env", "secrets"]));
564
+ }
565
+ /**
566
+ * Secret metadata: tags only. Must NEVER read the file body — the whole file
567
+ * is the value, so the entry is built from the filename alone (name-only).
568
+ */
569
+ function applySecretMetadata(entry, _ctx) {
570
+ entry.tags = Array.from(new Set([...(entry.tags ?? []), "secret", "sensitive"]));
571
+ }
590
572
  function applyTaskMetadata(entry, ctx) {
591
573
  try {
592
- const parsed = parseFrontmatter(ctx.content());
593
- const fm = parsed.data;
594
- const desc = toStringOrUndefined(fm.description);
595
- if (desc && !entry.description) {
596
- entry.description = desc;
597
- entry.source = "frontmatter";
598
- entry.confidence = 0.9;
599
- }
600
- if (Array.isArray(fm.tags)) {
601
- const fmTags = fm.tags.filter((t) => typeof t === "string" && t.trim().length > 0);
602
- if (fmTags.length > 0) {
603
- entry.tags = Array.from(new Set([...(entry.tags ?? []), ...fmTags]));
604
- }
605
- }
574
+ const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
606
575
  entry.tags = Array.from(new Set([...(entry.tags ?? []), "task", "scheduled"]));
607
576
  const hints = new Set(entry.searchHints ?? []);
608
- const schedule = toStringOrUndefined(fm.schedule);
577
+ const schedule = asNonEmptyString(fm.schedule);
609
578
  if (schedule)
610
579
  hints.add(`schedule:${schedule}`);
611
- const workflow = toStringOrUndefined(fm.workflow);
580
+ const workflow = asNonEmptyString(fm.workflow);
612
581
  if (workflow)
613
582
  hints.add(`workflow:${workflow}`);
614
- const prompt = toStringOrUndefined(fm.prompt);
583
+ const prompt = asNonEmptyString(fm.prompt);
615
584
  if (prompt)
616
585
  hints.add(`prompt:${prompt}`);
617
586
  if (hints.size > 0)
@@ -622,13 +591,8 @@ function applyTaskMetadata(entry, ctx) {
622
591
  }
623
592
  }
624
593
  registerMetadataContributor({
625
- name: "knowledge-toc-metadata",
626
- appliesTo: ({ rendererName }) => rendererName === "knowledge-md",
627
- contribute: (entry, ctx) => applyTocMetadata(entry, ctx.renderContext),
628
- });
629
- registerMetadataContributor({
630
- name: "wiki-toc-metadata",
631
- appliesTo: ({ rendererName }) => rendererName === "wiki-md",
594
+ name: "toc-metadata",
595
+ appliesTo: ({ rendererName }) => rendererName === "knowledge-md" || rendererName === "wiki-md",
632
596
  contribute: (entry, ctx) => applyTocMetadata(entry, ctx.renderContext),
633
597
  });
634
598
  registerMetadataContributor({
@@ -652,8 +616,18 @@ registerMetadataContributor({
652
616
  contribute: (entry, ctx) => applyVaultMetadata(entry, ctx.renderContext),
653
617
  });
654
618
  registerMetadataContributor({
655
- name: "task-frontmatter-metadata",
656
- appliesTo: ({ rendererName }) => rendererName === "task-md",
619
+ name: "env-file-metadata",
620
+ appliesTo: ({ rendererName }) => rendererName === "env-file",
621
+ contribute: (entry, ctx) => applyEnvMetadata(entry, ctx.renderContext),
622
+ });
623
+ registerMetadataContributor({
624
+ name: "secret-file-metadata",
625
+ appliesTo: ({ rendererName }) => rendererName === "secret-file",
626
+ contribute: (entry, ctx) => applySecretMetadata(entry, ctx.renderContext),
627
+ });
628
+ registerMetadataContributor({
629
+ name: "task-yaml-metadata",
630
+ appliesTo: ({ rendererName }) => rendererName === "task-yaml",
657
631
  contribute: (entry, ctx) => applyTaskMetadata(entry, ctx.renderContext),
658
632
  });
659
633
  // ── Registration ─────────────────────────────────────────────────────────────
@@ -668,7 +642,9 @@ const builtinRenderers = [
668
642
  memoryMdRenderer,
669
643
  workflowMdRenderer,
670
644
  scriptSourceRenderer,
645
+ envFileRenderer,
671
646
  vaultEnvRenderer,
647
+ secretFileRenderer,
672
648
  taskMdRenderer,
673
649
  ];
674
650
  /**
@@ -681,4 +657,4 @@ export function registerBuiltinRenderers() {
681
657
  }
682
658
  }
683
659
  // ── Named exports for testing ────────────────────────────────────────────────
684
- export { agentMdRenderer, commandMdRenderer, INTERPRETER_MAP, knowledgeMdRenderer, lessonMdRenderer, memoryMdRenderer, SETUP_SIGNALS, scriptSourceRenderer, skillMdRenderer, vaultEnvRenderer, wikiMdRenderer, workflowMdRenderer, };
660
+ export { agentMdRenderer, commandMdRenderer, envFileRenderer, INTERPRETER_MAP, knowledgeMdRenderer, lessonMdRenderer, memoryMdRenderer, SETUP_SIGNALS, scriptSourceRenderer, secretFileRenderer, skillMdRenderer, vaultEnvRenderer, wikiMdRenderer, workflowMdRenderer, };
@@ -0,0 +1,56 @@
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 { capDescription, NORMAL_DESCRIPTION_LIMIT, pickFields } from "./helpers";
5
+ import { registerOutputShape } from "./registry";
6
+ // Curation is a small, high-signal top-N. Even at `brief` we keep `followUp`
7
+ // (the actionable `akm show <ref>` command) and `reason` (why this asset was
8
+ // selected) — these are the point of curate, unlike a bulk search listing.
9
+ const BRIEF_FIELDS = ["source", "type", "name", "ref", "id", "followUp", "reason"];
10
+ const NORMAL_FIELDS = [
11
+ "source",
12
+ "type",
13
+ "name",
14
+ "ref",
15
+ "id",
16
+ "description",
17
+ "preview",
18
+ "keys",
19
+ "parameters",
20
+ "run",
21
+ "followUp",
22
+ "reason",
23
+ "score",
24
+ ];
25
+ // Agent shape: the minimal field set an LLM needs to decide and act.
26
+ const AGENT_FIELDS = ["source", "type", "name", "ref", "id", "description", "followUp", "reason", "score"];
27
+ function shapeCurateItem(item, detail, shape) {
28
+ if (shape === "agent") {
29
+ return capDescription(pickFields(item, AGENT_FIELDS), NORMAL_DESCRIPTION_LIMIT);
30
+ }
31
+ if (detail === "brief") {
32
+ return pickFields(item, BRIEF_FIELDS);
33
+ }
34
+ if (detail === "normal") {
35
+ return capDescription(pickFields(item, NORMAL_FIELDS), NORMAL_DESCRIPTION_LIMIT);
36
+ }
37
+ // full: project everything the curator emits.
38
+ return item;
39
+ }
40
+ export function shapeCurateOutput(result, detail, shape) {
41
+ const items = Array.isArray(result.items) ? result.items : [];
42
+ const shapedItems = items.map((item) => shapeCurateItem(item, detail, shape));
43
+ const base = {
44
+ // `shape`/`schemaVersion` discriminators preserve the prior passthrough
45
+ // envelope contract (#484) so consumers can pin a schema version.
46
+ schemaVersion: typeof result.schemaVersion === "number" ? result.schemaVersion : 1,
47
+ shape: "curate",
48
+ query: result.query,
49
+ summary: result.summary,
50
+ items: shapedItems,
51
+ ...(Array.isArray(result.warnings) && result.warnings.length > 0 ? { warnings: result.warnings } : {}),
52
+ ...(result.tip ? { tip: result.tip } : {}),
53
+ };
54
+ return base;
55
+ }
56
+ registerOutputShape("curate", (result, detail, shape) => shapeCurateOutput(result, detail, shape));
@@ -0,0 +1,10 @@
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 distill <ref>` (#228). The shape is
5
+ // simple — outcome + ids + optional payload — so `brief` strips the full
6
+ // proposal blob, `normal` keeps the headline fields, and `full` projects
7
+ // everything for downstream automation.
8
+ import { shapeDistillOutput } from "./helpers";
9
+ import { registerOutputShape } from "./registry";
10
+ registerOutputShape("distill", (result, detail) => shapeDistillOutput(result, detail));
@@ -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
+ // env-list strips `path` from each env object (security: avoid leaking
5
+ // absolute disk paths) then stamps the envelope.
6
+ import { registerOutputShape } from "./registry";
7
+ registerOutputShape("env-list", (result) => {
8
+ const r = result;
9
+ const envs = Array.isArray(r.envs) ? r.envs : [];
10
+ return {
11
+ ...r,
12
+ shape: r.shape ?? "env-list",
13
+ schemaVersion: r.schemaVersion ?? 1,
14
+ envs: envs.map((v) => {
15
+ const { path: _path, ...rest } = v;
16
+ return rest;
17
+ }),
18
+ };
19
+ });
@@ -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 events list` and `akm events tail` (#204).
5
+ // Both share the same envelope; the renderer in text.ts uses distinct command
6
+ // names so it can format streaming differently.
7
+ import { shapeEventsOutput } from "./helpers";
8
+ import { registerOutputShape } from "./registry";
9
+ const handler = (result, detail) => shapeEventsOutput(result, detail);
10
+ registerOutputShape("events-list", handler);
11
+ registerOutputShape("events-tail", handler);