akm-cli 0.6.1 → 0.7.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 (333) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/{cli.js → src/cli.js} +712 -34
  3. package/dist/{commands → src/commands}/config-cli.js +47 -4
  4. package/dist/src/commands/distill.js +283 -0
  5. package/dist/src/commands/events.js +108 -0
  6. package/dist/src/commands/history.js +191 -0
  7. package/dist/{commands → src/commands}/installed-stashes.js +1 -1
  8. package/dist/src/commands/proposal.js +119 -0
  9. package/dist/src/commands/propose.js +171 -0
  10. package/dist/src/commands/reflect.js +193 -0
  11. package/dist/{commands → src/commands}/registry-search.js +71 -7
  12. package/dist/{commands → src/commands}/remember.js +12 -0
  13. package/dist/{commands → src/commands}/search.js +104 -4
  14. package/dist/{commands → src/commands}/self-update.js +4 -3
  15. package/dist/{commands → src/commands}/show.js +73 -0
  16. package/dist/{commands → src/commands}/source-add.js +5 -1
  17. package/dist/{commands → src/commands}/source-manage.js +7 -1
  18. package/dist/{core → src/core}/asset-ref.js +5 -5
  19. package/dist/{core → src/core}/asset-spec.js +12 -0
  20. package/dist/{core → src/core}/common.js +1 -1
  21. package/dist/{core → src/core}/config.js +203 -121
  22. package/dist/{core → src/core}/errors.js +4 -0
  23. package/dist/src/core/events.js +239 -0
  24. package/dist/src/core/lesson-lint.js +86 -0
  25. package/dist/src/core/proposals.js +406 -0
  26. package/dist/src/core/warn.js +72 -0
  27. package/dist/{core → src/core}/write-source.js +80 -5
  28. package/dist/{indexer → src/indexer}/db-search.js +114 -24
  29. package/dist/{indexer → src/indexer}/db.js +76 -23
  30. package/dist/{indexer → src/indexer}/file-context.js +0 -3
  31. package/dist/src/indexer/graph-boost.js +179 -0
  32. package/dist/src/indexer/graph-extraction.js +212 -0
  33. package/dist/{indexer → src/indexer}/indexer.js +88 -7
  34. package/dist/{indexer → src/indexer}/matchers.js +1 -1
  35. package/dist/src/indexer/memory-inference.js +263 -0
  36. package/dist/{indexer → src/indexer}/metadata.js +111 -3
  37. package/dist/{indexer → src/indexer}/search-source.js +4 -2
  38. package/dist/src/integrations/agent/config.js +292 -0
  39. package/dist/src/integrations/agent/detect.js +94 -0
  40. package/dist/src/integrations/agent/index.js +17 -0
  41. package/dist/src/integrations/agent/profiles.js +65 -0
  42. package/dist/src/integrations/agent/prompts.js +167 -0
  43. package/dist/src/integrations/agent/spawn.js +272 -0
  44. package/dist/{integrations → src/integrations}/github.js +9 -3
  45. package/dist/{integrations → src/integrations}/lockfile.js +0 -26
  46. package/dist/{llm → src/llm}/client.js +33 -2
  47. package/dist/{llm → src/llm}/embedders/remote.js +37 -3
  48. package/dist/src/llm/feature-gate.js +108 -0
  49. package/dist/src/llm/graph-extract.js +107 -0
  50. package/dist/src/llm/index-passes.js +35 -0
  51. package/dist/src/llm/memory-infer.js +86 -0
  52. package/dist/{output → src/output}/cli-hints.js +15 -2
  53. package/dist/{output → src/output}/renderers.js +63 -2
  54. package/dist/src/output/shapes.js +523 -0
  55. package/dist/src/output/text.js +1116 -0
  56. package/dist/{registry → src/registry}/build-index.js +19 -8
  57. package/dist/{registry → src/registry}/factory.js +0 -8
  58. package/dist/{registry → src/registry}/providers/static-index.js +6 -3
  59. package/dist/{registry → src/registry}/resolve.js +68 -2
  60. package/dist/{setup → src/setup}/setup.js +52 -5
  61. package/dist/{sources → src/sources}/providers/git.js +7 -15
  62. package/dist/{wiki → src/wiki}/wiki.js +54 -6
  63. package/dist/{workflows → src/workflows}/runs.js +37 -3
  64. package/dist/tests/add-website-source.test.js +119 -0
  65. package/dist/tests/agent/agent-config-loader.test.js +70 -0
  66. package/dist/tests/agent/agent-config.test.js +221 -0
  67. package/dist/tests/agent/agent-detect.test.js +100 -0
  68. package/dist/tests/agent/agent-spawn.test.js +234 -0
  69. package/dist/tests/agent-output.test.js +186 -0
  70. package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +103 -0
  71. package/dist/tests/architecture/agent-spawn-seam.test.js +193 -0
  72. package/dist/tests/architecture/llm-stateless-seam.test.js +112 -0
  73. package/dist/tests/asset-ref.test.js +192 -0
  74. package/dist/tests/asset-registry.test.js +103 -0
  75. package/dist/tests/asset-spec.test.js +241 -0
  76. package/dist/tests/bench/attribution.test.js +996 -0
  77. package/dist/tests/bench/cleanup-sigint.test.js +83 -0
  78. package/dist/tests/bench/cleanup.js +234 -0
  79. package/dist/tests/bench/cleanup.test.js +166 -0
  80. package/dist/tests/bench/cli.js +1018 -0
  81. package/dist/tests/bench/cli.test.js +445 -0
  82. package/dist/tests/bench/compare.test.js +556 -0
  83. package/dist/tests/bench/corpus.js +317 -0
  84. package/dist/tests/bench/corpus.test.js +258 -0
  85. package/dist/tests/bench/doctor.js +525 -0
  86. package/dist/tests/bench/driver.js +401 -0
  87. package/dist/tests/bench/driver.test.js +584 -0
  88. package/dist/tests/bench/environment.js +233 -0
  89. package/dist/tests/bench/environment.test.js +199 -0
  90. package/dist/tests/bench/evolve-metrics.js +179 -0
  91. package/dist/tests/bench/evolve-metrics.test.js +187 -0
  92. package/dist/tests/bench/evolve.js +647 -0
  93. package/dist/tests/bench/evolve.test.js +624 -0
  94. package/dist/tests/bench/failure-modes.test.js +349 -0
  95. package/dist/tests/bench/feedback-integrity.test.js +457 -0
  96. package/dist/tests/bench/leakage.test.js +228 -0
  97. package/dist/tests/bench/learning-curve.test.js +134 -0
  98. package/dist/tests/bench/metrics.js +2395 -0
  99. package/dist/tests/bench/metrics.test.js +1150 -0
  100. package/dist/tests/bench/no-os-tmpdir-invariant.test.js +43 -0
  101. package/dist/tests/bench/opencode-config.js +194 -0
  102. package/dist/tests/bench/opencode-config.test.js +370 -0
  103. package/dist/tests/bench/report.js +1885 -0
  104. package/dist/tests/bench/report.test.js +1038 -0
  105. package/dist/tests/bench/run-config.js +355 -0
  106. package/dist/tests/bench/run-config.test.js +298 -0
  107. package/dist/tests/bench/run-curate-test.js +32 -0
  108. package/dist/tests/bench/run-failing-tasks.js +56 -0
  109. package/dist/tests/bench/run-full-bench.js +51 -0
  110. package/dist/tests/bench/run-items36-targeted.js +69 -0
  111. package/dist/tests/bench/run-nano-quick.js +42 -0
  112. package/dist/tests/bench/run-waveg-targeted.js +62 -0
  113. package/dist/tests/bench/runner.js +699 -0
  114. package/dist/tests/bench/runner.test.js +958 -0
  115. package/dist/tests/bench/search-bridge.test.js +331 -0
  116. package/dist/tests/bench/tmp.js +131 -0
  117. package/dist/tests/bench/trajectory.js +116 -0
  118. package/dist/tests/bench/trajectory.test.js +127 -0
  119. package/dist/tests/bench/verifier.js +114 -0
  120. package/dist/tests/bench/verifier.test.js +118 -0
  121. package/dist/tests/bench/workflow-evaluator.js +557 -0
  122. package/dist/tests/bench/workflow-evaluator.test.js +421 -0
  123. package/dist/tests/bench/workflow-spec.js +345 -0
  124. package/dist/tests/bench/workflow-spec.test.js +363 -0
  125. package/dist/tests/bench/workflow-trace.js +472 -0
  126. package/dist/tests/bench/workflow-trace.test.js +254 -0
  127. package/dist/tests/benchmark-search-quality.js +536 -0
  128. package/dist/tests/benchmark-suite.js +1441 -0
  129. package/dist/tests/capture-cli.test.js +112 -0
  130. package/dist/tests/cli-errors.test.js +204 -0
  131. package/dist/tests/commands/events.test.js +370 -0
  132. package/dist/tests/commands/history.test.js +418 -0
  133. package/dist/tests/commands/import.test.js +103 -0
  134. package/dist/tests/commands/proposal-cli.test.js +209 -0
  135. package/dist/tests/commands/reflect-propose-cli.test.js +333 -0
  136. package/dist/tests/commands/remember.test.js +97 -0
  137. package/dist/tests/commands/scope-flags.test.js +300 -0
  138. package/dist/tests/commands/search.test.js +537 -0
  139. package/dist/tests/commands/show-indexer-parity.test.js +117 -0
  140. package/dist/tests/commands/show.test.js +294 -0
  141. package/dist/tests/common.test.js +266 -0
  142. package/dist/tests/completions.test.js +142 -0
  143. package/dist/tests/config-cli.test.js +193 -0
  144. package/dist/tests/config-llm-features.test.js +139 -0
  145. package/dist/tests/config.test.js +569 -0
  146. package/dist/tests/contracts/migration-baseline.test.js +43 -0
  147. package/dist/tests/contracts/reflect-propose-envelope.test.js +139 -0
  148. package/dist/tests/contracts/spec-helpers.js +46 -0
  149. package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +228 -0
  150. package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +56 -0
  151. package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +34 -0
  152. package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +94 -0
  153. package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +39 -0
  154. package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +44 -0
  155. package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +47 -0
  156. package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +40 -0
  157. package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +58 -0
  158. package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +34 -0
  159. package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +75 -0
  160. package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +36 -0
  161. package/dist/tests/core/write-source.test.js +366 -0
  162. package/dist/tests/curate-command.test.js +87 -0
  163. package/dist/tests/db-scoring.test.js +201 -0
  164. package/dist/tests/db.test.js +654 -0
  165. package/dist/tests/distill-cli-flag.test.js +208 -0
  166. package/dist/tests/distill.test.js +515 -0
  167. package/dist/tests/docker-install.test.js +120 -0
  168. package/dist/tests/e2e.test.js +1419 -0
  169. package/dist/tests/embedder.test.js +340 -0
  170. package/dist/tests/embedding-model-config.test.js +379 -0
  171. package/dist/tests/feedback-command.test.js +172 -0
  172. package/dist/tests/file-context.test.js +552 -0
  173. package/dist/tests/fixtures/scripts/git/summarize-diff.js +9 -0
  174. package/dist/tests/fixtures/scripts/lint/eslint-check.js +7 -0
  175. package/dist/tests/fixtures/stashes/load.js +166 -0
  176. package/dist/tests/fixtures/stashes/load.test.js +97 -0
  177. package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +12 -0
  178. package/dist/tests/frontmatter.test.js +190 -0
  179. package/dist/tests/fts-field-weighting.test.js +254 -0
  180. package/dist/tests/fuzzy-search.test.js +230 -0
  181. package/dist/tests/git-provider-clone.test.js +45 -0
  182. package/dist/tests/github.test.js +161 -0
  183. package/dist/tests/graph-boost-ranking.test.js +305 -0
  184. package/dist/tests/graph-extraction.test.js +282 -0
  185. package/dist/tests/helpers/usage-events.js +8 -0
  186. package/dist/tests/index-pass-llm.test.js +161 -0
  187. package/dist/tests/indexer.test.js +570 -0
  188. package/dist/tests/info-command.test.js +166 -0
  189. package/dist/tests/init.test.js +69 -0
  190. package/dist/tests/install-script.test.js +246 -0
  191. package/dist/tests/integration/agent-real-profile.test.js +94 -0
  192. package/dist/tests/issue-36-repro.test.js +304 -0
  193. package/dist/tests/issues-191-194.test.js +160 -0
  194. package/dist/tests/lesson-lint.test.js +111 -0
  195. package/dist/tests/llm-client.test.js +115 -0
  196. package/dist/tests/llm-feature-gate.test.js +151 -0
  197. package/dist/tests/llm.test.js +139 -0
  198. package/dist/tests/lockfile.test.js +216 -0
  199. package/dist/tests/manifest.test.js +205 -0
  200. package/dist/tests/markdown.test.js +126 -0
  201. package/dist/tests/matchers-unit.test.js +189 -0
  202. package/dist/tests/memory-inference.test.js +299 -0
  203. package/dist/tests/merge-scoring.test.js +136 -0
  204. package/dist/tests/metadata.test.js +313 -0
  205. package/dist/tests/migration-help.test.js +89 -0
  206. package/dist/tests/origin-resolve.test.js +124 -0
  207. package/dist/tests/output-baseline.test.js +218 -0
  208. package/dist/tests/output-shapes-unit.test.js +478 -0
  209. package/dist/tests/parallel-search.test.js +272 -0
  210. package/dist/tests/parameter-metadata.test.js +365 -0
  211. package/dist/tests/paths.test.js +177 -0
  212. package/dist/tests/progressive-disclosure.test.js +280 -0
  213. package/dist/tests/proposals.test.js +279 -0
  214. package/dist/tests/proposed-quality.test.js +271 -0
  215. package/dist/tests/provider-registry.test.js +32 -0
  216. package/dist/tests/ranking-regression.test.js +548 -0
  217. package/dist/tests/reflect-propose.test.js +455 -0
  218. package/dist/tests/registry-build-index.test.js +394 -0
  219. package/dist/tests/registry-cli.test.js +290 -0
  220. package/dist/tests/registry-index-v2.test.js +430 -0
  221. package/dist/tests/registry-install.test.js +728 -0
  222. package/dist/tests/registry-providers/parity.test.js +189 -0
  223. package/dist/tests/registry-providers/skills-sh.test.js +309 -0
  224. package/dist/tests/registry-providers/static-index.test.js +238 -0
  225. package/dist/tests/registry-resolve.test.js +126 -0
  226. package/dist/tests/registry-search.test.js +923 -0
  227. package/dist/tests/remember-frontmatter.test.js +378 -0
  228. package/dist/tests/remember-unit.test.js +123 -0
  229. package/dist/tests/ripgrep-install.test.js +251 -0
  230. package/dist/tests/ripgrep-resolve.test.js +108 -0
  231. package/dist/tests/ripgrep.test.js +163 -0
  232. package/dist/tests/save-command.test.js +94 -0
  233. package/dist/tests/save-trust-qa-fixes.test.js +270 -0
  234. package/dist/tests/scoring-pipeline.test.js +648 -0
  235. package/dist/tests/search-include-proposed-cli.test.js +118 -0
  236. package/dist/tests/self-update.test.js +442 -0
  237. package/dist/tests/semantic-search-e2e.test.js +512 -0
  238. package/dist/tests/semantic-status.test.js +471 -0
  239. package/dist/tests/setup-run.integration.js +877 -0
  240. package/dist/tests/setup-wizard.test.js +198 -0
  241. package/dist/tests/setup.test.js +131 -0
  242. package/dist/tests/source-add.test.js +11 -0
  243. package/dist/tests/source-clone.test.js +254 -0
  244. package/dist/tests/source-manage.test.js +366 -0
  245. package/dist/tests/source-providers/filesystem.test.js +82 -0
  246. package/dist/tests/source-providers/git.test.js +252 -0
  247. package/dist/tests/source-providers/website.test.js +128 -0
  248. package/dist/tests/source-qa-fixes.test.js +286 -0
  249. package/dist/tests/source-registry.test.js +350 -0
  250. package/dist/tests/source-resolve.test.js +100 -0
  251. package/dist/tests/source-source.test.js +281 -0
  252. package/dist/tests/source.test.js +533 -0
  253. package/dist/tests/tar-utils-scan.test.js +73 -0
  254. package/dist/tests/toggle-components.test.js +73 -0
  255. package/dist/tests/usage-telemetry.test.js +265 -0
  256. package/dist/tests/utility-scoring.test.js +558 -0
  257. package/dist/tests/vault-load-error.test.js +78 -0
  258. package/dist/tests/vault-qa-fixes.test.js +194 -0
  259. package/dist/tests/vault.test.js +429 -0
  260. package/dist/tests/vector-search.test.js +608 -0
  261. package/dist/tests/walker.test.js +252 -0
  262. package/dist/tests/wave2-cluster-bc.test.js +228 -0
  263. package/dist/tests/wave2-cluster-d.test.js +180 -0
  264. package/dist/tests/wave2-cluster-e.test.js +179 -0
  265. package/dist/tests/wiki-qa-fixes.test.js +270 -0
  266. package/dist/tests/wiki.test.js +529 -0
  267. package/dist/tests/workflow-cli.test.js +271 -0
  268. package/dist/tests/workflow-markdown.test.js +171 -0
  269. package/dist/tests/workflow-path-escape.test.js +132 -0
  270. package/dist/tests/workflow-qa-fixes.test.js +395 -0
  271. package/dist/tests/workflows/indexer-rejection.test.js +213 -0
  272. package/docs/README.md +8 -0
  273. package/docs/migration/release-notes/0.7.0.md +244 -0
  274. package/package.json +2 -2
  275. package/dist/core/warn.js +0 -27
  276. package/dist/output/shapes.js +0 -212
  277. package/dist/output/text.js +0 -520
  278. /package/dist/{commands → src/commands}/completions.js +0 -0
  279. /package/dist/{commands → src/commands}/curate.js +0 -0
  280. /package/dist/{commands → src/commands}/info.js +0 -0
  281. /package/dist/{commands → src/commands}/init.js +0 -0
  282. /package/dist/{commands → src/commands}/install-audit.js +0 -0
  283. /package/dist/{commands → src/commands}/migration-help.js +0 -0
  284. /package/dist/{commands → src/commands}/source-clone.js +0 -0
  285. /package/dist/{commands → src/commands}/vault.js +0 -0
  286. /package/dist/{core → src/core}/asset-registry.js +0 -0
  287. /package/dist/{core → src/core}/frontmatter.js +0 -0
  288. /package/dist/{core → src/core}/markdown.js +0 -0
  289. /package/dist/{core → src/core}/paths.js +0 -0
  290. /package/dist/{indexer → src/indexer}/manifest.js +0 -0
  291. /package/dist/{indexer → src/indexer}/search-fields.js +0 -0
  292. /package/dist/{indexer → src/indexer}/semantic-status.js +0 -0
  293. /package/dist/{indexer → src/indexer}/usage-events.js +0 -0
  294. /package/dist/{indexer → src/indexer}/walker.js +0 -0
  295. /package/dist/{llm → src/llm}/embedder.js +0 -0
  296. /package/dist/{llm → src/llm}/embedders/cache.js +0 -0
  297. /package/dist/{llm → src/llm}/embedders/local.js +0 -0
  298. /package/dist/{llm → src/llm}/embedders/types.js +0 -0
  299. /package/dist/{llm → src/llm}/metadata-enhance.js +0 -0
  300. /package/dist/{output → src/output}/context.js +0 -0
  301. /package/dist/{registry → src/registry}/create-provider-registry.js +0 -0
  302. /package/dist/{registry → src/registry}/origin-resolve.js +0 -0
  303. /package/dist/{registry → src/registry}/providers/index.js +0 -0
  304. /package/dist/{registry → src/registry}/providers/skills-sh.js +0 -0
  305. /package/dist/{registry → src/registry}/providers/types.js +0 -0
  306. /package/dist/{registry → src/registry}/types.js +0 -0
  307. /package/dist/{setup → src/setup}/detect.js +0 -0
  308. /package/dist/{setup → src/setup}/ripgrep-install.js +0 -0
  309. /package/dist/{setup → src/setup}/ripgrep-resolve.js +0 -0
  310. /package/dist/{setup → src/setup}/steps.js +0 -0
  311. /package/dist/{sources → src/sources}/include.js +0 -0
  312. /package/dist/{sources → src/sources}/provider-factory.js +0 -0
  313. /package/dist/{sources → src/sources}/provider.js +0 -0
  314. /package/dist/{sources → src/sources}/providers/filesystem.js +0 -0
  315. /package/dist/{sources → src/sources}/providers/index.js +0 -0
  316. /package/dist/{sources → src/sources}/providers/install-types.js +0 -0
  317. /package/dist/{sources → src/sources}/providers/npm.js +0 -0
  318. /package/dist/{sources → src/sources}/providers/provider-utils.js +0 -0
  319. /package/dist/{sources → src/sources}/providers/sync-from-ref.js +0 -0
  320. /package/dist/{sources → src/sources}/providers/tar-utils.js +0 -0
  321. /package/dist/{sources → src/sources}/providers/website.js +0 -0
  322. /package/dist/{sources → src/sources}/resolve.js +0 -0
  323. /package/dist/{sources → src/sources}/types.js +0 -0
  324. /package/dist/{templates → src/templates}/wiki-templates.js +0 -0
  325. /package/dist/{version.js → src/version.js} +0 -0
  326. /package/dist/{workflows → src/workflows}/authoring.js +0 -0
  327. /package/dist/{workflows → src/workflows}/cli.js +0 -0
  328. /package/dist/{workflows → src/workflows}/db.js +0 -0
  329. /package/dist/{workflows → src/workflows}/document-cache.js +0 -0
  330. /package/dist/{workflows → src/workflows}/parser.js +0 -0
  331. /package/dist/{workflows → src/workflows}/renderer.js +0 -0
  332. /package/dist/{workflows → src/workflows}/schema.js +0 -0
  333. /package/dist/{workflows → src/workflows}/validator.js +0 -0
