akm-cli 0.7.0 → 0.7.2

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 (332) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{src/cli.js → cli.js} +22 -8
  3. package/dist/{src/commands → commands}/installed-stashes.js +1 -1
  4. package/dist/{src/commands → commands}/source-add.js +1 -1
  5. package/dist/{src/core → core}/common.js +16 -1
  6. package/dist/{src/core → core}/config.js +5 -2
  7. package/dist/{src/indexer → indexer}/db-search.js +16 -1
  8. package/dist/{src/indexer → indexer}/graph-extraction.js +5 -3
  9. package/dist/{src/indexer → indexer}/indexer.js +27 -11
  10. package/dist/{src/indexer → indexer}/memory-inference.js +47 -58
  11. package/dist/{src/indexer → indexer}/search-source.js +1 -1
  12. package/dist/{src/llm → llm}/client.js +61 -1
  13. package/dist/{src/llm → llm}/embedder.js +8 -5
  14. package/dist/{src/llm → llm}/embedders/local.js +8 -2
  15. package/dist/{src/llm → llm}/embedders/remote.js +4 -2
  16. package/dist/{src/llm → llm}/graph-extract.js +4 -4
  17. package/dist/llm/memory-infer.js +114 -0
  18. package/dist/{src/llm → llm}/metadata-enhance.js +2 -2
  19. package/dist/{src/output → output}/cli-hints.js +2 -0
  20. package/dist/{src/setup → setup}/setup.js +30 -20
  21. package/dist/sources/providers/website.js +27 -0
  22. package/dist/{src/sources/providers/website.js → sources/website-ingest.js} +38 -51
  23. package/docs/README.md +7 -0
  24. package/docs/migration/release-notes/0.7.0.md +14 -0
  25. package/package.json +11 -8
  26. package/dist/src/llm/memory-infer.js +0 -86
  27. package/dist/tests/add-website-source.test.js +0 -119
  28. package/dist/tests/agent/agent-config-loader.test.js +0 -70
  29. package/dist/tests/agent/agent-config.test.js +0 -221
  30. package/dist/tests/agent/agent-detect.test.js +0 -100
  31. package/dist/tests/agent/agent-spawn.test.js +0 -234
  32. package/dist/tests/agent-output.test.js +0 -186
  33. package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
  34. package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
  35. package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
  36. package/dist/tests/asset-ref.test.js +0 -192
  37. package/dist/tests/asset-registry.test.js +0 -103
  38. package/dist/tests/asset-spec.test.js +0 -241
  39. package/dist/tests/bench/attribution.test.js +0 -996
  40. package/dist/tests/bench/cleanup-sigint.test.js +0 -83
  41. package/dist/tests/bench/cleanup.js +0 -234
  42. package/dist/tests/bench/cleanup.test.js +0 -166
  43. package/dist/tests/bench/cli.js +0 -1018
  44. package/dist/tests/bench/cli.test.js +0 -445
  45. package/dist/tests/bench/compare.test.js +0 -556
  46. package/dist/tests/bench/corpus.js +0 -317
  47. package/dist/tests/bench/corpus.test.js +0 -258
  48. package/dist/tests/bench/doctor.js +0 -525
  49. package/dist/tests/bench/driver.js +0 -401
  50. package/dist/tests/bench/driver.test.js +0 -584
  51. package/dist/tests/bench/environment.js +0 -233
  52. package/dist/tests/bench/environment.test.js +0 -199
  53. package/dist/tests/bench/evolve-metrics.js +0 -179
  54. package/dist/tests/bench/evolve-metrics.test.js +0 -187
  55. package/dist/tests/bench/evolve.js +0 -647
  56. package/dist/tests/bench/evolve.test.js +0 -624
  57. package/dist/tests/bench/failure-modes.test.js +0 -349
  58. package/dist/tests/bench/feedback-integrity.test.js +0 -457
  59. package/dist/tests/bench/leakage.test.js +0 -228
  60. package/dist/tests/bench/learning-curve.test.js +0 -134
  61. package/dist/tests/bench/metrics.js +0 -2395
  62. package/dist/tests/bench/metrics.test.js +0 -1150
  63. package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
  64. package/dist/tests/bench/opencode-config.js +0 -194
  65. package/dist/tests/bench/opencode-config.test.js +0 -370
  66. package/dist/tests/bench/report.js +0 -1885
  67. package/dist/tests/bench/report.test.js +0 -1038
  68. package/dist/tests/bench/run-config.js +0 -355
  69. package/dist/tests/bench/run-config.test.js +0 -298
  70. package/dist/tests/bench/run-curate-test.js +0 -32
  71. package/dist/tests/bench/run-failing-tasks.js +0 -56
  72. package/dist/tests/bench/run-full-bench.js +0 -51
  73. package/dist/tests/bench/run-items36-targeted.js +0 -69
  74. package/dist/tests/bench/run-nano-quick.js +0 -42
  75. package/dist/tests/bench/run-waveg-targeted.js +0 -62
  76. package/dist/tests/bench/runner.js +0 -699
  77. package/dist/tests/bench/runner.test.js +0 -958
  78. package/dist/tests/bench/search-bridge.test.js +0 -331
  79. package/dist/tests/bench/tmp.js +0 -131
  80. package/dist/tests/bench/trajectory.js +0 -116
  81. package/dist/tests/bench/trajectory.test.js +0 -127
  82. package/dist/tests/bench/verifier.js +0 -114
  83. package/dist/tests/bench/verifier.test.js +0 -118
  84. package/dist/tests/bench/workflow-evaluator.js +0 -557
  85. package/dist/tests/bench/workflow-evaluator.test.js +0 -421
  86. package/dist/tests/bench/workflow-spec.js +0 -345
  87. package/dist/tests/bench/workflow-spec.test.js +0 -363
  88. package/dist/tests/bench/workflow-trace.js +0 -472
  89. package/dist/tests/bench/workflow-trace.test.js +0 -254
  90. package/dist/tests/benchmark-search-quality.js +0 -536
  91. package/dist/tests/benchmark-suite.js +0 -1441
  92. package/dist/tests/capture-cli.test.js +0 -112
  93. package/dist/tests/cli-errors.test.js +0 -204
  94. package/dist/tests/commands/events.test.js +0 -370
  95. package/dist/tests/commands/history.test.js +0 -418
  96. package/dist/tests/commands/import.test.js +0 -103
  97. package/dist/tests/commands/proposal-cli.test.js +0 -209
  98. package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
  99. package/dist/tests/commands/remember.test.js +0 -97
  100. package/dist/tests/commands/scope-flags.test.js +0 -300
  101. package/dist/tests/commands/search.test.js +0 -537
  102. package/dist/tests/commands/show-indexer-parity.test.js +0 -117
  103. package/dist/tests/commands/show.test.js +0 -294
  104. package/dist/tests/common.test.js +0 -266
  105. package/dist/tests/completions.test.js +0 -142
  106. package/dist/tests/config-cli.test.js +0 -193
  107. package/dist/tests/config-llm-features.test.js +0 -139
  108. package/dist/tests/config.test.js +0 -569
  109. package/dist/tests/contracts/migration-baseline.test.js +0 -43
  110. package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
  111. package/dist/tests/contracts/spec-helpers.js +0 -46
  112. package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
  113. package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
  114. package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
  115. package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
  116. package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
  117. package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
  118. package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
  119. package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
  120. package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
  121. package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
  122. package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
  123. package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
  124. package/dist/tests/core/write-source.test.js +0 -366
  125. package/dist/tests/curate-command.test.js +0 -87
  126. package/dist/tests/db-scoring.test.js +0 -201
  127. package/dist/tests/db.test.js +0 -654
  128. package/dist/tests/distill-cli-flag.test.js +0 -208
  129. package/dist/tests/distill.test.js +0 -515
  130. package/dist/tests/docker-install.test.js +0 -120
  131. package/dist/tests/e2e.test.js +0 -1419
  132. package/dist/tests/embedder.test.js +0 -340
  133. package/dist/tests/embedding-model-config.test.js +0 -379
  134. package/dist/tests/feedback-command.test.js +0 -172
  135. package/dist/tests/file-context.test.js +0 -552
  136. package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
  137. package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
  138. package/dist/tests/fixtures/stashes/load.js +0 -166
  139. package/dist/tests/fixtures/stashes/load.test.js +0 -97
  140. package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
  141. package/dist/tests/frontmatter.test.js +0 -190
  142. package/dist/tests/fts-field-weighting.test.js +0 -254
  143. package/dist/tests/fuzzy-search.test.js +0 -230
  144. package/dist/tests/git-provider-clone.test.js +0 -45
  145. package/dist/tests/github.test.js +0 -161
  146. package/dist/tests/graph-boost-ranking.test.js +0 -305
  147. package/dist/tests/graph-extraction.test.js +0 -282
  148. package/dist/tests/helpers/usage-events.js +0 -8
  149. package/dist/tests/index-pass-llm.test.js +0 -161
  150. package/dist/tests/indexer.test.js +0 -570
  151. package/dist/tests/info-command.test.js +0 -166
  152. package/dist/tests/init.test.js +0 -69
  153. package/dist/tests/install-script.test.js +0 -246
  154. package/dist/tests/integration/agent-real-profile.test.js +0 -94
  155. package/dist/tests/issue-36-repro.test.js +0 -304
  156. package/dist/tests/issues-191-194.test.js +0 -160
  157. package/dist/tests/lesson-lint.test.js +0 -111
  158. package/dist/tests/llm-client.test.js +0 -115
  159. package/dist/tests/llm-feature-gate.test.js +0 -151
  160. package/dist/tests/llm.test.js +0 -139
  161. package/dist/tests/lockfile.test.js +0 -216
  162. package/dist/tests/manifest.test.js +0 -205
  163. package/dist/tests/markdown.test.js +0 -126
  164. package/dist/tests/matchers-unit.test.js +0 -189
  165. package/dist/tests/memory-inference.test.js +0 -299
  166. package/dist/tests/merge-scoring.test.js +0 -136
  167. package/dist/tests/metadata.test.js +0 -313
  168. package/dist/tests/migration-help.test.js +0 -89
  169. package/dist/tests/origin-resolve.test.js +0 -124
  170. package/dist/tests/output-baseline.test.js +0 -218
  171. package/dist/tests/output-shapes-unit.test.js +0 -478
  172. package/dist/tests/parallel-search.test.js +0 -272
  173. package/dist/tests/parameter-metadata.test.js +0 -365
  174. package/dist/tests/paths.test.js +0 -177
  175. package/dist/tests/progressive-disclosure.test.js +0 -280
  176. package/dist/tests/proposals.test.js +0 -279
  177. package/dist/tests/proposed-quality.test.js +0 -271
  178. package/dist/tests/provider-registry.test.js +0 -32
  179. package/dist/tests/ranking-regression.test.js +0 -548
  180. package/dist/tests/reflect-propose.test.js +0 -455
  181. package/dist/tests/registry-build-index.test.js +0 -394
  182. package/dist/tests/registry-cli.test.js +0 -290
  183. package/dist/tests/registry-index-v2.test.js +0 -430
  184. package/dist/tests/registry-install.test.js +0 -728
  185. package/dist/tests/registry-providers/parity.test.js +0 -189
  186. package/dist/tests/registry-providers/skills-sh.test.js +0 -309
  187. package/dist/tests/registry-providers/static-index.test.js +0 -238
  188. package/dist/tests/registry-resolve.test.js +0 -126
  189. package/dist/tests/registry-search.test.js +0 -923
  190. package/dist/tests/remember-frontmatter.test.js +0 -378
  191. package/dist/tests/remember-unit.test.js +0 -123
  192. package/dist/tests/ripgrep-install.test.js +0 -251
  193. package/dist/tests/ripgrep-resolve.test.js +0 -108
  194. package/dist/tests/ripgrep.test.js +0 -163
  195. package/dist/tests/save-command.test.js +0 -94
  196. package/dist/tests/save-trust-qa-fixes.test.js +0 -270
  197. package/dist/tests/scoring-pipeline.test.js +0 -648
  198. package/dist/tests/search-include-proposed-cli.test.js +0 -118
  199. package/dist/tests/self-update.test.js +0 -442
  200. package/dist/tests/semantic-search-e2e.test.js +0 -512
  201. package/dist/tests/semantic-status.test.js +0 -471
  202. package/dist/tests/setup-run.integration.js +0 -877
  203. package/dist/tests/setup-wizard.test.js +0 -198
  204. package/dist/tests/setup.test.js +0 -131
  205. package/dist/tests/source-add.test.js +0 -11
  206. package/dist/tests/source-clone.test.js +0 -254
  207. package/dist/tests/source-manage.test.js +0 -366
  208. package/dist/tests/source-providers/filesystem.test.js +0 -82
  209. package/dist/tests/source-providers/git.test.js +0 -252
  210. package/dist/tests/source-providers/website.test.js +0 -128
  211. package/dist/tests/source-qa-fixes.test.js +0 -286
  212. package/dist/tests/source-registry.test.js +0 -350
  213. package/dist/tests/source-resolve.test.js +0 -100
  214. package/dist/tests/source-source.test.js +0 -281
  215. package/dist/tests/source.test.js +0 -533
  216. package/dist/tests/tar-utils-scan.test.js +0 -73
  217. package/dist/tests/toggle-components.test.js +0 -73
  218. package/dist/tests/usage-telemetry.test.js +0 -265
  219. package/dist/tests/utility-scoring.test.js +0 -558
  220. package/dist/tests/vault-load-error.test.js +0 -78
  221. package/dist/tests/vault-qa-fixes.test.js +0 -194
  222. package/dist/tests/vault.test.js +0 -429
  223. package/dist/tests/vector-search.test.js +0 -608
  224. package/dist/tests/walker.test.js +0 -252
  225. package/dist/tests/wave2-cluster-bc.test.js +0 -228
  226. package/dist/tests/wave2-cluster-d.test.js +0 -180
  227. package/dist/tests/wave2-cluster-e.test.js +0 -179
  228. package/dist/tests/wiki-qa-fixes.test.js +0 -270
  229. package/dist/tests/wiki.test.js +0 -529
  230. package/dist/tests/workflow-cli.test.js +0 -271
  231. package/dist/tests/workflow-markdown.test.js +0 -171
  232. package/dist/tests/workflow-path-escape.test.js +0 -132
  233. package/dist/tests/workflow-qa-fixes.test.js +0 -395
  234. package/dist/tests/workflows/indexer-rejection.test.js +0 -213
  235. /package/dist/{src/commands → commands}/completions.js +0 -0
  236. /package/dist/{src/commands → commands}/config-cli.js +0 -0
  237. /package/dist/{src/commands → commands}/curate.js +0 -0
  238. /package/dist/{src/commands → commands}/distill.js +0 -0
  239. /package/dist/{src/commands → commands}/events.js +0 -0
  240. /package/dist/{src/commands → commands}/history.js +0 -0
  241. /package/dist/{src/commands → commands}/info.js +0 -0
  242. /package/dist/{src/commands → commands}/init.js +0 -0
  243. /package/dist/{src/commands → commands}/install-audit.js +0 -0
  244. /package/dist/{src/commands → commands}/migration-help.js +0 -0
  245. /package/dist/{src/commands → commands}/proposal.js +0 -0
  246. /package/dist/{src/commands → commands}/propose.js +0 -0
  247. /package/dist/{src/commands → commands}/reflect.js +0 -0
  248. /package/dist/{src/commands → commands}/registry-search.js +0 -0
  249. /package/dist/{src/commands → commands}/remember.js +0 -0
  250. /package/dist/{src/commands → commands}/search.js +0 -0
  251. /package/dist/{src/commands → commands}/self-update.js +0 -0
  252. /package/dist/{src/commands → commands}/show.js +0 -0
  253. /package/dist/{src/commands → commands}/source-clone.js +0 -0
  254. /package/dist/{src/commands → commands}/source-manage.js +0 -0
  255. /package/dist/{src/commands → commands}/vault.js +0 -0
  256. /package/dist/{src/core → core}/asset-ref.js +0 -0
  257. /package/dist/{src/core → core}/asset-registry.js +0 -0
  258. /package/dist/{src/core → core}/asset-spec.js +0 -0
  259. /package/dist/{src/core → core}/errors.js +0 -0
  260. /package/dist/{src/core → core}/events.js +0 -0
  261. /package/dist/{src/core → core}/frontmatter.js +0 -0
  262. /package/dist/{src/core → core}/lesson-lint.js +0 -0
  263. /package/dist/{src/core → core}/markdown.js +0 -0
  264. /package/dist/{src/core → core}/paths.js +0 -0
  265. /package/dist/{src/core → core}/proposals.js +0 -0
  266. /package/dist/{src/core → core}/warn.js +0 -0
  267. /package/dist/{src/core → core}/write-source.js +0 -0
  268. /package/dist/{src/indexer → indexer}/db.js +0 -0
  269. /package/dist/{src/indexer → indexer}/file-context.js +0 -0
  270. /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
  271. /package/dist/{src/indexer → indexer}/manifest.js +0 -0
  272. /package/dist/{src/indexer → indexer}/matchers.js +0 -0
  273. /package/dist/{src/indexer → indexer}/metadata.js +0 -0
  274. /package/dist/{src/indexer → indexer}/search-fields.js +0 -0
  275. /package/dist/{src/indexer → indexer}/semantic-status.js +0 -0
  276. /package/dist/{src/indexer → indexer}/usage-events.js +0 -0
  277. /package/dist/{src/indexer → indexer}/walker.js +0 -0
  278. /package/dist/{src/integrations → integrations}/agent/config.js +0 -0
  279. /package/dist/{src/integrations → integrations}/agent/detect.js +0 -0
  280. /package/dist/{src/integrations → integrations}/agent/index.js +0 -0
  281. /package/dist/{src/integrations → integrations}/agent/profiles.js +0 -0
  282. /package/dist/{src/integrations → integrations}/agent/prompts.js +0 -0
  283. /package/dist/{src/integrations → integrations}/agent/spawn.js +0 -0
  284. /package/dist/{src/integrations → integrations}/github.js +0 -0
  285. /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
  286. /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
  287. /package/dist/{src/llm → llm}/embedders/types.js +0 -0
  288. /package/dist/{src/llm → llm}/feature-gate.js +0 -0
  289. /package/dist/{src/llm → llm}/index-passes.js +0 -0
  290. /package/dist/{src/output → output}/context.js +0 -0
  291. /package/dist/{src/output → output}/renderers.js +0 -0
  292. /package/dist/{src/output → output}/shapes.js +0 -0
  293. /package/dist/{src/output → output}/text.js +0 -0
  294. /package/dist/{src/registry → registry}/build-index.js +0 -0
  295. /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
  296. /package/dist/{src/registry → registry}/factory.js +0 -0
  297. /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
  298. /package/dist/{src/registry → registry}/providers/index.js +0 -0
  299. /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
  300. /package/dist/{src/registry → registry}/providers/static-index.js +0 -0
  301. /package/dist/{src/registry → registry}/providers/types.js +0 -0
  302. /package/dist/{src/registry → registry}/resolve.js +0 -0
  303. /package/dist/{src/registry → registry}/types.js +0 -0
  304. /package/dist/{src/setup → setup}/detect.js +0 -0
  305. /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
  306. /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
  307. /package/dist/{src/setup → setup}/steps.js +0 -0
  308. /package/dist/{src/sources → sources}/include.js +0 -0
  309. /package/dist/{src/sources → sources}/provider-factory.js +0 -0
  310. /package/dist/{src/sources → sources}/provider.js +0 -0
  311. /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
  312. /package/dist/{src/sources → sources}/providers/git.js +0 -0
  313. /package/dist/{src/sources → sources}/providers/index.js +0 -0
  314. /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
  315. /package/dist/{src/sources → sources}/providers/npm.js +0 -0
  316. /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
  317. /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
  318. /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
  319. /package/dist/{src/sources → sources}/resolve.js +0 -0
  320. /package/dist/{src/sources → sources}/types.js +0 -0
  321. /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
  322. /package/dist/{src/version.js → version.js} +0 -0
  323. /package/dist/{src/wiki → wiki}/wiki.js +0 -0
  324. /package/dist/{src/workflows → workflows}/authoring.js +0 -0
  325. /package/dist/{src/workflows → workflows}/cli.js +0 -0
  326. /package/dist/{src/workflows → workflows}/db.js +0 -0
  327. /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
  328. /package/dist/{src/workflows → workflows}/parser.js +0 -0
  329. /package/dist/{src/workflows → workflows}/renderer.js +0 -0
  330. /package/dist/{src/workflows → workflows}/runs.js +0 -0
  331. /package/dist/{src/workflows → workflows}/schema.js +0 -0
  332. /package/dist/{src/workflows → workflows}/validator.js +0 -0
