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
@@ -3,10 +3,49 @@ import path from "node:path";
3
3
  import { deriveCanonicalAssetName, deriveCanonicalAssetNameFromStashRoot, isRelevantAssetFile, } from "../core/asset-spec";
4
4
  import { isAssetType } from "../core/common";
5
5
  import { parseFrontmatter, toStringOrUndefined } from "../core/frontmatter";
6
- import { warn } from "../core/warn";
6
+ import { isVerbose, warn } from "../core/warn";
7
7
  import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "./file-context";
8
+ export const SCOPE_KEYS = ["user", "agent", "run", "channel"];
8
9
  // ── Load / Write ────────────────────────────────────────────────────────────
9
10
  const STASH_FILENAME = ".stash.json";
11
+ // ── Quality semantics (v1 spec §4.2) ────────────────────────────────────────
12
+ /**
13
+ * Well-known quality values. `generated` and `curated` are included in
14
+ * default search; `proposed` is excluded by default and opt-in via
15
+ * `--include-proposed`. Unknown values warn once and remain searchable.
16
+ */
17
+ export const KNOWN_QUALITY_VALUES = new Set(["generated", "curated", "proposed"]);
18
+ /** Tracks unknown quality values we've already warned about (one warn per value per process). */
19
+ const warnedUnknownQualityValues = new Set();
20
+ /**
21
+ * Normalize a `quality` string off a stash entry. Known values pass through
22
+ * untouched. Unknown values are accepted as-is (preserved verbatim on the
23
+ * entry) but trigger a one-time warning per unique value via the shared
24
+ * `warn()` helper (honours --quiet / `setQuiet()`).
25
+ */
26
+ export function normalizeQuality(raw) {
27
+ if (KNOWN_QUALITY_VALUES.has(raw))
28
+ return raw;
29
+ if (!warnedUnknownQualityValues.has(raw)) {
30
+ warnedUnknownQualityValues.add(raw);
31
+ warn(`Warning: unknown quality value "${raw}" — entry remains searchable, but consider using "generated", "curated", or "proposed" (v1 spec §4.2).`);
32
+ }
33
+ return raw;
34
+ }
35
+ /**
36
+ * Test-only: clear the per-process unknown-quality warning memo so a test
37
+ * can re-trigger the warning. Not part of the public API.
38
+ */
39
+ export function _resetUnknownQualityWarnings() {
40
+ warnedUnknownQualityValues.clear();
41
+ }
42
+ /**
43
+ * Returns true if an entry's quality marks it as "proposed". Proposed
44
+ * entries are excluded from default search per v1 spec §4.2.
45
+ */
46
+ export function isProposedQuality(quality) {
47
+ return quality === "proposed";
48
+ }
10
49
  export function stashFilePath(dirPath) {
11
50
  return path.join(dirPath, STASH_FILENAME);
12
51
  }
@@ -97,8 +136,9 @@ export function validateStashEntry(entry) {
97
136
  }
98
137
  if (typeof e.filename === "string" && e.filename)
99
138
  result.filename = e.filename;
100
- if (e.quality === "generated" || e.quality === "curated")
101
- result.quality = e.quality;
139
+ if (typeof e.quality === "string" && e.quality.length > 0) {
140
+ result.quality = normalizeQuality(e.quality);
141
+ }
102
142
  if (typeof e.confidence === "number" && Number.isFinite(e.confidence))
103
143
  result.confidence = Math.max(0, Math.min(1, e.confidence));