@@ -0,0 +1,1116 @@
1
+ /**
2
+ * Plain-text formatters for command output. Each top-level `formatPlain`
3
+ * branch dispatches to a small per-command helper. Returning `null` means
4
+ * "no plain rendering available — fall back to YAML".
5
+ *
6
+ * Pure functions — no IO.
7
+ */
8
+ import { formatInstallAuditSummary } from "../commands/install-audit";
9
+ export function outputJsonl(command, shaped) {
10
+ if (command === "search" || command === "registry-search") {
11
+ const r = shaped;
12
+ const hits = Array.isArray(r.hits) ? r.hits : [];
13
+ for (const hit of hits) {
14
+ console.log(JSON.stringify(hit));
15
+ }
16
+ const registryHits = Array.isArray(r.registryHits) ? r.registryHits : [];
17
+ for (const hit of registryHits) {
18
+ console.log(JSON.stringify(hit));
19
+ }
20
+ return;
21
+ }
22
+ // For non-search commands, output the whole object as a single JSONL line
23
+ console.log(JSON.stringify(shaped));
24
+ }
25
+ /**
26
+ * Return a plain-text string for commands that are better as short messages,
27
+ * or null to fall through to YAML output.
28
+ */
29
+ export function formatPlain(command, result, detail) {
30
+ const r = result;
31
+ switch (command) {
32
+ case "init": {
33
+ let out = `Stash initialized at ${r.stashDir ?? "unknown"}`;
34
+ if (r.configPath)
35
+ out += `\nConfig saved to ${r.configPath}`;
36
+ return out;
37
+ }
38
+ case "index": {
39
+ const indexResult = result;
40
+ let out = `Indexed ${indexResult.totalEntries ?? 0} entries from ${indexResult.directoriesScanned ?? 0} directories (mode: ${indexResult.mode ?? "unknown"})`;
41
+ const warnings = indexResult.warnings;
42
+ if (Array.isArray(warnings) && warnings.length > 0) {
43
+ out += `\nWarnings (${warnings.length}):`;
44
+ for (const message of warnings)
45
+ out += `\n - ${String(message)}`;
46
+ }
47
+ const verification = indexResult.verification;
48
+ if (verification?.ok === false && verification.message) {
49
+ out += `\nVerification: ${String(verification.message)}`;
50
+ }
51
+ return out;
52
+ }
53
+ case "show": {
54
+ return formatShowPlain(r, detail);
55
+ }
56
+ case "search": {
57
+ return formatSearchPlain(r, detail);
58
+ }
59
+ case "curate": {
60
+ return formatCuratePlain(r, detail);
61
+ }
62
+ case "wiki-list": {
63
+ return formatWikiListPlain(r);
64
+ }
65
+ case "wiki-show": {
66
+ return formatWikiShowPlain(r);
67
+ }
68
+ case "wiki-create": {
69
+ return formatWikiCreatePlain(r);
70
+ }
71
+ case "wiki-remove": {
72
+ return formatWikiRemovePlain(r);
73
+ }
74
+ case "wiki-pages": {
75
+ return formatWikiPagesPlain(r);
76
+ }
77
+ case "wiki-stash": {
78
+ return formatWikiStashPlain(r);
79
+ }
80
+ case "wiki-lint": {
81
+ return formatWikiLintPlain(r);
82
+ }
83
+ case "wiki-ingest": {
84
+ return formatWikiIngestPlain(r);
85
+ }
86
+ case "workflow-start":
87
+ case "workflow-status":
88
+ case "workflow-complete": {
89
+ return formatWorkflowStatusPlain(r);
90
+ }
91
+ case "workflow-next": {
92
+ return formatWorkflowNextPlain(r);
93
+ }
94
+ case "workflow-list": {
95
+ return formatWorkflowListPlain(r);
96
+ }
97
+ case "workflow-create": {
98
+ if (r.ref && r.path) {
99
+ return `Created ${String(r.ref)} at ${String(r.path)}`;
100
+ }
101
+ return null;
102
+ }
103
+ case "list": {
104
+ const sources = Array.isArray(r.sources) ? r.sources : [];
105
+ if (sources.length === 0)
106
+ return "No sources configured. Use `akm add` to add a source.";
107
+ const lines = [];
108
+ for (const src of sources) {
109
+ const kind = typeof src.kind === "string" ? src.kind : "unknown";
110
+ const name = typeof src.name === "string" ? src.name : "unnamed";
111
+ const ver = typeof src.version === "string" ? ` v${src.version}` : "";
112
+ const prov = typeof src.provider === "string" ? ` (${src.provider})` : "";
113
+ const flags = [];
114
+ if (typeof src.wiki === "string")
115
+ flags.push(`wiki:${src.wiki}`);
116
+ if (src.updatable === true)
117
+ flags.push("updatable");
118
+ if (src.writable === true)
119
+ flags.push("writable");
120
+ const flagText = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
121
+ lines.push(`[${kind}] ${name}${ver}${prov}${flagText}`);
122
+ }
123
+ lines.push("");
124
+ lines.push("To search: akm search '<query>' | To view an asset: akm show <ref>");
125
+ return lines.join("\n");
126
+ }
127
+ case "add": {
128
+ const index = r.index;
129
+ const scanned = index?.directoriesScanned ?? 0;
130
+ const total = index?.totalEntries ?? 0;
131
+ const lines = [`Installed ${r.ref} (${scanned} directories scanned, ${total} total assets indexed)`];
132
+ const warnings = index?.warnings;
133
+ if (Array.isArray(warnings) && warnings.length > 0) {
134
+ lines.push(`Warnings (${warnings.length}):`);
135
+ for (const message of warnings)
136
+ lines.push(` - ${String(message)}`);
137
+ }
138
+ const installed = r.installed;
139
+ const audit = installed?.audit;
140
+ if (audit && typeof audit === "object") {
141
+ lines.push(formatInstallAuditSummary(audit));
142
+ }
143
+ return lines.join("\n");
144
+ }
145
+ case "remove": {
146
+ const target = r.target ?? r.ref ?? "";
147
+ const ok = r.ok !== false ? "OK" : "FAILED";
148
+ return `remove: ${target} ${ok}`;
149
+ }
150
+ case "update": {
151
+ const processed = r.processed;
152
+ if (!processed?.length)
153
+ return `update: nothing to update`;
154
+ const lines = processed.map((item) => {
155
+ const changed = item.changed;
156
+ const installed = item.installed;
157
+ const previous = item.previous;
158
+ if (changed?.any) {
159
+ const prev = previous?.resolvedVersion ?? "unknown";
160
+ const next = installed?.resolvedVersion ?? "unknown";
161
+ return `update: ${item.id} v${prev} → v${next}`;
162
+ }
163
+ return `update: ${item.id} (unchanged)`;
164
+ });
165
+ return lines.join("\n");
166
+ }
167
+ case "upgrade": {
168
+ if (r.upgraded === true) {
169
+ return `akm upgraded: v${r.currentVersion} → v${r.newVersion}`;
170
+ }
171
+ if (r.updateAvailable === true) {
172
+ return `akm v${r.currentVersion} → v${r.latestVersion} available (run 'akm upgrade' to install)`;
173
+ }
174
+ if (r.updateAvailable === false && r.latestVersion) {
175
+ return `akm v${r.currentVersion} is already the latest version`;
176
+ }
177
+ if (r.message)
178
+ return String(r.message);
179
+ return null;
180
+ }
181
+ case "clone": {
182
+ const dst = r.destination?.path ?? "unknown";
183
+ const remote = r.remoteFetched ? " (fetched from remote)" : "";
184
+ const over = r.overwritten ? " (overwritten)" : "";
185
+ return `Cloned${remote} → ${dst}${over}`;
186
+ }
187
+ // Output shape registration for `akm history` — paired with the shape function in shapes.ts.
188
+ case "history": {
189
+ return formatHistoryPlain(r);
190
+ }
191
+ // Output shape registration for `akm events list` / `akm events tail`
192
+ // (#204). Both share a renderer; `events-tail` is also called per-event
193
+ // by the streaming code path via `formatEventLine`.
194
+ case "events-list":
195
+ case "events-tail": {
196
+ return formatEventsPlain(r);
197
+ }
198
+ // Output shape registration for `akm proposal *` (#225).
199
+ case "proposal-list": {
200
+ return formatProposalListPlain(r);
201
+ }
202
+ case "proposal-show": {
203
+ return formatProposalShowPlain(r);
204
+ }
205
+ case "proposal-accept": {
206
+ return formatProposalAcceptPlain(r);
207
+ }
208
+ case "proposal-reject": {
209
+ return formatProposalRejectPlain(r);
210
+ }
211
+ case "proposal-diff": {
212
+ return formatProposalDiffPlain(r);
213
+ }
214
+ // Output shape registration for `akm reflect` / `akm propose` (#226).
215
+ case "reflect":
216
+ case "propose": {
217
+ return formatProposalProducerPlain(command, r);
218
+ }
219
+ // Output shape registration for `akm distill <ref>` (#228). Three branches
220
+ // mirror the three terminal `outcome` values.
221
+ case "distill": {
222
+ return formatDistillPlain(r);
223
+ }
224
+ case "info":
225
+ return formatInfoPlain(r);
226
+ case "config":
227
+ return formatConfigPlain(r);
228
+ case "feedback":
229
+ return formatFeedbackPlain(r);
230
+ case "remember":
231
+ return formatRememberPlain(r);
232
+ case "import":
233
+ return formatImportPlain(r);
234
+ case "save":
235
+ return formatSavePlain(r);
236
+ case "enable":
237
+ case "disable":
238
+ return formatToggleComponentPlain(command, r);
239
+ case "registry-list":
240
+ return formatRegistryListPlain(r);
241
+ case "registry-add":
242
+ return formatRegistryAddPlain(r);
243
+ case "registry-remove":
244
+ return formatRegistryRemovePlain(r);
245
+ case "registry-search":
246
+ return formatRegistrySearchPlain(r, detail);
247
+ case "registry-build-index":
248
+ return formatRegistryBuildIndexPlain(r);
249
+ case "vault-list":
250
+ return formatVaultListPlain(r);
251
+ case "vault-create":
252
+ return `Created vault ${String(r.ref ?? "?")} at ${String(r.path ?? "?")}`;
253
+ case "vault-set":
254
+ return `Set ${String(r.key ?? "?")} in ${String(r.ref ?? "?")} (value not displayed)`;
255
+ case "vault-unset": {
256
+ const removed = r.removed === true;
257
+ const head = removed
258
+ ? `Removed ${String(r.key ?? "?")} from ${String(r.ref ?? "?")}`
259
+ : `Key ${String(r.key ?? "?")} was not present in ${String(r.ref ?? "?")}`;
260
+ return head;
261
+ }
262
+ case "wiki-register":
263
+ return formatWikiRegisterPlain(r);
264
+ case "workflow-resume":
265
+ return formatWorkflowStatusPlain(r) ?? `Resumed workflow run ${String(r.id ?? r.runId ?? "?")}`;
266
+ case "workflow-validate":
267
+ return formatWorkflowValidatePlain(r);
268
+ default:
269
+ return null; // fall through to YAML
270
+ }
271
+ }
272
+ export function formatInfoPlain(r) {
273
+ const lines = [];
274
+ if (r.version)
275
+ lines.push(`version: ${String(r.version)}`);
276
+ if (r.stashDir)
277
+ lines.push(`stashDir: ${String(r.stashDir)}`);
278
+ if (r.configPath)
279
+ lines.push(`configPath: ${String(r.configPath)}`);
280
+ if (r.cacheDir)
281
+ lines.push(`cacheDir: ${String(r.cacheDir)}`);
282
+ if (r.dbPath)
283
+ lines.push(`dbPath: ${String(r.dbPath)}`);
284
+ const capabilities = r.capabilities;
285
+ if (capabilities) {
286
+ lines.push("capabilities:");
287
+ for (const [k, v] of Object.entries(capabilities)) {
288
+ lines.push(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : String(v)}`);
289
+ }
290
+ }
291
+ const indexStats = r.index;
292
+ if (indexStats) {
293
+ lines.push("index:");
294
+ for (const [k, v] of Object.entries(indexStats)) {
295
+ lines.push(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : String(v)}`);
296
+ }
297
+ }
298
+ if (lines.length === 0)
299
+ return JSON.stringify(r, null, 2);
300
+ return lines.join("\n");
301
+ }
302
+ export function formatConfigPlain(r) {
303
+ // Recursive flattener: prints `key=value` lines, and nested objects as
304
+ // `parent.child=value`. Arrays render as JSON for compactness.
305
+ const lines = [];
306
+ const walk = (obj, prefix) => {
307
+ for (const [k, v] of Object.entries(obj)) {
308
+ const path = prefix ? `${prefix}.${k}` : k;
309
+ if (v === null || v === undefined) {
310
+ lines.push(`${path}=`);
311
+ }
312
+ else if (Array.isArray(v)) {
313
+ lines.push(`${path}=${JSON.stringify(v)}`);
314
+ }
315
+ else if (typeof v === "object") {
316
+ walk(v, path);
317
+ }
318
+ else {
319
+ lines.push(`${path}=${String(v)}`);
320
+ }
321
+ }
322
+ };
323
+ walk(r, "");
324
+ if (lines.length === 0)
325
+ return "(empty config)";
326
+ return lines.join("\n");
327
+ }
328
+ export function formatFeedbackPlain(r) {
329
+ const ref = String(r.ref ?? "?");
330
+ const signal = String(r.signal ?? "?");
331
+ const note = typeof r.note === "string" && r.note ? ` — ${r.note}` : "";
332
+ return `Recorded ${signal} feedback for ${ref}${note}`;
333
+ }
334
+ export function formatRememberPlain(r) {
335
+ const ref = String(r.ref ?? "?");
336
+ const pathValue = String(r.path ?? "?");
337
+ return `Saved ${ref} at ${pathValue}`;
338
+ }
339
+ export function formatImportPlain(r) {
340
+ const ref = String(r.ref ?? "?");
341
+ const source = String(r.source ?? "?");
342
+ const pathValue = String(r.path ?? "?");
343
+ return `Imported ${source} → ${ref} at ${pathValue}`;
344
+ }
345
+ export function formatSavePlain(r) {
346
+ if (r.ok === false) {
347
+ const reason = typeof r.reason === "string" ? r.reason : "unknown";
348
+ return `save: failed (${reason})`;
349
+ }
350
+ const name = typeof r.name === "string" ? r.name : "primary stash";
351
+ const committed = r.committed === true;
352
+ const pushed = r.pushed === true;
353
+ const parts = [`save: ${name}`];
354
+ parts.push(committed ? "committed" : "no changes");
355
+ if (pushed)
356
+ parts.push("pushed");
357
+ return parts.join(" — ");
358
+ }
359
+ export function formatToggleComponentPlain(command, r) {
360
+ const verb = command === "enable" ? "Enabled" : "Disabled";
361
+ const component = String(r.component ?? "?");
362
+ const changed = r.changed === true;
363
+ return changed ? `${verb} ${component}` : `${component} was already ${command}d`;
364
+ }
365
+ export function formatRegistryListPlain(r) {
366
+ const registries = Array.isArray(r.registries) ? r.registries : [];
367
+ if (registries.length === 0) {
368
+ return "No registries configured. Add one with `akm registry add <url>`.";
369
+ }
370
+ const lines = [];
371
+ for (const reg of registries) {
372
+ const url = String(reg.url ?? "?");
373
+ const name = typeof reg.name === "string" ? reg.name : "";
374
+ const provider = typeof reg.provider === "string" ? ` (${reg.provider})` : "";
375
+ const enabled = reg.enabled === false ? " [disabled]" : "";
376
+ const head = name ? `${name}: ${url}` : url;
377
+ lines.push(`${head}${provider}${enabled}`);
378
+ }
379
+ return lines.join("\n");
380
+ }
381
+ export function formatRegistryAddPlain(r) {
382
+ if (r.added === false) {
383
+ return typeof r.message === "string" ? r.message : "Registry already configured.";
384
+ }
385
+ const registries = Array.isArray(r.registries) ? r.registries.length : 0;
386
+ return `Registry added (${registries} total).`;
387
+ }
388
+ export function formatRegistryRemovePlain(r) {
389
+ if (r.removed === false) {
390
+ return typeof r.message === "string" ? r.message : "No matching registry found.";
391
+ }
392
+ const entry = r.entry;
393
+ const url = entry ? String(entry.url ?? entry.name ?? "?") : "?";
394
+ return `Removed registry ${url}`;
395
+ }
396
+ export function formatRegistrySearchPlain(r, detail) {
397
+ // Reuse the same renderer as `search` — both share `hits` / `registryHits`.
398
+ return formatSearchPlain(r, detail);
399
+ }
400
+ export function formatRegistryBuildIndexPlain(r) {
401
+ const outPath = String(r.outPath ?? "?");
402
+ const total = typeof r.totalKits === "number" ? r.totalKits : 0;
403
+ const version = typeof r.version === "number" ? `v${r.version}` : "";
404
+ return `Wrote registry index ${version} (${total} kits) → ${outPath}`.replace(/\s+/g, " ").trim();
405
+ }
406
+ export function formatVaultListPlain(r) {
407
+ // Single-vault listing: { ref, path, entries: [{ key, comment? }, ...] }
408
+ if (typeof r.ref === "string" && Array.isArray(r.entries)) {
409
+ const ref = r.ref;
410
+ const entries = r.entries;
411
+ if (entries.length === 0) {
412
+ return `No keys in ${ref}. Set one with \`akm vault set ${ref} KEY=VALUE\`.`;
413
+ }
414
+ const lines = [ref];
415
+ for (const e of entries) {
416
+ const key = String(e.key ?? "?");
417
+ const comment = typeof e.comment === "string" && e.comment ? ` # ${e.comment}` : "";
418
+ lines.push(` ${key}${comment}`);
419
+ }
420
+ return lines.join("\n");
421
+ }
422
+ // Multi-vault listing: { vaults: [{ ref, path, keyCount }, ...] }
423
+ const vaults = Array.isArray(r.vaults) ? r.vaults : [];
424
+ if (vaults.length === 0) {
425
+ return "No vaults. Create one with `akm vault create <name>` then `akm vault set vault:<name> KEY=VALUE`.";
426
+ }
427
+ const lines = [];
428
+ for (const v of vaults) {
429
+ const ref = String(v.ref ?? "?");
430
+ const keyCount = typeof v.keyCount === "number" ? v.keyCount : 0;
431
+ lines.push(`${ref}\t${keyCount} key(s)`);
432
+ }
433
+ return lines.join("\n");
434
+ }
435
+ export function formatWikiRegisterPlain(r) {
436
+ const name = String(r.name ?? r.wiki ?? "?");
437
+ const ref = String(r.ref ?? r.path ?? r.url ?? "?");
438
+ return `Registered wiki ${name} → ${ref}`;
439
+ }
440
+ export function formatWorkflowValidatePlain(r) {
441
+ const ok = r.ok !== false;
442
+ const pathValue = String(r.path ?? "?");
443
+ if (!ok)
444
+ return `workflow validate: failed (${pathValue})`;
445
+ const title = typeof r.title === "string" ? r.title : "";
446
+ const stepCount = typeof r.stepCount === "number" ? r.stepCount : 0;
447
+ return `workflow validate: ok — ${title || pathValue} (${stepCount} step(s))`;
448
+ }
449
+ export function formatProposalProducerPlain(command, r) {
450
+ if (r.ok === false) {
451
+ const reason = String(r.reason ?? "unknown");
452
+ const error = typeof r.error === "string" ? r.error : "";
453
+ const lines = [`${command}: failed (${reason})`];
454
+ if (error)
455
+ lines.push(` error: ${error}`);
456
+ if (r.ref)
457
+ lines.push(` ref: ${String(r.ref)}`);
458
+ if (r.exitCode !== undefined && r.exitCode !== null) {
459
+ lines.push(` exitCode: ${String(r.exitCode)}`);
460
+ }
461
+ return lines.join("\n");
462
+ }
463
+ const proposal = r.proposal ?? {};
464
+ const id = String(proposal.id ?? "?");
465
+ const ref = String(r.ref ?? proposal.ref ?? "?");
466
+ const status = String(proposal.status ?? "pending");
467
+ return `${command}: queued proposal ${id} (${ref}) [${status}]`;
468
+ }
469
+ export function formatProposalListPlain(r) {
470
+ const proposals = Array.isArray(r.proposals) ? r.proposals : [];
471
+ const total = typeof r.totalCount === "number" ? r.totalCount : proposals.length;
472
+ if (proposals.length === 0) {
473
+ return `${total} proposal(s).\nNo proposals.\nGenerate one with \`akm reflect <ref>\`, \`akm propose <type> <name> --task ...\`, or \`akm distill <ref>\`.`;
474
+ }
475
+ const lines = [`${total} proposal(s)`, ""];
476
+ for (const p of proposals) {
477
+ const id = String(p.id ?? "?");
478
+ const ref = String(p.ref ?? "?");
479
+ const status = String(p.status ?? "?");
480
+ const source = String(p.source ?? "?");
481
+ const created = String(p.createdAt ?? "?");
482
+ lines.push(`${id} [${status}] ${ref} source=${source} ${created}`);
483
+ }
484
+ return lines.join("\n").trimEnd();
485
+ }
486
+ export function formatProposalShowPlain(r) {
487
+ const p = r.proposal ?? {};
488
+ const lines = [];
489
+ lines.push(`# proposal ${String(p.id ?? "?")}`);
490
+ lines.push(`ref: ${String(p.ref ?? "?")}`);
491
+ lines.push(`status: ${String(p.status ?? "?")}`);
492
+ lines.push(`source: ${String(p.source ?? "?")}`);
493
+ if (p.sourceRun)
494
+ lines.push(`sourceRun: ${String(p.sourceRun)}`);
495
+ if (p.createdAt)
496
+ lines.push(`createdAt: ${String(p.createdAt)}`);
497
+ if (p.updatedAt)
498
+ lines.push(`updatedAt: ${String(p.updatedAt)}`);
499
+ const review = p.review;
500
+ if (review) {
501
+ lines.push(`review.outcome: ${String(review.outcome ?? "?")}`);
502
+ if (review.reason)
503
+ lines.push(`review.reason: ${String(review.reason)}`);
504
+ if (review.decidedAt)
505
+ lines.push(`review.decidedAt: ${String(review.decidedAt)}`);
506
+ }
507
+ const validation = r.validation;
508
+ if (validation) {
509
+ const ok = validation.ok === true;
510
+ const findings = Array.isArray(validation.findings) ? validation.findings : [];
511
+ lines.push("");
512
+ lines.push(`validation: ${ok ? "ok" : `${findings.length} finding(s)`}`);
513
+ for (const f of findings) {
514
+ lines.push(` - [${String(f.kind)}] ${String(f.message)}`);
515
+ }
516
+ }
517
+ const payload = p.payload;
518
+ if (payload && typeof payload.content === "string") {
519
+ lines.push("");
520
+ lines.push("payload:");
521
+ lines.push(payload.content);
522
+ }
523
+ return lines.join("\n").trimEnd();
524
+ }
525
+ export function formatProposalAcceptPlain(r) {
526
+ return `Accepted proposal ${String(r.id ?? "?")} → ${String(r.ref ?? "?")} at ${String(r.assetPath ?? "?")}`;
527
+ }
528
+ export function formatProposalRejectPlain(r) {
529
+ const reason = r.reason ? ` (${String(r.reason)})` : "";
530
+ return `Rejected proposal ${String(r.id ?? "?")} (${String(r.ref ?? "?")})${reason}`;
531
+ }
532
+ export function formatDistillPlain(r) {
533
+ const outcome = String(r.outcome ?? "unknown");
534
+ const inputRef = String(r.inputRef ?? "?");
535
+ const lessonRef = String(r.lessonRef ?? "?");
536
+ if (outcome === "queued") {
537
+ const id = String(r.proposalId ?? "?");
538
+ return `Distilled ${inputRef} → proposal ${id} (${lessonRef}). Run \`akm proposal show ${id}\` to review.`;
539
+ }
540
+ if (outcome === "validation_failed") {
541
+ const findings = Array.isArray(r.findings) ? r.findings : [];
542
+ const lines = [`Distillation produced an invalid lesson for ${inputRef}; no proposal queued.`];
543
+ for (const f of findings) {
544
+ lines.push(` - ${String(f.message ?? f.kind ?? "validation finding")}`);
545
+ }
546
+ return lines.join("\n");
547
+ }
548
+ // skipped
549
+ const message = typeof r.message === "string" ? r.message : "feature disabled or LLM unavailable";
550
+ return `Distill skipped for ${inputRef}: ${message}`;
551
+ }
552
+ export function formatProposalDiffPlain(r) {
553
+ const header = r.isNew
554
+ ? `# proposal ${String(r.id ?? "?")} (new asset: ${String(r.ref ?? "?")})`
555
+ : `# proposal ${String(r.id ?? "?")} (update: ${String(r.ref ?? "?")})`;
556
+ const unified = typeof r.unified === "string" ? r.unified : "";
557
+ if (!unified)
558
+ return `${header}\n(no changes)`;
559
+ return `${header}\n${unified}`;
560
+ }
561
+ export function formatEventsPlain(r) {
562
+ const events = Array.isArray(r.events) ? r.events : [];
563
+ const headerParts = [];
564
+ if (typeof r.ref === "string" && r.ref)
565
+ headerParts.push(`ref: ${r.ref}`);
566
+ if (typeof r.type === "string" && r.type)
567
+ headerParts.push(`type: ${r.type}`);
568
+ if (typeof r.since === "string" && r.since)
569
+ headerParts.push(`since: ${r.since}`);
570
+ const totalCount = typeof r.totalCount === "number" ? r.totalCount : events.length;
571
+ headerParts.push(`${totalCount} event(s)`);
572
+ const header = headerParts.join(" ");
573
+ if (events.length === 0) {
574
+ return `${header}\nNo events.`;
575
+ }
576
+ const lines = [header, ""];
577
+ for (const event of events) {
578
+ lines.push(formatEventLine(event));
579
+ }
580
+ return lines.join("\n").trimEnd();
581
+ }
582
+ export function formatEventLine(event) {
583
+ const ts = String(event.ts ?? "?");
584
+ const eventType = String(event.eventType ?? "?");
585
+ const ref = event.ref ? String(event.ref) : null;
586
+ const head = ref ? `${ts} [${eventType}] ${ref}` : `${ts} [${eventType}]`;
587
+ if (event.metadata != null && event.metadata !== "") {
588
+ const meta = typeof event.metadata === "string" ? event.metadata : JSON.stringify(event.metadata);
589
+ return `${head}\n metadata: ${meta}`;
590
+ }
591
+ return head;
592
+ }
593
+ export function formatHistoryPlain(r) {
594
+ const entries = Array.isArray(r.entries) ? r.entries : [];
595
+ const headerParts = [];
596
+ if (typeof r.ref === "string" && r.ref)
597
+ headerParts.push(`ref: ${r.ref}`);
598
+ if (typeof r.since === "string" && r.since)
599
+ headerParts.push(`since: ${r.since}`);
600
+ const totalCount = typeof r.totalCount === "number" ? r.totalCount : entries.length;
601
+ headerParts.push(`${totalCount} event(s)`);
602
+ // Show active event sources so operators know which streams were consulted.
603
+ if (Array.isArray(r.sources) && r.sources.length > 0) {
604
+ headerParts.push(`sources: ${r.sources.join(", ")}`);
605
+ }
606
+ const header = headerParts.join(" ");
607
+ if (entries.length === 0) {
608
+ const scope = typeof r.ref === "string" && r.ref ? ` for ${r.ref}` : "";
609
+ return `${header}\nNo history${scope}.`;
610
+ }
611
+ const lines = [header, ""];
612
+ for (const entry of entries) {
613
+ const created = String(entry.createdAt ?? "?");
614
+ const eventType = String(entry.eventType ?? "?");
615
+ const ref = entry.ref ? String(entry.ref) : null;
616
+ const signal = entry.signal ? String(entry.signal) : null;
617
+ const query = entry.query ? String(entry.query) : null;
618
+ const head = ref ? `${created} [${eventType}] ${ref}` : `${created} [${eventType}]`;
619
+ lines.push(head);
620
+ if (signal)
621
+ lines.push(` signal: ${signal}`);
622
+ if (query)
623
+ lines.push(` query: ${query}`);
624
+ if (entry.metadata != null && entry.metadata !== "") {
625
+ const meta = typeof entry.metadata === "string" ? entry.metadata : JSON.stringify(entry.metadata);
626
+ lines.push(` metadata: ${meta}`);
627
+ }
628
+ }
629
+ return lines.join("\n").trimEnd();
630
+ }
631
+ function formatShowPlain(r, detail) {
632
+ const lines = [];
633
+ if (r.type || r.name) {
634
+ lines.push(`# ${String(r.type ?? "asset")}: ${String(r.name ?? "unknown")}`);
635
+ }
636
+ if (r.path && r.editable !== false) {
637
+ lines.push(`file: ${String(r.path)}`);
638
+ }
639
+ if (r.origin !== undefined)
640
+ lines.push(`# origin: ${String(r.origin)}`);
641
+ if (r.action)
642
+ lines.push(`# ${String(r.action)}`);
643
+ if (r.description)
644
+ lines.push(`description: ${String(r.description)}`);
645
+ if (r.workflowTitle)
646
+ lines.push(`workflowTitle: ${String(r.workflowTitle)}`);
647
+ if (r.agent)
648
+ lines.push(`agent: ${String(r.agent)}`);
649
+ if (Array.isArray(r.parameters) && r.parameters.length > 0)
650
+ lines.push(`parameters: ${r.parameters.join(", ")}`);
651
+ if (Array.isArray(r.workflowParameters) && r.workflowParameters.length > 0) {
652
+ lines.push("workflowParameters:");
653
+ for (const parameter of r.workflowParameters) {
654
+ const name = typeof parameter.name === "string" ? parameter.name : "unknown";
655
+ const description = typeof parameter.description === "string" && parameter.description.trim() ? `: ${parameter.description}` : "";
656
+ lines.push(` - ${name}${description}`);
657
+ }
658
+ }
659
+ if (r.modelHint != null)
660
+ lines.push(`modelHint: ${String(r.modelHint)}`);
661
+ if (r.toolPolicy != null)
662
+ lines.push(`toolPolicy: ${JSON.stringify(r.toolPolicy)}`);
663
+ if (r.run)
664
+ lines.push(`run: ${String(r.run)}`);
665
+ if (r.setup)
666
+ lines.push(`setup: ${String(r.setup)}`);
667
+ if (r.cwd)
668
+ lines.push(`cwd: ${String(r.cwd)}`);
669
+ if (detail === "full") {
670
+ if (r.path)
671
+ lines.push(`path: ${String(r.path)}`);
672
+ if (r.editable !== undefined)
673
+ lines.push(`editable: ${String(r.editable)}`);
674
+ if (r.editHint)
675
+ lines.push(`editHint: ${String(r.editHint)}`);
676
+ if (r.schemaVersion !== undefined)
677
+ lines.push(`schemaVersion: ${String(r.schemaVersion)}`);
678
+ }
679
+ const payloads = [r.content, r.template, r.prompt].filter((value) => value != null).map(String);
680
+ if (Array.isArray(r.steps) && r.steps.length > 0) {
681
+ if (lines.length > 0)
682
+ lines.push("");
683
+ lines.push("steps:");
684
+ for (const [index, step] of r.steps.entries()) {
685
+ const title = typeof step.title === "string" ? step.title : "Untitled step";
686
+ const id = typeof step.id === "string" ? step.id : "unknown";
687
+ lines.push(` ${index + 1}. ${title} [${id}]`);
688
+ if (typeof step.instructions === "string" && step.instructions.trim()) {
689
+ const instrLines = step.instructions.trim().split("\n");
690
+ lines.push(` instructions: ${instrLines[0]}`);
691
+ for (const instrLine of instrLines.slice(1))
692
+ lines.push(` ${instrLine}`);
693
+ }
694
+ if (Array.isArray(step.completionCriteria) && step.completionCriteria.length > 0) {
695
+ lines.push(" completion:");
696
+ for (const criterion of step.completionCriteria) {
697
+ lines.push(` - ${String(criterion)}`);
698
+ }
699
+ }
700
+ }
701
+ }
702
+ if (payloads.length > 0) {
703
+ if (lines.length > 0)
704
+ lines.push("");
705
+ lines.push(...payloads);
706
+ }
707
+ // REC-01 / REC-09: Append a type-specific directive so agents apply the
708
+ // content rather than substituting training-data approximations.
709
+ const assetType = typeof r.type === "string" ? r.type : null;
710
+ const assetRef = typeof r.name === "string" && assetType ? `${assetType}:${r.name}` : null;
711
+ // Show-loop detection: if the agent has shown this asset 3+ times without
712
+ // writing anything, surface a warning so it stops cycling and acts.
713
+ const showLoopCount = typeof r.showLoopWarning === "number" ? r.showLoopWarning : 0;
714
+ if (showLoopCount >= 3) {
715
+ lines.push("");
716
+ lines.push(`WARNING: You have shown this asset ${showLoopCount} times without completing the task.`);
717
+ lines.push("Stop re-reading — you have the information you need. Act on it now:");
718
+ lines.push(" - Write your output file using the content above.");
719
+ lines.push(` - If this asset does not contain what you need, run \`akm feedback '${assetRef ?? "<ref>"}' --negative\` and search for a different asset.`);
720
+ }
721
+ if (assetType === "skill" || assetType === "knowledge") {
722
+ const activeRun = r.activeRun;
723
+ if (activeRun) {
724
+ // Active workflow: redirect agent to workflow commands instead of direct apply
725
+ lines.unshift(` akm workflow complete '${activeRun.runId}'${activeRun.stepId ? ` --step '${activeRun.stepId}'` : ""}`);
726
+ lines.unshift("Read this schema, then follow your workflow step's instructions to edit the workspace file. When done, mark the step complete:");
727
+ lines.unshift(`WORKFLOW ACTIVE — schema shown as reference (run: ${activeRun.runId})`);
728
+ lines.unshift("---");
729
+ lines.unshift("");
730
+ // Still show feedback line at the end but skip the APPLY directive
731
+ lines.push("");
732
+ lines.push(`Run \`akm feedback ${assetRef ? `'${assetRef}'` : "<ref>"} --positive\` if the step succeeds, or \`--negative\` if this schema did not help.`);
733
+ }
734
+ else {
735
+ // No active workflow: show the APPLY directive. Branch on whether this
736
+ // skill primarily teaches CLI commands (shell output) vs YAML schema.
737
+ const preApplyLines = [...lines];
738
+ lines.push("");
739
+ lines.push("---");
740
+ if (isCommandOutputSkill(preApplyLines)) {
741
+ lines.push("APPLY (only if no workflow step is required for this task):");
742
+ lines.push(" 1. Identify the output file from README.md (typically commands.txt).");
743
+ lines.push(" 2. Write the exact command syntax from the code blocks above — replace every placeholder (`<name>`, `<value>`) with a real, concrete value from your task context. Do not write placeholder text.");
744
+ lines.push(" 3. Each command should be on a single line (no backslash line continuation unless the verifier expects it).");
745
+ lines.push(`Run \`akm feedback ${assetRef ? `'${assetRef}'` : "<ref>"} --positive\` after the task succeeds, or \`--negative\` if this reference did not contain the needed command syntax.`);
746
+ }
747
+ else {
748
+ lines.push("APPLY (only if no workflow step is required for this task):");
749
+ lines.push(" 1. Identify the target file from README.md — write or edit it. If the file does not yet exist, CREATE it with the full structure from this schema.");
750
+ lines.push(" 2. Add/edit the fields shown above using the exact field names from this schema.");
751
+ lines.push(" 3. COPY the exact YAML structure and field names from the code blocks above — do not substitute synonyms or invent nesting. Replace every placeholder value with a real, concrete value from your task context. Do not leave any field as null, empty, or a placeholder.");
752
+ lines.push(`Run \`akm feedback ${assetRef ? `'${assetRef}'` : "<ref>"} --positive\` after the task succeeds, or \`--negative\` if the task fails after following this guidance.`);
753
+ }
754
+ }
755
+ }
756
+ else if (assetType === "workflow") {
757
+ const workflowName = typeof r.name === "string" ? r.name : null;
758
+ const workflowRef = workflowName ? `workflow:${workflowName}` : "<ref>";
759
+ // Insert action directive BEFORE the workflow content by prepending to lines at the
760
+ // separator position. We find where the header ends and insert after the first `---`.
761
+ // Since lines already contain the full content at this point, we locate the insertion
762
+ // index: right after the first `---` separator if present, otherwise after the header.
763
+ const separatorIdx = lines.indexOf("---");
764
+ const insertIdx = separatorIdx >= 0 ? separatorIdx + 1 : r.type || r.name ? 1 : 0;
765
+ const actionDirective = [
766
+ `ACTION REQUIRED: Do not execute steps manually from this output.`,
767
+ `Run \`akm workflow next '${workflowRef}'\` to get your current step with exact instructions.`,
768
+ "---",
769
+ ];
770
+ lines.splice(insertIdx, 0, "", ...actionDirective);
771
+ lines.push("");
772
+ lines.push("---");
773
+ lines.push(`NEXT STEP: Run \`akm workflow next '${workflowRef}'\` to see the current workflow step.`);
774
+ lines.push("Do not edit workspace files before completing each step with `akm workflow complete`.");
775
+ }
776
+ return lines.length > 0 ? lines.join("\n") : null;
777
+ }
778
+ /**
779
+ * Detect whether a skill's rendered content primarily teaches CLI commands
780
+ * rather than YAML schema. Used to select the right APPLY directive variant.
781
+ *
782
+ * Heuristic: count code-block lines that start with known shell command
783
+ * prefixes vs lines that look like YAML key-value pairs. If CLI lines
784
+ * outnumber YAML lines (and there is at least one CLI line), treat the
785
+ * skill as command-output.
786
+ */
787
+ function isCommandOutputSkill(lines) {
788
+ const codeLines = lines.filter((l) => l.startsWith(" ") || l.startsWith("\t") || /^`/.test(l));
789
+ const cliPattern = /^(az |kubectl |docker |git |helm |terraform |aws |gcloud )/;
790
+ const yamlPattern = /^\s+\w+:/;
791
+ const cliCount = codeLines.filter((l) => cliPattern.test(l.trim())).length;
792
+ const yamlCount = codeLines.filter((l) => yamlPattern.test(l)).length;
793
+ return cliCount > yamlCount && cliCount > 0;
794
+ }
795
+ export function formatWorkflowListPlain(result) {
796
+ const runs = Array.isArray(result.runs) ? result.runs : [];
797
+ if (runs.length === 0) {
798
+ return "No workflow runs. Start one with `akm workflow next workflow:<name>` or author one with `akm workflow create <name>`.";
799
+ }
800
+ return runs
801
+ .map((run) => {
802
+ const id = typeof run.id === "string" ? run.id : "unknown";
803
+ const ref = typeof run.workflowRef === "string" ? run.workflowRef : "workflow:unknown";
804
+ const status = typeof run.status === "string" ? run.status : "unknown";
805
+ const currentStep = typeof run.currentStepId === "string" ? ` (current: ${run.currentStepId})` : "";
806
+ return `${id} ${ref} [${status}]${currentStep}`;
807
+ })
808
+ .join("\n");
809
+ }
810
+ export function formatWorkflowStatusPlain(result) {
811
+ const run = typeof result.run === "object" && result.run !== null ? result.run : undefined;
812
+ const workflow = typeof result.workflow === "object" && result.workflow !== null
813
+ ? result.workflow
814
+ : undefined;
815
+ if (!run || !workflow)
816
+ return null;
817
+ const lines = [
818
+ `workflow: ${String(workflow.ref ?? "workflow:unknown")}`,
819
+ `run: ${String(run.id ?? "unknown")}`,
820
+ `title: ${String(run.workflowTitle ?? workflow.title ?? "Workflow")}`,
821
+ `status: ${String(run.status ?? "unknown")}`,
822
+ ];
823
+ if (run.currentStepId)
824
+ lines.push(`currentStep: ${String(run.currentStepId)}`);
825
+ const steps = Array.isArray(workflow.steps) ? workflow.steps : [];
826
+ if (steps.length > 0) {
827
+ lines.push("steps:");
828
+ for (const step of steps) {
829
+ const title = typeof step.title === "string" ? step.title : "Untitled step";
830
+ const id = typeof step.id === "string" ? step.id : "unknown";
831
+ const status = typeof step.status === "string" ? step.status : "unknown";
832
+ lines.push(` - ${title} [${id}] (${status})`);
833
+ if (typeof step.notes === "string" && step.notes.trim()) {
834
+ lines.push(` notes: ${step.notes}`);
835
+ }
836
+ }
837
+ }
838
+ return lines.join("\n");
839
+ }
840
+ export function formatWorkflowNextPlain(result) {
841
+ const base = formatWorkflowStatusPlain(result);
842
+ const step = typeof result.step === "object" && result.step !== null ? result.step : undefined;
843
+ if (!step)
844
+ return base;
845
+ const lines = base ? [base, "", "next:"] : ["next:"];
846
+ lines.push(` ${String(step.title ?? "Untitled step")} [${String(step.id ?? "unknown")}]`);
847
+ if (typeof step.instructions === "string" && step.instructions.trim()) {
848
+ const instrLines = step.instructions.trim().split("\n");
849
+ lines.push(` instructions: ${instrLines[0]}`);
850
+ for (const instrLine of instrLines.slice(1))
851
+ lines.push(` ${instrLine}`);
852
+ }
853
+ const completion = Array.isArray(step.completionCriteria) ? step.completionCriteria : [];
854
+ if (completion.length > 0) {
855
+ lines.push(" completion:");
856
+ for (const criterion of completion) {
857
+ lines.push(` - ${String(criterion)}`);
858
+ }
859
+ }
860
+ // T2-3: surface run-id as labeled field
861
+ const run = typeof result.run === "object" && result.run !== null ? result.run : undefined;
862
+ const runId = typeof run?.id === "string" ? run.id : null;
863
+ const stepId = typeof step?.id === "string" ? step.id : null;
864
+ if (runId) {
865
+ lines.push("");
866
+ lines.push(`runId: ${runId}`);
867
+ }
868
+ // T1-6: complete command
869
+ if (runId && stepId) {
870
+ lines.push("");
871
+ lines.push("COMPLETE THIS STEP:");
872
+ lines.push(` akm workflow complete '${runId}' --step '${stepId}'`);
873
+ }
874
+ else if (runId) {
875
+ lines.push("");
876
+ lines.push("COMPLETE THIS STEP:");
877
+ lines.push(` akm workflow complete '${runId}' --step '<step-id>'`);
878
+ }
879
+ return lines.join("\n");
880
+ }
881
+ export function formatSearchPlain(r, detail) {
882
+ const hits = r.hits ?? [];
883
+ const registryHits = r.registryHits ?? [];
884
+ const allHits = [...hits, ...registryHits];
885
+ if (allHits.length === 0) {
886
+ const warnings = Array.isArray(r.warnings) ? r.warnings : [];
887
+ const hasSetupWarning = warnings.some((w) => String(w).toLowerCase().includes("no stash") || String(w).toLowerCase().includes("not configured"));
888
+ if (hasSetupWarning) {
889
+ return "No stash configured. Run `akm init` to create your working stash, then `akm index` to build the search index.";
890
+ }
891
+ const base = r.tip ? String(r.tip) : "No matches found.";
892
+ return `${base}\nTry:\n akm search '<broader-term>' # fewer keywords\n akm list # see all configured sources\n akm curate '<query>' # let akm select the best match`;
893
+ }
894
+ const lines = [];
895
+ for (const hit of allHits) {
896
+ const type = hit.type ?? "unknown";
897
+ const name = hit.name ?? "unnamed";
898
+ const score = hit.score != null ? ` (score: ${hit.score})` : "";
899
+ const desc = hit.description ? ` ${hit.description}` : "";
900
+ lines.push(`${type}: ${name}${score}`);
901
+ if (desc)
902
+ lines.push(desc);
903
+ if (hit.id)
904
+ lines.push(` id: ${String(hit.id)}`);
905
+ if (hit.ref)
906
+ lines.push(` ref: ${String(hit.ref)}`);
907
+ if (hit.origin !== undefined)
908
+ lines.push(` origin: ${String(hit.origin)}`);
909
+ if (hit.size)
910
+ lines.push(` size: ${String(hit.size)}`);
911
+ if (hit.action)
912
+ lines.push(` action: ${String(hit.action)}`);
913
+ if (hit.run)
914
+ lines.push(` run: ${String(hit.run)}`);
915
+ if (Array.isArray(hit.tags) && hit.tags.length > 0)
916
+ lines.push(` tags: ${hit.tags.join(", ")}`);
917
+ // Optional v1 spec §4.2 quality marker (e.g. "curated" / "proposed").
918
+ if (typeof hit.quality === "string" && hit.quality)
919
+ lines.push(` quality: ${hit.quality}`);
920
+ // Surface optional hit-level warnings (v1 spec §4.2). The legacy
921
+ // `curated` boolean was removed in v1.
922
+ if (Array.isArray(hit.warnings) && hit.warnings.length > 0) {
923
+ lines.push(` warnings: ${hit.warnings.join("; ")}`);
924
+ }
925
+ if (detail === "full") {
926
+ if (hit.path)
927
+ lines.push(` path: ${String(hit.path)}`);
928
+ if (hit.editable != null)
929
+ lines.push(` editable: ${String(hit.editable)}`);
930
+ if (hit.editHint)
931
+ lines.push(` editHint: ${String(hit.editHint)}`);
932
+ const whyMatched = hit.whyMatched;
933
+ if (whyMatched && whyMatched.length > 0) {
934
+ lines.push(` whyMatched: ${whyMatched.join(", ")}`);
935
+ }
936
+ }
937
+ lines.push(""); // blank line between hits
938
+ }
939
+ if (detail === "full" && r.timing) {
940
+ const timing = r.timing;
941
+ const parts = [];
942
+ if (timing.totalMs != null)
943
+ parts.push(`total: ${timing.totalMs}ms`);
944
+ if (timing.rankMs != null)
945
+ parts.push(`rank: ${timing.rankMs}ms`);
946
+ if (timing.embedMs != null)
947
+ parts.push(`embed: ${timing.embedMs}ms`);
948
+ if (parts.length > 0)
949
+ lines.push(`timing: ${parts.join(", ")}`);
950
+ }
951
+ // REC-02: When stash hits exist, tell the agent the next required step so it
952
+ // doesn't skip `akm show` and write from training memory instead.
953
+ if (hits.length >= 1) {
954
+ // Prefer skill/command/agent type hits for the "Next:" ref — knowledge docs are
955
+ // supplementary context, not the authoritative schema agents should load first.
956
+ const preferredHit = hits.find((h) => h.type === "skill" || h.type === "command" || h.type === "agent") ?? hits[0];
957
+ const topRef = typeof preferredHit.ref === "string" ? preferredHit.ref : null;
958
+ const hasWorkflowHit = hits.some((h) => h.type === "workflow");
959
+ if (topRef) {
960
+ if (hasWorkflowHit) {
961
+ const workflowRef = hits.find((h) => h.type === "workflow");
962
+ const wfRef = workflowRef && typeof workflowRef.ref === "string" ? workflowRef.ref : topRef;
963
+ lines.push(`Next: akm show '${topRef}' | To start a workflow: akm workflow next '${wfRef}'`);
964
+ lines.push("After running workflow next: follow each step and run `akm workflow complete <run-id> --step <step-id>` when done.");
965
+ }
966
+ else {
967
+ lines.push(`Next: akm show '${topRef}'`);
968
+ lines.push("After reading the asset: check whether a workflow applies before editing — if so, use `akm workflow next` instead.");
969
+ }
970
+ }
971
+ }
972
+ return lines.join("\n").trimEnd();
973
+ }
974
+ export function formatWikiListPlain(r) {
975
+ const wikis = Array.isArray(r.wikis) ? r.wikis : [];
976
+ if (wikis.length === 0)
977
+ return "No wikis. Create one with `akm wiki create <name>` or register one with `akm wiki register <name> <path-or-repo>`.";
978
+ const lines = ["NAME\tPAGES\tRAWS\tLAST-MODIFIED"];
979
+ for (const w of wikis) {
980
+ const name = typeof w.name === "string" ? w.name : "?";
981
+ const pages = typeof w.pages === "number" ? w.pages : 0;
982
+ const raws = typeof w.raws === "number" ? w.raws : 0;
983
+ const modified = typeof w.lastModified === "string" ? w.lastModified : "-";
984
+ lines.push(`${name}\t${pages}\t${raws}\t${modified}`);
985
+ }
986
+ return lines.join("\n");
987
+ }
988
+ export function formatWikiShowPlain(r) {
989
+ const lines = [];
990
+ if (r.name)
991
+ lines.push(`# wiki: ${String(r.name)}`);
992
+ if (r.path)
993
+ lines.push(`path: ${String(r.path)}`);
994
+ if (r.description)
995
+ lines.push(`description: ${String(r.description)}`);
996
+ if (typeof r.pages === "number")
997
+ lines.push(`pages: ${r.pages}`);
998
+ if (typeof r.raws === "number")
999
+ lines.push(`raws: ${r.raws}`);
1000
+ if (r.lastModified)
1001
+ lines.push(`lastModified: ${String(r.lastModified)}`);
1002
+ const recentLog = Array.isArray(r.recentLog) ? r.recentLog : [];
1003
+ if (recentLog.length > 0) {
1004
+ lines.push("", "recent log:");
1005
+ for (const entry of recentLog) {
1006
+ lines.push(entry);
1007
+ lines.push("");
1008
+ }
1009
+ }
1010
+ return lines.join("\n").trimEnd();
1011
+ }
1012
+ export function formatWikiCreatePlain(r) {
1013
+ const created = Array.isArray(r.created) ? r.created : [];
1014
+ const skipped = Array.isArray(r.skipped) ? r.skipped : [];
1015
+ const lines = [`Created wiki ${String(r.ref ?? r.name)} at ${String(r.path ?? "?")}`];
1016
+ if (created.length > 0)
1017
+ lines.push(` created: ${created.length} file(s)`);
1018
+ if (skipped.length > 0)
1019
+ lines.push(` skipped: ${skipped.length} existing file(s)`);
1020
+ return lines.join("\n");
1021
+ }
1022
+ export function formatWikiRemovePlain(r) {
1023
+ const preserved = r.preservedRaw === true;
1024
+ const removed = Array.isArray(r.removed) ? r.removed.length : 0;
1025
+ const base = `Removed wiki ${String(r.name ?? "?")} (${removed} path(s))`;
1026
+ return preserved ? `${base}; raw/ preserved at ${String(r.rawPath ?? "raw/")}` : base;
1027
+ }
1028
+ export function formatWikiPagesPlain(r) {
1029
+ const pages = Array.isArray(r.pages) ? r.pages : [];
1030
+ if (pages.length === 0)
1031
+ return `No pages in wiki:${String(r.wiki ?? "?")}.`;
1032
+ const lines = [];
1033
+ for (const p of pages) {
1034
+ const ref = String(p.ref ?? "?");
1035
+ const kind = typeof p.pageKind === "string" ? ` [${p.pageKind}]` : "";
1036
+ const desc = typeof p.description === "string" && p.description ? ` — ${p.description}` : "";
1037
+ lines.push(`${ref}${kind}${desc}`);
1038
+ }
1039
+ return lines.join("\n");
1040
+ }
1041
+ export function formatWikiStashPlain(r) {
1042
+ const slug = String(r.slug ?? "?");
1043
+ const pathValue = String(r.path ?? "?");
1044
+ return `Stashed ${slug} → ${pathValue}`;
1045
+ }
1046
+ export function formatWikiLintPlain(r) {
1047
+ const findings = Array.isArray(r.findings) ? r.findings : [];
1048
+ const pagesScanned = typeof r.pagesScanned === "number" ? r.pagesScanned : 0;
1049
+ const rawsScanned = typeof r.rawsScanned === "number" ? r.rawsScanned : 0;
1050
+ const header = `${findings.length} finding(s) in wiki:${String(r.wiki ?? "?")} (${pagesScanned} page(s), ${rawsScanned} raw(s))`;
1051
+ if (findings.length === 0)
1052
+ return `${header} — clean.`;
1053
+ const lines = [header];
1054
+ for (const f of findings) {
1055
+ const kind = String(f.kind ?? "?");
1056
+ const message = String(f.message ?? "");
1057
+ lines.push(`- [${kind}] ${message}`);
1058
+ }
1059
+ return lines.join("\n");
1060
+ }
1061
+ export function formatWikiIngestPlain(r) {
1062
+ if (typeof r.workflow === "string")
1063
+ return r.workflow;
1064
+ return JSON.stringify(r, null, 2);
1065
+ }
1066
+ export function formatCuratePlain(r, detail) {
1067
+ const query = typeof r.query === "string" ? r.query : "";
1068
+ const summary = typeof r.summary === "string" ? r.summary : "";
1069
+ const items = Array.isArray(r.items) ? r.items : [];
1070
+ const lines = [`Curated results for "${query}"`];
1071
+ if (summary)
1072
+ lines.push(summary);
1073
+ if (items.length === 0) {
1074
+ if (r.tip)
1075
+ lines.push(String(r.tip));
1076
+ return lines.join("\n");
1077
+ }
1078
+ for (const item of items) {
1079
+ const type = typeof item.type === "string" ? item.type : "unknown";
1080
+ const name = typeof item.name === "string" ? item.name : "unnamed";
1081
+ lines.push("");
1082
+ lines.push(`[${type}] ${name}`);
1083
+ if (item.description)
1084
+ lines.push(` ${String(item.description)}`);
1085
+ if (item.preview)
1086
+ lines.push(` preview: ${String(item.preview)}`);
1087
+ if (item.ref)
1088
+ lines.push(` ref: ${String(item.ref)}`);
1089
+ if (item.id)
1090
+ lines.push(` id: ${String(item.id)}`);
1091
+ if (Array.isArray(item.parameters) && item.parameters.length > 0) {
1092
+ lines.push(` parameters: ${item.parameters.join(", ")}`);
1093
+ }
1094
+ if (item.run)
1095
+ lines.push(` run: ${String(item.run)}`);
1096
+ if (item.followUp)
1097
+ lines.push(` show: ${String(item.followUp)}`);
1098
+ if (detail !== "brief" && item.reason)
1099
+ lines.push(` why: ${String(item.reason)}`);
1100
+ }
1101
+ const warnings = Array.isArray(r.warnings) ? r.warnings : [];
1102
+ if (warnings.length > 0) {
1103
+ lines.push("");
1104
+ lines.push("Warnings:");
1105
+ for (const warning of warnings) {
1106
+ lines.push(`- ${String(warning)}`);
1107
+ }
1108
+ }
1109
+ lines.push("");
1110
+ lines.push("Next steps:");
1111
+ lines.push(" 1. Run `akm show <ref>` for the best result above to read the full schema.");
1112
+ lines.push(" 2. Edit the workspace file using the schema field names and your task-specific values.");
1113
+ lines.push(" 3. Run `akm feedback <ref> --positive` when the task succeeds.");
1114
+ lines.push("To search further: akm search '<query>'");
1115
+ return lines.join("\n");
1116
+ }