@@ -51,8 +51,9 @@ export async function chatCompletion(config, messages, options) {
51
51
  messages,
52
52
  temperature: options?.temperature ?? config.temperature ?? 0.3,
53
53
  max_tokens: options?.maxTokens ?? config.maxTokens ?? 512,
54
+ ...config.extraParams,
54
55
  }),
55
- });
56
+ }, 30_000, options?.signal);
56
57
  if (!response.ok) {
57
58
  const rawBody = await response.text().catch(() => "");
58
59
  const safeBody = redactErrorBody(rawBody);
@@ -65,6 +66,7 @@ export async function chatCompletion(config, messages, options) {
65
66
  export function stripJsonFences(raw) {
66
67
  return raw
67
68
  .trim()
69
+ .replace(/<think>[\s\S]*?<\/think>/gi, "")
68
70
  .replace(/^```(?:json)?\s*\n?/i, "")
69
71
  .replace(/\n?```\s*$/i, "")
70
72
  .trim();
@@ -78,6 +80,64 @@ export function parseJsonResponse(raw) {
78
80
  return undefined;
79
81
  }
80
82
  }
83
+ /**
84
+ * Best-effort recovery for providers that wrap JSON in extra prose or fenced
85
+ * blocks. Extracts the first balanced top-level object/array and parses it.
86
+ */
87
+ export function parseEmbeddedJsonResponse(raw) {
88
+ const direct = parseJsonResponse(raw);
89
+ if (direct !== undefined)
90
+ return direct;
91
+ const text = stripJsonFences(raw);
92
+ let arrayFallback;
93
+ for (let start = 0; start < text.length; start++) {
94
+ const opener = text[start];
95
+ if (opener !== "{" && opener !== "[")
96
+ continue;
97
+ const closer = opener === "{" ? "}" : "]";
98
+ let depth = 0;
99
+ let inString = false;
100
+ let escaped = false;
101
+ for (let i = start; i < text.length; i++) {
102
+ const ch = text[i];
103
+ if (inString) {
104
+ if (escaped) {
105
+ escaped = false;
106
+ }
107
+ else if (ch === "\\") {
108
+ escaped = true;
109
+ }
110
+ else if (ch === '"') {
111
+ inString = false;
112
+ }
113
+ continue;
114
+ }
115
+ if (ch === '"') {
116
+ inString = true;
117
+ continue;
118
+ }
119
+ if (ch === opener)
120
+ depth += 1;
121
+ if (ch === closer) {
122
+ depth -= 1;
123
+ if (depth === 0) {
124
+ try {
125
+ const parsed = JSON.parse(text.slice(start, i + 1));
126
+ if (!Array.isArray(parsed)) {
127
+ return parsed;
128
+ }
129
+ arrayFallback ??= parsed;
130
+ break;
131
+ }
132
+ catch {
133
+ break;
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return arrayFallback;
140
+ }
81
141
  // ── Availability check ──────────────────────────────────────────────────────
82
142
  /**
83
143
  * Check if the LLM endpoint is reachable.
@@ -47,14 +47,14 @@ export function resetLocalEmbedder() {
47
47
  * Results are cached in an LRU cache (max ~100 entries) keyed by query text
48
48
  * and embedding config. Repeated identical queries return the cached vector.
49
49
  */
50
- export async function embed(text, embeddingConfig) {
50
+ export async function embed(text, embeddingConfig, signal) {
51
51
  const key = embedCacheKey(text, embeddingConfig);
52
52
  const cached = getCachedEmbedding(key);
53
53
  if (cached)
54
54
  return cached;
55
55
  const result = embeddingConfig && hasRemoteEndpoint(embeddingConfig)
56
- ? await new RemoteEmbedder(embeddingConfig).embed(text)
57
- : await localEmbedder.embedWithModel(text, embeddingConfig?.localModel);
56
+ ? await new RemoteEmbedder(embeddingConfig).embed(text, signal)
57
+ : await localEmbedder.embed(text, signal);
58
58
  setCachedEmbedding(key, result);
59
59
  return result;
60
60
  }
@@ -63,16 +63,19 @@ export async function embed(text, embeddingConfig) {
63
63
  * Uses the OpenAI-compatible batch API for remote endpoints (batches of 100).
64
64
  * Falls back to sequential embedding for the local transformer pipeline.
65
65
  */
66
- export async function embedBatch(texts, embeddingConfig) {
66
+ export async function embedBatch(texts, embeddingConfig, signal) {
67
67
  if (texts.length === 0)
68
68
  return [];
69
69
  if (embeddingConfig && hasRemoteEndpoint(embeddingConfig)) {
70
- return new RemoteEmbedder(embeddingConfig).embedBatch(texts);
70
+ return new RemoteEmbedder(embeddingConfig).embedBatch(texts, signal);
71
71
  }
72
72
  // Local transformer: process sequentially (pipeline handles one at a time)
73
73
  const localModel = embeddingConfig?.localModel;
74
74
  const results = [];
75
75
  for (const text of texts) {
76
+ if (signal?.aborted) {
77
+ throw signal.reason instanceof Error ? signal.reason : new Error("embedding interrupted");
78
+ }
76
79
  results.push(await localEmbedder.embedWithModel(text, localModel));
77
80
  }
78
81
  return results;
@@ -42,14 +42,20 @@ export class LocalEmbedder {
42
42
  this.pipelinePromise = undefined;
43
43
  this.pipelineModelName = undefined;
44
44
  }
45
- async embed(text) {
45
+ async embed(text, signal) {
46
+ if (signal?.aborted) {
47
+ throw signal.reason instanceof Error ? signal.reason : new Error("embedding interrupted");
48
+ }
46
49
  return this.embedWithModel(text, this.defaultModel);
47
50
  }
48
- async embedBatch(texts) {
51
+ async embedBatch(texts, signal) {
49
52
  if (texts.length === 0)
50
53
  return [];
51
54
  const results = [];
52
55
  for (const text of texts) {
56
+ if (signal?.aborted) {
57
+ throw signal.reason instanceof Error ? signal.reason : new Error("embedding interrupted");
58
+ }
53
59
  results.push(await this.embedWithModel(text, this.defaultModel));
54
60
  }
55
61
  return results;
@@ -15,7 +15,7 @@ export class RemoteEmbedder {
15
15
  constructor(config) {
16
16
  this.config = config;
17
17
  }
18
- async embed(text) {
18
+ async embed(text, signal) {
19
19
  const headers = this.buildHeaders();
20
20
  const body = {
21
21
  input: text,
@@ -32,6 +32,7 @@ export class RemoteEmbedder {
32
32
  method: "POST",
33
33
  headers,
34
34
  body: JSON.stringify(body),
35
+ signal,
35
36
  });
36
37
  if (!response.ok) {
37
38
  const errBody = await response.text().catch(() => "");
@@ -43,7 +44,7 @@ export class RemoteEmbedder {
43
44
  }
44
45
  return l2Normalize(json.data[0].embedding);
45
46
  }
46
- async embedBatch(texts) {
47
+ async embedBatch(texts, signal) {
47
48
  if (texts.length === 0)
48
49
  return [];
49
50
  const results = [];
@@ -66,6 +67,7 @@ export class RemoteEmbedder {
66
67
  method: "POST",
67
68
  headers,
68
69
  body: JSON.stringify(body),
70
+ signal,
69
71
  });
70
72
  if (!response.ok) {
71
73
  const respBody = await response.text().catch(() => "");
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import { toErrorMessage } from "../core/common";
21
21
  import { warn } from "../core/warn";
22
- import { chatCompletion, parseJsonResponse } from "./client";
22
+ import { chatCompletion, parseEmbeddedJsonResponse } from "./client";
23
23
  /** Hard cap on body chars sent to the model. */
24
24
  const MAX_BODY_CHARS = 4000;
25
25
  /** Hard cap on entities returned per asset — guards against runaway LLM output. */
@@ -49,7 +49,7 @@ Asset body:
49
49
  * JSON, empty response). Errors are logged via `warn()` but never thrown — a
50
50
  * failed extraction for one asset must not abort the rest of the index pass.
51
51
  */
52
- export async function extractGraphFromBody(llmConfig, body) {
52
+ export async function extractGraphFromBody(llmConfig, body, signal) {
53
53
  const empty = { entities: [], relations: [] };
54
54
  const trimmedBody = body.trim();
55
55
  if (!trimmedBody)
@@ -61,14 +61,14 @@ export async function extractGraphFromBody(llmConfig, body) {
61
61
  chatCompletion(llmConfig, [
62
62
  { role: "system", content: SYSTEM_PROMPT },
63
63
  { role: "user", content: userPrompt },
64
- ], { maxTokens: 1024, temperature: 0.1 }),
64
+ ], { maxTokens: 1024, temperature: 0.1, signal }),
65
65
  new Promise((_, reject) => {
66
66
  timeoutHandle = setTimeout(() => reject(new Error("graph extraction timed out")), LLM_TIMEOUT_MS);
67
67
  }),
68
68
  ]);
69
69
  if (!raw)
70
70
  return empty;
71
- const parsed = parseJsonResponse(raw);
71
+ const parsed = parseEmbeddedJsonResponse(raw);
72
72
  if (!parsed) {
73
73
  warn("graph extraction: invalid JSON response from LLM; skipping asset.");
74
74
  return empty;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * LLM helper for the `akm index` memory-inference pass (#201).
3
+ *
4
+ * Compresses a single memory body into one higher-signal derived memory. The
5
+ * pass itself (in `src/indexer/memory-inference.ts`) is responsible for
6
+ * deciding which memories are pending, persisting the derived memory with the
7
+ * correct frontmatter (`inferred: true`, `source: <parent-ref>`), and marking
8
+ * the parent as processed for idempotency.
9
+ *
10
+ * This module is intentionally tiny and stateless so tests can stub it via
11
+ * `mock.module("../src/llm/memory-infer", ...)` without hitting a network.
12
+ *
13
+ * Locked v1 contract (#208): the LLM connection always comes from the
14
+ * shared `akm.llm` block — never from a per-pass override. Callers obtain
15
+ * the connection via `resolveIndexPassLLM("memory", config)` and pass it
16
+ * straight through.
17
+ */
18
+ import { toErrorMessage } from "../core/common";
19
+ import { warn } from "../core/warn";
20
+ import { chatCompletion, parseEmbeddedJsonResponse } from "./client";
21
+ /** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
22
+ const MAX_BODY_CHARS = 4000;
23
+ /** Hard timeout for the LLM call. The index run must not hang on a misbehaving endpoint. */
24
+ const LLM_TIMEOUT_MS = 30_000;
25
+ const SYSTEM_PROMPT = "You compress a developer memory into one high-signal derived memory for later retrieval. " +
26
+ "Return only valid JSON. No prose outside the JSON object. No markdown fences.";
27
+ const USER_PROMPT_PREFIX = `Compress the memory below into one concise, information-dense derived memory.
28
+
29
+ Rules:
30
+ - Output ONLY a JSON object with exactly these keys: {"title": string, "description": string, "tags": string[], "searchHints": string[], "content": string}.
31
+ - ` +
32
+ '"title"' +
33
+ ` is a short, descriptive title for the derived memory.
34
+ - ` +
35
+ '"description"' +
36
+ ` is one sentence explaining why this derived memory matters.
37
+ - ` +
38
+ '"tags"' +
39
+ ` contains 3-8 specific keywords.
40
+ - ` +
41
+ '"searchHints"' +
42
+ ` contains 3-6 natural-language retrieval phrases.
43
+ - ` +
44
+ '"content"' +
45
+ ` must be compact markdown that preserves the reusable insight, root cause, fix, constraints, and applicability conditions when present.
46
+ - Prefer 2-4 short sections with informative headings over long prose.
47
+ - Omit timestamps, verification-only metrics, pleasantries, and session-specific chatter unless they are essential to applying the insight later.
48
+ - Preserve technical specifics (names, versions, identifiers, selectors, file paths, config keys) verbatim.
49
+
50
+ Memory:
51
+ `;
52
+ /**
53
+ * Compress a single memory body into one derived memory via the configured LLM.
54
+ *
55
+ * Returns `undefined` on any failure (timeout, invalid JSON, empty response).
56
+ * Errors
57
+ * are logged via `warn()` but never thrown — a failed split for one memory
58
+ * must not abort the rest of the index pass.
59
+ */
60
+ export async function compressMemoryToDerivedMemory(llmConfig, body, signal) {
61
+ const trimmedBody = body.trim();
62
+ if (!trimmedBody)
63
+ return undefined;
64
+ const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
65
+ let timeoutHandle;
66
+ try {
67
+ const raw = await Promise.race([
68
+ chatCompletion(llmConfig, [
69
+ { role: "system", content: SYSTEM_PROMPT },
70
+ { role: "user", content: userPrompt },
71
+ ], { maxTokens: 768, temperature: 0.1, signal }),
72
+ new Promise((_, reject) => {
73
+ timeoutHandle = setTimeout(() => reject(new Error("memory inference timed out")), LLM_TIMEOUT_MS);
74
+ }),
75
+ ]);
76
+ if (!raw)
77
+ return undefined;
78
+ const parsed = parseEmbeddedJsonResponse(raw);
79
+ if (!parsed) {
80
+ warn("memory inference: invalid JSON response from LLM; skipping memory.");
81
+ return undefined;
82
+ }
83
+ const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
84
+ const description = typeof parsed.description === "string" ? parsed.description.trim() : "";
85
+ const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
86
+ const tags = Array.isArray(parsed.tags)
87
+ ? parsed.tags
88
+ .filter((t) => typeof t === "string")
89
+ .map((t) => t.trim())
90
+ .filter(Boolean)
91
+ .slice(0, 8)
92
+ : [];
93
+ const searchHints = Array.isArray(parsed.searchHints)
94
+ ? parsed.searchHints
95
+ .filter((h) => typeof h === "string")
96
+ .map((h) => h.trim())
97
+ .filter(Boolean)
98
+ .slice(0, 6)
99
+ : [];
100
+ if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
101
+ warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
102
+ return undefined;
103
+ }
104
+ return { title, description, tags, searchHints, content };
105
+ }
106
+ catch (err) {
107
+ warn(`memory inference failed: ${toErrorMessage(err)}`);
108
+ return undefined;
109
+ }
110
+ finally {
111
+ if (timeoutHandle !== undefined)
112
+ clearTimeout(timeoutHandle);
113
+ }
114
+ }
@@ -11,7 +11,7 @@ const SYSTEM_PROMPT = `You are a metadata generator for a developer asset regist
11
11
  * Use an LLM to enhance a stash entry's metadata: improve description,
12
12
  * generate searchHints, and suggest tags.
13
13
  */
14
- export async function enhanceMetadata(config, entry, fileContent) {
14
+ export async function enhanceMetadata(config, entry, fileContent, signal) {
15
15
  const contextParts = [`Name: ${entry.name}`, `Type: ${entry.type}`];
16
16
  if (entry.description)
17
17
  contextParts.push(`Current description: ${entry.description}`);
@@ -33,7 +33,7 @@ Return ONLY the JSON object, no explanation.`;
33
33
  const raw = await chatCompletion(config, [
34
34
  { role: "system", content: SYSTEM_PROMPT },
35
35
  { role: "user", content: userPrompt },
36
- ]);
36
+ ], { signal });
37
37
  const parsed = parseJsonResponse(raw);
38
38
  if (!parsed)
39
39
  return {};
@@ -133,6 +133,7 @@ akm remember "Deployment needs VPN access" # Record a memory in your stash
133
133
  akm remember --name release-retro < notes.md # Save multiline memory from stdin
134
134
  akm import ./docs/auth-flow.md # Import a file as knowledge
135
135
  akm import - --name scratch-notes < notes.md # Import stdin as a knowledge doc
136
+ akm import https://example.com/docs/auth # Fetch one URL and import it as knowledge
136
137
  akm workflow create ship-release # Create a workflow asset in the stash
137
138
  akm workflow validate workflows/foo.md # Validate a workflow file or ref; lists every error
138
139
  akm workflow next workflow:ship-release # Start or resume the next workflow step
@@ -160,6 +161,7 @@ akm wiki show research # Path, description, counts, last
160
161
  akm wiki pages research # Page refs + descriptions (excludes schema/index/log; includes raw/)
161
162
  akm wiki search research "attention" # Scoped search (equivalent to --type wiki --wiki research)
162
163
  akm wiki stash research ./paper.md # Copy source into raw/<slug>.md (never overwrites)
164
+ akm wiki stash research https://example.com/paper # Fetch one URL into raw/<slug>.md
163
165
  echo "..." | akm wiki stash research - # stdin form
164
166
  akm wiki lint research # Structural checks: orphans, broken xrefs, uncited raws, stale index
165
167
  akm wiki ingest research # Print the ingest workflow for this wiki (no action)
@@ -19,15 +19,22 @@ import { probeLlmCapabilities } from "../llm/client";
19
19
  import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "../llm/embedder";
20
20
  import { detectAgentPlatforms, detectOllama } from "./detect";
21
21
  import { createSetupContext, runSetupSteps } from "./steps";
22
- // ── Constants ───────────────────────────────────────────────────────────────
23
22
  /**
24
23
  * Recommended GitHub repositories shown during setup.
25
- *
26
- * Currently empty — populating from the akm-registry at runtime is a
27
- * separate feature. The wizard prompt infrastructure is retained for that
28
- * future use.
29
24
  */
30
- const RECOMMENDED_GITHUB_REPOS = [];
25
+ const RECOMMENDED_GITHUB_REPOS = [
26
+ {
27
+ url: "https://github.com/itlackey/akm-stash",
28
+ name: "itlackey/akm-stash",
29
+ hint: "official onboarding stash",
30
+ defaultSelected: true,
31
+ },
32
+ {
33
+ url: "https://github.com/andrewyng/context-hub",
34
+ name: "andrewyng/context-hub",
35
+ hint: "optional community prompt and context stash",
36
+ },
37
+ ];
31
38
  // Approximate first-download sizes used in the setup note.
32
39
  // LOCAL_MODEL_APPROX_SIZE_MB tracks the default local model (DEFAULT_LOCAL_MODEL).
33
40
  const LOCAL_MODEL_APPROX_SIZE_MB = 130;
@@ -466,7 +473,7 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels) {
466
473
  }
467
474
  return llm;
468
475
  }
469
- async function stepRegistries(current) {
476
+ export async function stepRegistries(current) {
470
477
  const defaults = DEFAULT_CONFIG.registries ?? [];
471
478
  const currentRegistries = current.registries ?? defaults;
472
479
  const defaultUrls = new Set(defaults.map((r) => r.url));
@@ -508,40 +515,43 @@ async function stepRegistries(current) {
508
515
  * @internal Exported for testing only.
509
516
  */
510
517
  export async function stepAddSources(current) {
511
- const stashes = [...(current.sources ?? current.stashes ?? [])];
512
- if (stashes.length > 0) {
513
- p.log.info(`You have ${stashes.length} existing stash source(s).`);
518
+ const sources = [...(current.sources ?? current.stashes ?? [])];
519
+ if (sources.length > 0) {
520
+ p.log.info(`You have ${sources.length} existing stash source(s).`);
514
521
  }
515
522
  // ── Recommended GitHub repos ───────────────────────────────────────────
516
523
  // Skip the prompt entirely when there are no recommendations to show.
517
524
  // The infrastructure is retained for a future registry-driven version.
518
525
  if (RECOMMENDED_GITHUB_REPOS.length > 0) {
519
- const existingUrls = new Set(stashes.map((s) => s.url));
526
+ const existingUrls = new Set(sources.map((s) => s.url));
520
527
  const repoOptions = RECOMMENDED_GITHUB_REPOS.map((r) => ({
521
528
  value: r.url,
522
529
  label: r.name,
523
530
  hint: existingUrls.has(r.url) ? `${r.hint} (already added)` : r.hint,
524
531
  }));
532
+ const initialValues = sources.length > 0
533
+ ? repoOptions.filter((o) => existingUrls.has(o.value)).map((o) => o.value)
534
+ : RECOMMENDED_GITHUB_REPOS.filter((r) => r.defaultSelected).map((r) => r.url);
525
535
  const selectedRepos = await prompt(() => p.multiselect({
526
536
  message: "Recommended GitHub repositories — toggle to add or remove:",
527
537
  options: repoOptions,
528
- initialValues: repoOptions.filter((o) => existingUrls.has(o.value)).map((o) => o.value),
538
+ initialValues,
529
539
  required: false,
530
540
  }));
531
541
  // Add newly selected repos
532
542
  for (const url of selectedRepos) {
533
543
  if (!existingUrls.has(url)) {
534
544
  const rec = RECOMMENDED_GITHUB_REPOS.find((r) => r.url === url);
535
- stashes.push({ type: "git", url, name: rec?.name });
545
+ sources.push({ type: "git", url, name: rec?.name });
536
546
  existingUrls.add(url);
537
547
  }
538
548
  }
539
549
  // Remove deselected repos that were previously configured
540
550
  for (const rec of RECOMMENDED_GITHUB_REPOS) {
541
551
  if (existingUrls.has(rec.url) && !selectedRepos.includes(rec.url)) {
542
- const idx = stashes.findIndex((s) => s.url === rec.url);
552
+ const idx = sources.findIndex((s) => s.url === rec.url);
543
553
  if (idx !== -1) {
544
- stashes.splice(idx, 1);
554
+ sources.splice(idx, 1);
545
555
  existingUrls.delete(rec.url);
546
556
  p.log.info(`Removed ${rec.name}.`);
547
557
  }
@@ -583,8 +593,8 @@ export async function stepAddSources(current) {
583
593
  const entry = { type: "git", url: url.trim() };
584
594
  if (name.trim())
585
595
  entry.name = name.trim();
586
- if (!stashes.some((s) => s.url === entry.url)) {
587
- stashes.push(entry);
596
+ if (!sources.some((s) => s.url === entry.url)) {
597
+ sources.push(entry);
588
598
  }
589
599
  else {
590
600
  p.log.warn("This URL is already configured.");
@@ -611,15 +621,15 @@ export async function stepAddSources(current) {
611
621
  const entry = { type: "filesystem", path: resolved };
612
622
  if (name.trim())
613
623
  entry.name = name.trim();
614
- if (!stashes.some((s) => s.path === entry.path)) {
615
- stashes.push(entry);
624
+ if (!sources.some((s) => s.path === entry.path)) {
625
+ sources.push(entry);
616
626
  }
617
627
  else {
618
628
  p.log.warn("This path is already configured.");
619
629
  }
620
630
  }
621
631
  }
622
- return stashes;
632
+ return sources;
623
633
  }
624
634
  async function stepAgentPlatforms(current) {
625
635
  const platforms = detectAgentPlatforms();
@@ -0,0 +1,27 @@
1
+ import { registerSourceProvider } from "../provider-factory";
2
+ import { ensureWebsiteMirror, getWebsiteCachePaths, validateWebsiteUrl } from "../website-ingest";
3
+ /**
4
+ * Website source provider — thin adapter over the shared website ingest module.
5
+ */
6
+ class WebsiteSourceProvider {
7
+ kind = "website";
8
+ name;
9
+ #config;
10
+ #url;
11
+ constructor(config) {
12
+ this.#config = config;
13
+ this.name = config.name ?? "website";
14
+ this.#url = validateWebsiteUrl(config.url ?? "");
15
+ }
16
+ async init(_ctx) {
17
+ // URL validation already happens in the constructor; nothing else to do.
18
+ }
19
+ path() {
20
+ return getWebsiteCachePaths(this.#url).stashDir;
21
+ }
22
+ async sync() {
23
+ await ensureWebsiteMirror(this.#config, { requireStashDir: true });
24
+ }
25
+ }
26
+ registerSourceProvider("website", (config) => new WebsiteSourceProvider(config));
27
+ export { WebsiteSourceProvider };