104
144
  if (typeof e.source === "string" &&
@@ -157,6 +197,11 @@ export function validateStashEntry(entry) {
157
197
  if (filtered.length > 0)
158
198
  result.sources = filtered;
159
199
  }
200
+ if (typeof e.scope === "object" && e.scope !== null && !Array.isArray(e.scope)) {
201
+ const scope = normalizeScopeObject(e.scope);
202
+ if (scope)
203
+ result.scope = scope;
204
+ }
160
205
  if (Array.isArray(e.parameters)) {
161
206
  const validated = e.parameters
162
207
  .filter((p) => {
@@ -183,6 +228,46 @@ export function validateStashEntry(entry) {
183
228
  }
184
229
  return result;
185
230
  }
231
+ /**
232
+ * Coerce a raw `{ user, agent, run, channel }` object into a clean
233
+ * `StashEntryScope`, dropping non-string and empty values. Returns
234
+ * `undefined` when no recognized keys carry a value.
235
+ */
236
+ function normalizeScopeObject(raw) {
237
+ const out = {};
238
+ for (const key of SCOPE_KEYS) {
239
+ const value = raw[key];
240
+ if (typeof value === "string") {
241
+ const trimmed = value.trim();
242
+ if (trimmed)
243
+ out[key] = trimmed;
244
+ }
245
+ else if (typeof value === "number" && Number.isFinite(value)) {
246
+ out[key] = String(value);
247
+ }
248
+ }
249
+ return Object.keys(out).length > 0 ? out : undefined;
250
+ }
251
+ /**
252
+ * Pull `scope_user` / `scope_agent` / `scope_run` / `scope_channel` out of
253
+ * a parsed frontmatter block and attach them as `entry.scope`. Tolerates
254
+ * missing or malformed values; legacy memories without these keys are left
255
+ * untouched (no `scope` field added).
256
+ */
257
+ export function applyScopeFrontmatter(entry, fmData) {
258
+ const collected = {};
259
+ for (const key of SCOPE_KEYS) {
260
+ const fmKey = `scope_${key}`;
261
+ if (Object.hasOwn(fmData, fmKey)) {
262
+ collected[key] = fmData[fmKey];
263
+ }
264
+ }
265
+ if (Object.keys(collected).length === 0)
266
+ return;
267
+ const scope = normalizeScopeObject(collected);
268
+ if (scope)
269
+ entry.scope = scope;
270
+ }
186
271
  function normalizeNonEmptyStringList(value) {
187
272
  if (typeof value === "string") {
188
273
  const trimmed = value.trim();
@@ -400,6 +485,8 @@ export async function generateMetadata(dirPath, assetType, files, typeRoot = dir
400
485
  entry.parameters = fmParams;
401
486
  // Pass wiki-pattern frontmatter through onto the entry
402
487
  applyWikiFrontmatter(entry, parsed.data);
488
+ // Pass canonical scope_* frontmatter through onto the entry
489
+ applyScopeFrontmatter(entry, parsed.data);
403
490
  // Extract parameters from template placeholders ($1, $ARGUMENTS, {{named}})
404
491
  if (entry.type === "command") {
405
492
  const cmdParams = extractCommandParameters(parsed.content);
@@ -516,6 +603,8 @@ export async function generateMetadataFlat(stashRoot, files) {
516
603
  entry.parameters = fmParams;
517
604
  // Pass wiki-pattern frontmatter through onto the entry
518
605
  applyWikiFrontmatter(entry, parsed.data);
606
+ // Pass canonical scope_* frontmatter through onto the entry
607
+ applyScopeFrontmatter(entry, parsed.data);
519
608
  // Extract parameters from template placeholders ($1, $ARGUMENTS, {{named}})
520
609
  if (entry.type === "command") {
521
610
  const cmdParams = extractCommandParameters(parsed.content);
@@ -568,9 +657,28 @@ function buildMetadataSkipWarning(filePath, assetType, error) {
568
657
  const warning = assetType === "workflow"
569
658
  ? `Skipped workflow ${filePath}:\n${detail}`
570
659
  : `Skipped malformed ${assetType} asset at ${filePath}: ${detail}`;
660
+ // Workflow validation warnings are noisy on cold-start search against fresh
661
+ // registry-cloned content (see issue #273). At default verbosity we suppress
662
+ // the per-spec stderr line and rely on a one-line summary emitted by the
663
+ // indexer driver after the run completes. The full per-file detail is still
664
+ // returned in the warnings[] array (and IndexResponse.warnings) for
665
+ // programmatic consumers, and verbose mode restores the immediate stderr
666
+ // print so workflow authors keep the rich feedback they expect.
667
+ if (assetType === "workflow" && !isVerbose()) {
668
+ return warning;
669
+ }
571
670
  warn(warning);
572
671
  return warning;
573
672
  }
673
+ /**
674
+ * Returns true when a metadata-skip warning was produced by the workflow
675
+ * validator. Used by the indexer driver to count workflow skips for the
676
+ * default-verbosity summary line. Matches the prefix produced by
677
+ * `buildMetadataSkipWarning` for `assetType === "workflow"`.
678
+ */
679
+ export function isWorkflowSkipWarning(warning) {
680
+ return warning.startsWith("Skipped workflow ");
681
+ }
574
682
  function normalizeTerms(values) {
575
683
  const normalized = new Set();
576
684
  for (const value of values) {
@@ -224,7 +224,9 @@ function isValidDirectory(dir) {
224
224
  */
225
225
  export async function ensureSourceCaches(config) {
226
226
  const cfg = config ?? loadConfig();
227
- for (const entry of cfg.stashes ?? []) {
227
+ // Use sources[] (current key) with fallback to stashes[] (deprecated, one-release compat).
228
+ const entries = cfg.sources ?? cfg.stashes ?? [];
229
+ for (const entry of entries) {
228
230
  if (!GIT_STASH_TYPES.has(entry.type) || !entry.url || entry.enabled === false)
229
231
  continue;
230
232
  try {
@@ -236,7 +238,7 @@ export async function ensureSourceCaches(config) {
236
238
  warn(`Warning: failed to refresh git mirror for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
237
239
  }
238
240
  }
239
- for (const entry of cfg.stashes ?? []) {
241
+ for (const entry of entries) {
240
242
  if (entry.type !== "website" || !entry.url || entry.enabled === false)
241
243
  continue;
242
244
  try {
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Parser + resolver for the optional `agent` config block (v1 spec §12).
3
+ *
4
+ * The on-disk shape is:
5
+ *
6
+ * ```jsonc
7
+ * {
8
+ * "agent": {
9
+ * "default": "opencode",
10
+ * "timeoutMs": 60000,
11
+ * "profiles": {
12
+ * "opencode": { "bin": "opencode", "args": ["--non-interactive"], ... }
13
+ * }
14
+ * }
15
+ * }
16
+ * ```
17
+ *
18
+ * Unknown keys at any level under `agent` are warn-and-ignored — this is the
19
+ * v1 §9.2 contract. Missing `agent` block disables agent commands; callers
20
+ * should reach for {@link requireAgentConfig} to surface a stable
21
+ * `ConfigError` with a hint pointing at setup.
22
+ *
23
+ * No LLM SDK is imported here. The runtime path is shell-out only (see
24
+ * `./spawn.ts`).
25
+ */
26
+ import { ConfigError } from "../../core/errors";
27
+ import { warn } from "../../core/warn";
28
+ import { BUILTIN_AGENT_PROFILE_NAMES, getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles";
29
+ /** Keys recognised at the top level of an `agent` config block. */
30
+ const KNOWN_AGENT_KEYS = new Set(["default", "timeoutMs", "profiles"]);
31
+ /** Keys recognised on a profile entry. */
32
+ const KNOWN_PROFILE_KEYS = new Set(["bin", "args", "stdio", "env", "envPassthrough", "timeoutMs", "parseOutput"]);
33
+ /**
34
+ * Default hard timeout for an agent CLI. Spec §12.2 calls for a hard
35
+ * timeout; 60s matches the example value in `docs/configuration.md`.
36
+ */
37
+ export const DEFAULT_AGENT_TIMEOUT_MS = 60_000;
38
+ /**
39
+ * Parse a raw value (typically `rawConfig.agent` from `JSON.parse`) into a
40
+ * normalised {@link AgentConfig}. Returns `undefined` when the value is not
41
+ * an object (i.e. the block is absent or malformed at the root level — for
42
+ * malformed roots we emit a warning).
43
+ *
44
+ * Unknown keys (top-level and per-profile) are warn-and-ignore. Type errors
45
+ * on individual fields are warn-and-ignore so a bad `timeoutMs` does not
46
+ * break the rest of the block.
47
+ */
48
+ export function parseAgentConfig(value) {
49
+ if (value === undefined)
50
+ return undefined;
51
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
52
+ warn('[akm] Ignoring "agent" config: expected an object.');
53
+ return undefined;
54
+ }
55
+ const raw = value;
56
+ const out = {};
57
+ for (const key of Object.keys(raw)) {
58
+ if (!KNOWN_AGENT_KEYS.has(key)) {
59
+ warn(`[akm] Ignoring unknown agent config key: "${key}"`);
60
+ }
61
+ }
62
+ if ("default" in raw) {
63
+ if (typeof raw.default === "string" && raw.default.trim()) {
64
+ out.default = raw.default.trim();
65
+ }
66
+ else if (raw.default !== undefined) {
67
+ warn("[akm] Ignoring agent.default: expected a non-empty string.");
68
+ }
69
+ }
70
+ if ("timeoutMs" in raw) {
71
+ if (typeof raw.timeoutMs === "number" &&
72
+ Number.isFinite(raw.timeoutMs) &&
73
+ Number.isInteger(raw.timeoutMs) &&
74
+ raw.timeoutMs > 0) {
75
+ out.timeoutMs = raw.timeoutMs;
76
+ }
77
+ else {
78
+ warn("[akm] Ignoring agent.timeoutMs: expected a positive integer (milliseconds).");
79
+ }
80
+ }
81
+ if ("profiles" in raw) {
82
+ const profiles = parseAgentProfilesMap(raw.profiles);
83
+ if (profiles)
84
+ out.profiles = profiles;
85
+ }
86
+ return out;
87
+ }
88
+ function parseAgentProfilesMap(value) {
89
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
90
+ warn("[akm] Ignoring agent.profiles: expected an object.");
91
+ return undefined;
92
+ }
93
+ const out = {};
94
+ for (const [name, raw] of Object.entries(value)) {
95
+ const parsed = parseAgentProfileConfig(name, raw);
96
+ if (parsed)
97
+ out[name] = parsed;
98
+ }
99
+ return Object.keys(out).length > 0 ? out : undefined;
100
+ }
101
+ function parseAgentProfileConfig(name, value) {
102
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
103
+ warn(`[akm] Ignoring agent.profiles."${name}": expected an object.`);
104
+ return undefined;
105
+ }
106
+ const raw = value;
107
+ const out = {};
108
+ for (const key of Object.keys(raw)) {
109
+ if (!KNOWN_PROFILE_KEYS.has(key)) {
110
+ warn(`[akm] Ignoring unknown agent.profiles."${name}" key: "${key}"`);
111
+ }
112
+ }
113
+ if (typeof raw.bin === "string" && raw.bin.trim()) {
114
+ out.bin = raw.bin.trim();
115
+ }
116
+ else if (raw.bin !== undefined) {
117
+ warn(`[akm] Ignoring agent.profiles."${name}".bin: expected a non-empty string.`);
118
+ }
119
+ if (Array.isArray(raw.args)) {
120
+ const args = raw.args.filter((a) => typeof a === "string");
121
+ if (args.length === raw.args.length) {
122
+ out.args = args;
123
+ }
124
+ else {
125
+ warn(`[akm] Ignoring non-string entries in agent.profiles."${name}".args.`);
126
+ if (args.length > 0)
127
+ out.args = args;
128
+ }
129
+ }
130
+ else if (raw.args !== undefined) {
131
+ warn(`[akm] Ignoring agent.profiles."${name}".args: expected an array of strings.`);
132
+ }
133
+ if (raw.stdio === "captured" || raw.stdio === "interactive") {
134
+ out.stdio = raw.stdio;
135
+ }
136
+ else if (raw.stdio !== undefined) {
137
+ warn(`[akm] Ignoring agent.profiles."${name}".stdio: expected "captured" or "interactive".`);
138
+ }
139
+ if (typeof raw.env === "object" && raw.env !== null && !Array.isArray(raw.env)) {
140
+ const env = {};
141
+ for (const [k, v] of Object.entries(raw.env)) {
142
+ if (typeof v === "string")
143
+ env[k] = v;
144
+ }
145
+ if (Object.keys(env).length > 0)
146
+ out.env = env;
147
+ }
148
+ else if (raw.env !== undefined) {
149
+ warn(`[akm] Ignoring agent.profiles."${name}".env: expected a string-valued object.`);
150
+ }
151
+ if (Array.isArray(raw.envPassthrough)) {
152
+ const list = raw.envPassthrough.filter((s) => typeof s === "string" && s.length > 0);
153
+ if (list.length > 0)
154
+ out.envPassthrough = list;
155
+ }
156
+ else if (raw.envPassthrough !== undefined) {
157
+ warn(`[akm] Ignoring agent.profiles."${name}".envPassthrough: expected an array of strings.`);
158
+ }
159
+ if (typeof raw.timeoutMs === "number" &&
160
+ Number.isFinite(raw.timeoutMs) &&
161
+ Number.isInteger(raw.timeoutMs) &&
162
+ raw.timeoutMs > 0) {
163
+ out.timeoutMs = raw.timeoutMs;
164
+ }
165
+ else if (raw.timeoutMs !== undefined) {
166
+ warn(`[akm] Ignoring agent.profiles."${name}".timeoutMs: expected a positive integer.`);
167
+ }
168
+ if (raw.parseOutput === "text" || raw.parseOutput === "json") {
169
+ out.parseOutput = raw.parseOutput;
170
+ }
171
+ else if (raw.parseOutput !== undefined) {
172
+ warn(`[akm] Ignoring agent.profiles."${name}".parseOutput: expected "text" or "json".`);
173
+ }
174
+ return out;
175
+ }
176
+ /**
177
+ * Merge a user override (from `agent.profiles[<name>]`) on top of the
178
+ * built-in profile (if any) and return the resolved profile. If `name`
179
+ * matches no built-in and the user override has no `bin`, returns
180
+ * `undefined` — the profile is unusable.
181
+ *
182
+ * Used at the spawn site, never at config-load time. Keeping merge logic
183
+ * here means the parser stays a pure shape-checker.
184
+ */
185
+ export function resolveAgentProfile(name, overrides) {
186
+ const builtin = getBuiltinAgentProfile(name);
187
+ if (!builtin && !overrides?.bin)
188
+ return undefined;
189
+ const base = builtin ??
190
+ {
191
+ name,
192
+ bin: overrides?.bin ?? name,
193
+ args: [],
194
+ stdio: "captured",
195
+ envPassthrough: [],
196
+ parseOutput: "text",
197
+ };
198
+ if (!overrides)
199
+ return base;
200
+ const merged = {
201
+ name,
202
+ bin: overrides.bin ?? base.bin,
203
+ args: overrides.args ?? base.args,
204
+ stdio: overrides.stdio ?? base.stdio,
205
+ env: overrides.env ?? base.env,
206
+ envPassthrough: overrides.envPassthrough
207
+ ? mergePassthrough(base.envPassthrough, overrides.envPassthrough)
208
+ : base.envPassthrough,
209
+ timeoutMs: overrides.timeoutMs ?? base.timeoutMs,
210
+ parseOutput: overrides.parseOutput ?? base.parseOutput,
211
+ };
212
+ return merged;
213
+ }
214
+ function mergePassthrough(base, extra) {
215
+ const seen = new Set();
216
+ const out = [];
217
+ for (const k of [...base, ...extra]) {
218
+ if (!seen.has(k)) {
219
+ seen.add(k);
220
+ out.push(k);
221
+ }
222
+ }
223
+ return out;
224
+ }
225
+ /**
226
+ * Resolve the runnable profile for `name`, or `undefined` if none is
227
+ * available (no built-in and no user override with a `bin`).
228
+ */
229
+ export function resolveProfileFromConfig(name, agent) {
230
+ return resolveAgentProfile(name, agent?.profiles?.[name]);
231
+ }
232
+ /**
233
+ * Return the names of every profile available in `agent` config (built-in
234
+ * names plus any user-defined ones). Sorted, deduplicated.
235
+ */
236
+ export function listAgentProfileNames(agent) {
237
+ const seen = new Set(BUILTIN_AGENT_PROFILE_NAMES);
238
+ for (const name of Object.keys(agent?.profiles ?? {}))
239
+ seen.add(name);
240
+ return [...seen].sort();
241
+ }
242
+ /**
243
+ * Resolve the default profile name. Order: explicit `name` arg → config
244
+ * `agent.default` → undefined.
245
+ */
246
+ export function resolveDefaultProfileName(agent, requested) {
247
+ if (requested?.trim())
248
+ return requested.trim();
249
+ if (agent?.default?.trim())
250
+ return agent.default.trim();
251
+ return undefined;
252
+ }
253
+ /**
254
+ * Throw a {@link ConfigError} with a stable hint when the caller needs
255
+ * `agent` config but it is missing or unresolvable.
256
+ *
257
+ * Covers two cases per acceptance criteria:
258
+ *
259
+ * 1. The `agent` block is absent — agent commands are disabled.
260
+ * 2. The block exists but no usable profile (no `default`, no requested
261
+ * name, or the named profile cannot be resolved).
262
+ *
263
+ * Use as `const profile = requireAgentProfile(config.agent, requestedName)`.
264
+ */
265
+ export function requireAgentProfile(agent, requested) {
266
+ if (!agent) {
267
+ throw new ConfigError("agent commands are disabled: no `agent` block in config.json.", "INVALID_CONFIG_FILE", 'Run `akm setup` to detect and configure an agent CLI, or add an `agent` block manually (see docs/configuration.md "agent.*").');
268
+ }
269
+ const name = resolveDefaultProfileName(agent, requested);
270
+ if (!name) {
271
+ throw new ConfigError("agent commands require a profile: pass --profile or set `agent.default` in config.json.", "INVALID_CONFIG_FILE", `Available profiles: ${listAgentProfileNames(agent).join(", ")}.`);
272
+ }
273
+ const profile = resolveProfileFromConfig(name, agent);
274
+ if (!profile) {
275
+ throw new ConfigError(`agent profile "${name}" is not built-in and has no \`bin\` override.`, "INVALID_CONFIG_FILE", `Define agent.profiles."${name}".bin in config.json, or pick one of: ${listAgentProfileNames(agent).join(", ")}.`);
276
+ }
277
+ return profile;
278
+ }
279
+ /**
280
+ * Convenience: list every fully-resolved profile (built-ins merged with
281
+ * any user overrides). Used by setup detection to enumerate candidates.
282
+ */
283
+ export function listResolvedAgentProfiles(agent) {
284
+ const resolved = [];
285
+ const builtins = listBuiltinAgentProfiles();
286
+ for (const name of listAgentProfileNames(agent)) {
287
+ const profile = resolveProfileFromConfig(name, agent) ?? builtins[name];
288
+ if (profile)
289
+ resolved.push(profile);
290
+ }
291
+ return resolved;
292
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Setup-time agent CLI detection (v1 spec §12.3).
3
+ *
4
+ * Probes every known/configured agent profile by checking `which <bin>` on
5
+ * PATH. We do **not** call `<bin> --version` — that would execute the
6
+ * binary at setup time, which is unnecessarily side-effectful for
7
+ * detection. The presence of the binary on PATH is sufficient signal; the
8
+ * spawn wrapper handles failures at run-time.
9
+ *
10
+ * Tests inject a fake `whichFn` so detection branches can be exercised
11
+ * without poking at the real PATH.
12
+ */
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { listResolvedAgentProfiles } from "./config";
16
+ /**
17
+ * Default PATH lookup. Walks `process.env.PATH` and returns the first
18
+ * existing executable file. Returns `undefined` when the bin is not on
19
+ * PATH or the env is empty.
20
+ *
21
+ * `process.env.PATH` is split on the platform-correct delimiter; on
22
+ * Windows the binary may have an executable extension, but for v1 we
23
+ * keep this Unix-flavoured (Bun's primary target) and look for an exact
24
+ * match.
25
+ */
26
+ export function defaultWhich(bin, envSource = process.env) {
27
+ if (!bin || bin.includes("/") || bin.includes("\\")) {
28
+ // Absolute / relative paths: caller already specified location.
29
+ try {
30
+ return fs.statSync(bin).isFile() ? bin : undefined;
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ }
36
+ const pathVar = envSource.PATH ?? envSource.Path ?? envSource.path ?? "";
37
+ if (!pathVar)
38
+ return undefined;
39
+ const sep = pathVar.includes(";") && !pathVar.includes(":") ? ";" : path.delimiter;
40
+ for (const dir of pathVar.split(sep)) {
41
+ if (!dir)
42
+ continue;
43
+ const candidate = path.join(dir, bin);
44
+ try {
45
+ const st = fs.statSync(candidate);
46
+ if (st.isFile())
47
+ return candidate;
48
+ }
49
+ catch {
50
+ /* keep walking */
51
+ }
52
+ }
53
+ return undefined;
54
+ }
55
+ /**
56
+ * Probe every resolvable agent profile (built-ins plus user overrides)
57
+ * for an installed CLI.
58
+ *
59
+ * @param agent Optional `agent` config block. When omitted we probe the
60
+ * built-ins.
61
+ * @param whichFn Binary lookup. Tests should inject a stub.
62
+ */
63
+ export function detectAgentCliProfiles(agent, whichFn = defaultWhich) {
64
+ const profiles = listResolvedAgentProfiles(agent);
65
+ return profiles.map((profile) => probeProfile(profile, whichFn));
66
+ }
67
+ function probeProfile(profile, whichFn) {
68
+ const resolved = whichFn(profile.bin);
69
+ return {
70
+ name: profile.name,
71
+ bin: profile.bin,
72
+ available: Boolean(resolved),
73
+ ...(resolved ? { resolvedPath: resolved } : {}),
74
+ };
75
+ }
76
+ /**
77
+ * Pick the default profile to persist after a setup-time detection run.
78
+ *
79
+ * Strategy:
80
+ * 1. If the user already set `agent.default` and that profile is
81
+ * available, keep it (round-trip stability).
82
+ * 2. Otherwise, pick the first available result in detection order.
83
+ * 3. If nothing is available, return `undefined` and the caller skips
84
+ * writing `agent.default`.
85
+ */
86
+ export function pickDefaultAgentProfile(results, existingDefault) {
87
+ if (existingDefault) {
88
+ const match = results.find((r) => r.name === existingDefault && r.available);
89
+ if (match)
90
+ return match.name;
91
+ }
92
+ const first = results.find((r) => r.available);
93
+ return first?.name;
94
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Internal entry point for the `agent` integration. CLI-only project — no
3
+ * public exports map. Other akm modules import from this barrel for the
4
+ * sake of grouping imports.
5
+ *
6
+ * Surface:
7
+ * • Types: AgentProfile, AgentConfig, AgentRunResult, AgentFailureReason.
8
+ * • Profiles: getBuiltinAgentProfile, listBuiltinAgentProfiles, BUILTIN_AGENT_PROFILE_NAMES.
9
+ * • Config: parseAgentConfig, resolveProfileFromConfig, requireAgentProfile, listResolvedAgentProfiles, listAgentProfileNames.
10
+ * • Spawn: runAgent.
11
+ * • Detection: detectAgentCliProfiles, pickDefaultAgentProfile, defaultWhich.
12
+ */
13
+ export { DEFAULT_AGENT_TIMEOUT_MS, listAgentProfileNames, listResolvedAgentProfiles, parseAgentConfig, requireAgentProfile, resolveAgentProfile, resolveDefaultProfileName, resolveProfileFromConfig, } from "./config";
14
+ export { defaultWhich, detectAgentCliProfiles, pickDefaultAgentProfile } from "./detect";
15
+ export { BUILTIN_AGENT_PROFILE_NAMES, getBuiltinAgentProfile, listBuiltinAgentProfiles, } from "./profiles";
16
+ export { buildProposePrompt, buildReflectPrompt, parseAgentProposalPayload, stripJsonFences } from "./prompts";
17
+ export { runAgent } from "./spawn";
@@ -0,0 +1,65 @@
1
+ const COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR"];
2
+ /**
3
+ * Built-in profiles for the five agent CLIs the v1 spec calls out
4
+ * explicitly. The fields here are conservative defaults — every value is
5
+ * overridable from user config.
6
+ */
7
+ const BUILTINS = {
8
+ opencode: {
9
+ name: "opencode",
10
+ bin: "opencode",
11
+ args: ["run"],
12
+ stdio: "interactive",
13
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENCODE_API_KEY", "OPENCODE_CONFIG"],
14
+ parseOutput: "text",
15
+ },
16
+ claude: {
17
+ name: "claude",
18
+ bin: "claude",
19
+ args: [],
20
+ stdio: "interactive",
21
+ envPassthrough: [...COMMON_PASSTHROUGH, "ANTHROPIC_API_KEY", "CLAUDE_CONFIG"],
22
+ parseOutput: "text",
23
+ },
24
+ codex: {
25
+ name: "codex",
26
+ bin: "codex",
27
+ args: [],
28
+ stdio: "interactive",
29
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "CODEX_CONFIG"],
30
+ parseOutput: "text",
31
+ },
32
+ gemini: {
33
+ name: "gemini",
34
+ bin: "gemini",
35
+ args: [],
36
+ stdio: "interactive",
37
+ envPassthrough: [...COMMON_PASSTHROUGH, "GEMINI_API_KEY", "GOOGLE_API_KEY"],
38
+ parseOutput: "text",
39
+ },
40
+ aider: {
41
+ name: "aider",
42
+ bin: "aider",
43
+ args: ["--no-auto-commits"],
44
+ stdio: "interactive",
45
+ envPassthrough: [...COMMON_PASSTHROUGH, "OPENAI_API_KEY", "ANTHROPIC_API_KEY"],
46
+ parseOutput: "text",
47
+ },
48
+ };
49
+ /** Names of every built-in profile. Stable, sorted. */
50
+ export const BUILTIN_AGENT_PROFILE_NAMES = Object.freeze(Object.keys(BUILTINS).sort());
51
+ /** Returns the built-in profile by name, or `undefined` if not built-in. */
52
+ export function getBuiltinAgentProfile(name) {
53
+ return BUILTINS[name];
54
+ }
55
+ /**
56
+ * Return a deep copy of every built-in profile keyed by name. Callers
57
+ * should not assume reference equality with subsequent calls.
58
+ */
59
+ export function listBuiltinAgentProfiles() {
60
+ const out = {};
61
+ for (const [name, profile] of Object.entries(BUILTINS)) {
62
+ out[name] = { ...profile };
63
+ }
64
+ return out;
65
+ }