akm-cli 0.9.0-beta.9 → 0.9.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/CHANGELOG.md +715 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  39. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  40. package/dist/assets/templates/html/health.html +281 -111
  41. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  42. package/dist/assets/wiki/schema-template.md +4 -4
  43. package/dist/cli/clack.js +56 -0
  44. package/dist/cli/config-migrate.js +7 -1
  45. package/dist/cli/confirm.js +1 -1
  46. package/dist/cli/parse-args.js +46 -1
  47. package/dist/cli/shared.js +28 -0
  48. package/dist/cli.js +25 -21
  49. package/dist/commands/agent/agent-dispatch.js +3 -2
  50. package/dist/commands/agent/agent-support.js +0 -7
  51. package/dist/commands/agent/contribute-cli.js +26 -7
  52. package/dist/commands/config-cli.js +26 -13
  53. package/dist/commands/env/child-env.js +47 -0
  54. package/dist/commands/env/env-binding.js +95 -0
  55. package/dist/commands/env/env-cli.js +228 -292
  56. package/dist/commands/env/env.js +14 -67
  57. package/dist/commands/env/secret-cli.js +140 -138
  58. package/dist/commands/feedback-cli.js +156 -155
  59. package/dist/commands/graph/graph-cli.js +5 -13
  60. package/dist/commands/graph/graph.js +3 -3
  61. package/dist/commands/health/advisories.js +151 -0
  62. package/dist/commands/health/checks.js +103 -16
  63. package/dist/commands/health/html-report.js +447 -81
  64. package/dist/commands/health/improve-metrics.js +771 -0
  65. package/dist/commands/health/llm-usage.js +65 -0
  66. package/dist/commands/health/md-report.js +103 -0
  67. package/dist/commands/health/metrics.js +278 -0
  68. package/dist/commands/health/stash-exposure.js +46 -0
  69. package/dist/commands/health/surfaces.js +216 -0
  70. package/dist/commands/health/task-runs.js +135 -0
  71. package/dist/commands/health/types.js +26 -0
  72. package/dist/commands/health/windows.js +195 -0
  73. package/dist/commands/health.js +91 -1091
  74. package/dist/commands/improve/anti-collapse.js +170 -0
  75. package/dist/commands/improve/calibration.js +161 -0
  76. package/dist/commands/improve/collapse-detector.js +421 -0
  77. package/dist/commands/improve/consolidate/chunking.js +141 -0
  78. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  79. package/dist/commands/improve/consolidate/merge.js +145 -0
  80. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  81. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  82. package/dist/commands/improve/consolidate.js +1295 -1277
  83. package/dist/commands/improve/dedup.js +482 -0
  84. package/dist/commands/improve/distill/content-repair.js +202 -0
  85. package/dist/commands/improve/distill/promote-memory.js +229 -0
  86. package/dist/commands/improve/distill/quality-gate.js +236 -0
  87. package/dist/commands/improve/distill-guards.js +127 -0
  88. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  89. package/dist/commands/improve/distill.js +228 -605
  90. package/dist/commands/improve/eligibility.js +434 -0
  91. package/dist/commands/improve/encoding-salience.js +205 -0
  92. package/dist/commands/improve/extract-cli.js +179 -59
  93. package/dist/commands/improve/extract-prompt.js +54 -3
  94. package/dist/commands/improve/extract-watch.js +140 -0
  95. package/dist/commands/improve/extract.js +409 -43
  96. package/dist/commands/improve/feedback-valence.js +54 -0
  97. package/dist/commands/improve/hot-probation.js +45 -0
  98. package/dist/commands/improve/improve-auto-accept.js +157 -10
  99. package/dist/commands/improve/improve-cli.js +115 -73
  100. package/dist/commands/improve/improve-profiles.js +28 -8
  101. package/dist/commands/improve/improve-result-file.js +15 -25
  102. package/dist/commands/improve/improve-session.js +58 -0
  103. package/dist/commands/improve/improve.js +485 -2764
  104. package/dist/commands/improve/locks.js +154 -0
  105. package/dist/commands/improve/loop-stages.js +1100 -0
  106. package/dist/commands/improve/memory/memory-belief.js +14 -15
  107. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  108. package/dist/commands/improve/memory/memory-improve.js +27 -27
  109. package/dist/commands/improve/outcome-loop.js +270 -0
  110. package/dist/commands/improve/preparation.js +2002 -0
  111. package/dist/commands/improve/proactive-maintenance.js +37 -35
  112. package/dist/commands/improve/procedural.js +398 -0
  113. package/dist/commands/improve/recombine.js +818 -0
  114. package/dist/commands/improve/reflect-noise.js +0 -0
  115. package/dist/commands/improve/reflect.js +206 -45
  116. package/dist/commands/improve/salience.js +455 -0
  117. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  118. package/dist/commands/improve/shared.js +51 -0
  119. package/dist/commands/improve/triage.js +93 -0
  120. package/dist/commands/lint/agent-linter.js +19 -24
  121. package/dist/commands/lint/base-linter.js +173 -60
  122. package/dist/commands/lint/command-linter.js +19 -24
  123. package/dist/commands/lint/env-key-rules.js +38 -1
  124. package/dist/commands/lint/fact-linter.js +39 -0
  125. package/dist/commands/lint/index.js +31 -13
  126. package/dist/commands/lint/memory-linter.js +1 -1
  127. package/dist/commands/lint/registry.js +7 -2
  128. package/dist/commands/lint/task-linter.js +3 -3
  129. package/dist/commands/lint/workflow-linter.js +26 -1
  130. package/dist/commands/observability-cli.js +4 -4
  131. package/dist/commands/proposal/drain-policies.js +13 -4
  132. package/dist/commands/proposal/drain.js +45 -51
  133. package/dist/commands/proposal/legacy-import.js +115 -0
  134. package/dist/commands/proposal/proposal-cli.js +24 -34
  135. package/dist/commands/proposal/proposal.js +2 -1
  136. package/dist/commands/proposal/propose.js +8 -3
  137. package/dist/commands/proposal/repository.js +829 -0
  138. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  139. package/dist/commands/proposal/validators/proposals.js +93 -895
  140. package/dist/commands/read/curate.js +410 -111
  141. package/dist/commands/read/knowledge.js +10 -3
  142. package/dist/commands/read/remember-cli.js +133 -138
  143. package/dist/commands/read/search-cli.js +15 -8
  144. package/dist/commands/read/search.js +22 -11
  145. package/dist/commands/read/show.js +106 -14
  146. package/dist/commands/registry-cli.js +76 -87
  147. package/dist/commands/remember.js +11 -12
  148. package/dist/commands/sources/add-cli.js +91 -95
  149. package/dist/commands/sources/history.js +1 -1
  150. package/dist/commands/sources/init.js +66 -18
  151. package/dist/commands/sources/installed-stashes.js +11 -3
  152. package/dist/commands/sources/migration-help.js +7 -4
  153. package/dist/commands/sources/schema-repair.js +44 -46
  154. package/dist/commands/sources/self-update.js +2 -2
  155. package/dist/commands/sources/source-add.js +7 -3
  156. package/dist/commands/sources/sources-cli.js +3 -3
  157. package/dist/commands/sources/stash-cli.js +19 -39
  158. package/dist/commands/sources/stash-skeleton.js +57 -8
  159. package/dist/commands/tasks/default-tasks.js +15 -2
  160. package/dist/commands/tasks/tasks-cli.js +20 -29
  161. package/dist/commands/tasks/tasks.js +39 -11
  162. package/dist/commands/wiki-cli.js +23 -38
  163. package/dist/commands/workflow-cli.js +291 -13
  164. package/dist/core/asset/asset-registry.js +3 -1
  165. package/dist/core/asset/asset-spec.js +79 -5
  166. package/dist/core/asset/frontmatter.js +188 -167
  167. package/dist/core/asset/markdown.js +8 -0
  168. package/dist/core/authoring-rules.js +92 -0
  169. package/dist/core/common.js +4 -23
  170. package/dist/core/concurrent.js +10 -1
  171. package/dist/core/config/config-io.js +10 -1
  172. package/dist/core/config/config-migration.js +18 -40
  173. package/dist/core/config/config-schema.js +403 -62
  174. package/dist/core/config/config-types.js +3 -3
  175. package/dist/core/config/config.js +67 -22
  176. package/dist/core/deep-merge.js +38 -0
  177. package/dist/core/errors.js +1 -0
  178. package/dist/core/eval/rank-metrics.js +113 -0
  179. package/dist/core/events.js +4 -7
  180. package/dist/core/improve-types.js +47 -8
  181. package/dist/core/json-schema.js +142 -0
  182. package/dist/core/logs-db.js +14 -75
  183. package/dist/core/parse.js +36 -16
  184. package/dist/core/paths.js +18 -18
  185. package/dist/core/standards/resolve-standards-context.js +87 -0
  186. package/dist/core/standards/resolve-stash-standards.js +99 -0
  187. package/dist/core/standards/resolve-type-conventions.js +66 -0
  188. package/dist/core/state/migrations.js +770 -0
  189. package/dist/core/state-db.js +132 -1126
  190. package/dist/core/structured.js +69 -0
  191. package/dist/core/time.js +53 -0
  192. package/dist/core/warn.js +21 -0
  193. package/dist/core/write-source.js +37 -0
  194. package/dist/indexer/db/db.js +261 -770
  195. package/dist/indexer/db/entry-mapper.js +41 -0
  196. package/dist/indexer/db/graph-db.js +129 -86
  197. package/dist/indexer/db/llm-cache.js +2 -2
  198. package/dist/indexer/db/schema.js +516 -0
  199. package/dist/indexer/ensure-index.js +36 -92
  200. package/dist/indexer/feedback/utility-policy.js +75 -0
  201. package/dist/indexer/graph/graph-boost.js +51 -41
  202. package/dist/indexer/graph/graph-extraction.js +207 -4
  203. package/dist/indexer/index-writer-lock.js +18 -11
  204. package/dist/indexer/index-written-assets.js +105 -0
  205. package/dist/indexer/indexer.js +182 -204
  206. package/dist/indexer/passes/dir-staleness.js +114 -0
  207. package/dist/indexer/passes/memory-inference.js +13 -5
  208. package/dist/indexer/passes/metadata.js +20 -0
  209. package/dist/indexer/read-preflight.js +23 -0
  210. package/dist/indexer/search/db-search.js +89 -13
  211. package/dist/indexer/search/fts-query.js +51 -0
  212. package/dist/indexer/search/ranking-contributors.js +95 -9
  213. package/dist/indexer/search/ranking.js +79 -3
  214. package/dist/indexer/search/search-fields.js +6 -0
  215. package/dist/indexer/search/search-source.js +32 -21
  216. package/dist/indexer/search/semantic-status.js +4 -0
  217. package/dist/indexer/walk/matchers.js +48 -0
  218. package/dist/indexer/walk/walker.js +21 -13
  219. package/dist/integrations/agent/builders.js +41 -13
  220. package/dist/integrations/agent/config.js +20 -59
  221. package/dist/integrations/agent/detect.js +9 -0
  222. package/dist/integrations/agent/index.js +3 -19
  223. package/dist/integrations/agent/model-aliases.js +16 -2
  224. package/dist/integrations/agent/profiles.js +79 -6
  225. package/dist/integrations/agent/prompts.js +75 -9
  226. package/dist/integrations/agent/runner-dispatch.js +83 -0
  227. package/dist/integrations/agent/runner.js +13 -9
  228. package/dist/integrations/agent/spawn.js +206 -81
  229. package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
  230. package/dist/integrations/harnesses/aider/index.js +58 -0
  231. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  232. package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
  233. package/dist/integrations/harnesses/amazonq/index.js +59 -0
  234. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  235. package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
  236. package/dist/integrations/harnesses/claude/index.js +27 -23
  237. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  238. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  239. package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
  240. package/dist/integrations/harnesses/codex/index.js +63 -0
  241. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  242. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  243. package/dist/integrations/harnesses/copilot/index.js +60 -0
  244. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  245. package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
  246. package/dist/integrations/harnesses/gemini/index.js +60 -0
  247. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  248. package/dist/integrations/harnesses/index.js +28 -7
  249. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  250. package/dist/integrations/harnesses/opencode/index.js +17 -16
  251. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  252. package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
  253. package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
  254. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
  255. package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
  256. package/dist/integrations/harnesses/openhands/index.js +58 -0
  257. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  258. package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
  259. package/dist/integrations/harnesses/pi/index.js +58 -0
  260. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  261. package/dist/integrations/harnesses/types.js +8 -0
  262. package/dist/integrations/session-logs/index.js +40 -11
  263. package/dist/llm/call-ai.js +2 -2
  264. package/dist/llm/client.js +34 -11
  265. package/dist/llm/embedder.js +67 -4
  266. package/dist/llm/embedders/cache.js +3 -1
  267. package/dist/llm/embedders/deterministic.js +66 -0
  268. package/dist/llm/embedders/local.js +73 -3
  269. package/dist/llm/feature-gate.js +16 -15
  270. package/dist/llm/graph-extract.js +67 -44
  271. package/dist/llm/memory-infer-impl.js +138 -0
  272. package/dist/llm/memory-infer.js +1 -127
  273. package/dist/llm/metadata-enhance.js +44 -31
  274. package/dist/llm/structured-call.js +49 -0
  275. package/dist/migrate-storage-node.mjs +8 -0
  276. package/dist/output/context.js +5 -5
  277. package/dist/output/renderers.js +87 -15
  278. package/dist/output/shapes/curate.js +14 -2
  279. package/dist/output/shapes/helpers.js +0 -3
  280. package/dist/output/shapes/passthrough.js +6 -1
  281. package/dist/output/text/helpers.js +241 -2
  282. package/dist/output/text/workflow.js +4 -1
  283. package/dist/registry/providers/skills-sh.js +21 -147
  284. package/dist/registry/providers/static-index.js +15 -157
  285. package/dist/registry/resolve.js +27 -9
  286. package/dist/runtime.js +25 -1
  287. package/dist/schemas/akm-config.json +14225 -0
  288. package/dist/schemas/akm-workflow.json +328 -0
  289. package/dist/scripts/migrate-storage.js +2743 -8390
  290. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
  291. package/dist/setup/detect.js +9 -0
  292. package/dist/setup/legacy-config.js +106 -0
  293. package/dist/setup/prompt.js +57 -0
  294. package/dist/setup/providers.js +14 -0
  295. package/dist/setup/registry-stash-loader.js +12 -0
  296. package/dist/setup/semantic-assets.js +124 -0
  297. package/dist/setup/setup.js +52 -1614
  298. package/dist/setup/steps/connection.js +734 -0
  299. package/dist/setup/steps/output.js +31 -0
  300. package/dist/setup/steps/platforms.js +124 -0
  301. package/dist/setup/steps/semantic.js +27 -0
  302. package/dist/setup/steps/sources.js +222 -0
  303. package/dist/setup/steps/stashdir.js +42 -0
  304. package/dist/setup/steps/tasks.js +152 -0
  305. package/dist/sources/include.js +6 -2
  306. package/dist/sources/providers/filesystem.js +0 -1
  307. package/dist/sources/providers/git-install.js +210 -0
  308. package/dist/sources/providers/git-provider.js +234 -0
  309. package/dist/sources/providers/git-stash.js +248 -0
  310. package/dist/sources/providers/git.js +10 -661
  311. package/dist/sources/providers/npm.js +2 -6
  312. package/dist/sources/providers/provider-utils.js +13 -7
  313. package/dist/sources/providers/sync-from-ref.js +9 -1
  314. package/dist/sources/providers/website.js +9 -5
  315. package/dist/sources/website-ingest.js +187 -29
  316. package/dist/sources/wiki-fetchers/registry.js +53 -0
  317. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  318. package/dist/storage/database.js +45 -10
  319. package/dist/storage/managed-db.js +82 -0
  320. package/dist/storage/repositories/canaries-repository.js +107 -0
  321. package/dist/storage/repositories/consolidation-repository.js +38 -0
  322. package/dist/storage/repositories/embeddings-repository.js +72 -0
  323. package/dist/storage/repositories/events-repository.js +187 -0
  324. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  325. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  326. package/dist/storage/repositories/index-db.js +14 -8
  327. package/dist/storage/repositories/proposals-repository.js +220 -0
  328. package/dist/storage/repositories/recombine-repository.js +213 -0
  329. package/dist/storage/repositories/registry-cache.js +93 -0
  330. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  331. package/dist/storage/repositories/task-history-repository.js +93 -0
  332. package/dist/storage/repositories/workflow-runs-repository.js +189 -1
  333. package/dist/storage/sqlite-pragmas.js +146 -0
  334. package/dist/tasks/backends/cron.js +1 -1
  335. package/dist/tasks/backends/index.js +9 -0
  336. package/dist/tasks/backends/launchd.js +1 -1
  337. package/dist/tasks/backends/schtasks.js +1 -1
  338. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  339. package/dist/tasks/runner.js +15 -13
  340. package/dist/text-import-hook.mjs +1 -1
  341. package/dist/wiki/wiki.js +52 -11
  342. package/dist/workflows/authoring/authoring.js +123 -10
  343. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  344. package/dist/workflows/cli.js +5 -0
  345. package/dist/workflows/db.js +138 -4
  346. package/dist/workflows/exec/brief.js +484 -0
  347. package/dist/workflows/exec/native-executor.js +975 -0
  348. package/dist/workflows/exec/param-secrets.js +115 -0
  349. package/dist/workflows/exec/report.js +1295 -0
  350. package/dist/workflows/exec/run-workflow.js +596 -0
  351. package/dist/workflows/exec/scheduler.js +100 -0
  352. package/dist/workflows/exec/step-work.js +1156 -0
  353. package/dist/workflows/exec/unit-writer.js +23 -0
  354. package/dist/workflows/exec/watch.js +116 -0
  355. package/dist/workflows/exec/worktree.js +171 -0
  356. package/dist/workflows/ir/compile.js +388 -0
  357. package/dist/workflows/ir/params.js +54 -0
  358. package/dist/workflows/ir/plan-hash.js +33 -0
  359. package/dist/workflows/ir/schema.js +4 -0
  360. package/dist/workflows/parser.js +3 -1
  361. package/dist/workflows/program/expressions.js +369 -0
  362. package/dist/workflows/program/parser.js +760 -0
  363. package/dist/workflows/program/project.js +105 -0
  364. package/dist/workflows/program/schema.js +54 -0
  365. package/dist/workflows/renderer.js +82 -5
  366. package/dist/workflows/runtime/agent-identity.js +59 -14
  367. package/dist/workflows/runtime/runs.js +248 -153
  368. package/dist/workflows/runtime/unit-checkin.js +45 -0
  369. package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
  370. package/dist/workflows/validate-summary.js +26 -10
  371. package/dist/workflows/validator.js +1 -1
  372. package/docs/README.md +69 -18
  373. package/docs/data-and-telemetry.md +7 -5
  374. package/docs/migration/release-notes/0.7.0.md +1 -1
  375. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  376. package/docs/migration/release-notes/0.9.0.md +39 -0
  377. package/package.json +10 -10
  378. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  379. package/dist/commands/db-cli.js +0 -23
  380. package/dist/indexer/db/db-backup.js +0 -376
  381. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -0,0 +1,210 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { spawnSync } from "node:child_process";
5
+ import { randomBytes } from "node:crypto";
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { UsageError } from "../../core/errors.js";
9
+ import { getRegistryCacheDir } from "../../core/paths.js";
10
+ import { parseRegistryRef, resolveRegistryArtifact, validateGitRef, validateGitUrl } from "../../registry/resolve.js";
11
+ import { applyAkmIncludeConfig, buildInstallCacheDir, copyDirectoryContents, detectStashRoot, isDirectory, } from "./provider-utils.js";
12
+ /**
13
+ * Shared subprocess wrapper for `git` invocations. Disables git's interactive
14
+ * terminal prompt so a missing credential never hangs the process.
15
+ */
16
+ export function runGit(args, options) {
17
+ return spawnSync("git", args, {
18
+ encoding: "utf8",
19
+ ...options,
20
+ env: { ...process.env, ...options?.env, GIT_TERMINAL_PROMPT: "0" },
21
+ });
22
+ }
23
+ /**
24
+ * Sync mode for a one-shot install ref (`akm add github:owner/repo` or
25
+ * `akm add git:url`). Runs the clone → strip → include-filter pipeline that
26
+ * historically lived in `installRegistryRef()`.
27
+ */
28
+ export async function syncRegistryGitRef(ref, options) {
29
+ const parsed = parseRegistryRef(ref);
30
+ if (parsed.source === "github") {
31
+ const githubRef = {
32
+ source: "git",
33
+ ref: parsed.ref,
34
+ id: parsed.id,
35
+ url: `https://github.com/${parsed.owner}/${parsed.repo}.git`,
36
+ requestedRef: parsed.requestedRef,
37
+ };
38
+ const result = await doSyncGit(githubRef, options);
39
+ return { ...result, source: "github" };
40
+ }
41
+ if (parsed.source !== "git") {
42
+ throw new UsageError(`syncRegistryGitRef requires a git: or github: ref, got "${ref}"`);
43
+ }
44
+ return doSyncGit(parsed, options);
45
+ }
46
+ async function doSyncGit(parsed, options) {
47
+ const resolved = await resolveRegistryArtifact(parsed);
48
+ const syncedAt = (options?.now ?? new Date()).toISOString();
49
+ const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheDir();
50
+ const cacheDir = buildInstallCacheDir(cacheRootDir, parsed.source, parsed.id, resolved.resolvedRevision);
51
+ const cloneDir = path.join(cacheDir, "clone");
52
+ const extractedDir = path.join(cacheDir, "extracted");
53
+ // Cache hit
54
+ if (!options?.force && isDirectory(extractedDir)) {
55
+ try {
56
+ const provisionalKitRoot = detectStashRoot(extractedDir);
57
+ const installRoot = applyAkmIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
58
+ const stashRoot = detectStashRoot(installRoot);
59
+ if (stashRoot) {
60
+ return {
61
+ id: resolved.id,
62
+ source: resolved.source,
63
+ ref: resolved.ref,
64
+ artifactUrl: resolved.artifactUrl,
65
+ resolvedVersion: resolved.resolvedVersion,
66
+ resolvedRevision: resolved.resolvedRevision,
67
+ contentDir: stashRoot,
68
+ cacheDir,
69
+ extractedDir,
70
+ writable: options?.writable,
71
+ syncedAt,
72
+ };
73
+ }
74
+ }
75
+ catch {
76
+ // Cache invalid, re-clone
77
+ }
78
+ }
79
+ fs.mkdirSync(cacheDir, { recursive: true });
80
+ // Validate URL and ref before passing to git to prevent command injection
81
+ validateGitUrl(parsed.url);
82
+ if (parsed.requestedRef)
83
+ validateGitRef(parsed.requestedRef);
84
+ let provisionalKitRoot;
85
+ let installRoot;
86
+ let stashRoot;
87
+ try {
88
+ const cloneArgs = ["clone", "--depth", "1"];
89
+ if (parsed.requestedRef) {
90
+ cloneArgs.push("--branch", parsed.requestedRef);
91
+ }
92
+ cloneArgs.push(parsed.url, cloneDir);
93
+ const cloneResult = runGit(cloneArgs, { timeout: 120_000 });
94
+ if (cloneResult.status !== 0) {
95
+ throw new Error(classifyCloneFailure(parsed.url, cloneResult.stderr, cloneResult.error));
96
+ }
97
+ // Copy contents to extracted dir without .git
98
+ fs.mkdirSync(extractedDir, { recursive: true });
99
+ copyDirectoryContents(cloneDir, extractedDir);
100
+ // Clean up the clone dir
101
+ fs.rmSync(cloneDir, { recursive: true, force: true });
102
+ provisionalKitRoot = detectStashRoot(extractedDir);
103
+ installRoot = applyAkmIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
104
+ stashRoot = detectStashRoot(installRoot);
105
+ }
106
+ catch (err) {
107
+ // Clean up the cache directory so stale or partially-cloned artifacts
108
+ // don't cause false cache hits on the next install attempt.
109
+ try {
110
+ fs.rmSync(cacheDir, { recursive: true, force: true });
111
+ }
112
+ catch {
113
+ /* best-effort */
114
+ }
115
+ throw err;
116
+ }
117
+ return {
118
+ id: resolved.id,
119
+ source: resolved.source,
120
+ ref: resolved.ref,
121
+ artifactUrl: resolved.artifactUrl,
122
+ resolvedVersion: resolved.resolvedVersion,
123
+ resolvedRevision: resolved.resolvedRevision,
124
+ contentDir: stashRoot,
125
+ cacheDir,
126
+ extractedDir,
127
+ writable: options?.writable,
128
+ syncedAt,
129
+ };
130
+ }
131
+ export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
132
+ // Stage the clone into a sibling temp dir so that a failed clone never
133
+ // destroys a previously-valid destDir (e.g. when the remote is temporarily
134
+ // unreachable and we have a valid cached copy).
135
+ const tmpDir = `${destDir}.tmp-${randomBytes(4).toString("hex")}`;
136
+ const args = ["clone", "--depth", "1"];
137
+ if (ref)
138
+ args.push("--branch", ref);
139
+ args.push(cloneUrl, tmpDir);
140
+ const result = runGit(args, { timeout: 120_000 });
141
+ if (result.status !== 0) {
142
+ // Clean up the (possibly partial) temp dir but leave destDir untouched.
143
+ fs.rmSync(tmpDir, { recursive: true, force: true });
144
+ throw new Error(classifyCloneFailure(cloneUrl, result.stderr, result.error));
145
+ }
146
+ try {
147
+ if (!writable) {
148
+ // Remove .git directory — we only need the working tree for read-only stashes
149
+ const gitDir = path.join(tmpDir, ".git");
150
+ if (fs.existsSync(gitDir))
151
+ fs.rmSync(gitDir, { recursive: true, force: true });
152
+ }
153
+ // Swap: remove the old destDir (if any) then atomically rename tmpDir into place.
154
+ if (fs.existsSync(destDir))
155
+ fs.rmSync(destDir, { recursive: true, force: true });
156
+ fs.renameSync(tmpDir, destDir);
157
+ }
158
+ catch (err) {
159
+ // Post-clone steps failed — clean up the temp dir to avoid orphaned dirs.
160
+ fs.rmSync(tmpDir, { recursive: true, force: true });
161
+ throw err;
162
+ }
163
+ }
164
+ // ── Clone-failure classification (#487) ─────────────────────────────────────
165
+ /**
166
+ * Translate git's stderr into an actionable message. Without this, a user
167
+ * who passes a nonexistent or private repo to `akm add` sees:
168
+ *
169
+ * "could not read Username for 'https://github.com': No such device or
170
+ * address"
171
+ *
172
+ * That is git falling through to its auth-prompt path — the actual cause
173
+ * is "repo doesn't exist (or is private)". We classify the common patterns
174
+ * and emit a message that names the cause and the fix.
175
+ */
176
+ export function classifyCloneFailure(url, stderr, spawnError) {
177
+ const safeUrl = redactUrlUserinfo(url);
178
+ const raw = (stderr ?? "").trim();
179
+ const spawnMsg = spawnError?.message ?? "";
180
+ // `git` binary not on PATH.
181
+ if (spawnError?.code === "ENOENT") {
182
+ return `Failed to clone ${safeUrl}: 'git' is not installed or not on PATH. Install git, then re-run.`;
183
+ }
184
+ // Auth-prompt fall-through (the headline #487 case).
185
+ if (/could not read Username|terminal prompts disabled|Authentication failed|fatal: Authentication/i.test(raw)) {
186
+ return (`Failed to clone ${safeUrl}: repository not found or private. ` +
187
+ `If the repository is public, double-check the URL and try again. ` +
188
+ `If it is private, set GH_TOKEN (or configure a git credential helper) before re-running.`);
189
+ }
190
+ // 404-style messages from git http.
191
+ if (/repository '.*' not found|HTTP 404|fatal: remote error|not found:|Not Found/i.test(raw)) {
192
+ return (`Failed to clone ${safeUrl}: repository not found. ` +
193
+ `Check the URL — for GitHub, the form is 'owner/repo' or 'github:owner/repo'.`);
194
+ }
195
+ // SSH connection issues.
196
+ if (/Permission denied \(publickey\)|kex_exchange_identification|Connection refused|Connection timed out/i.test(raw)) {
197
+ return (`Failed to clone ${safeUrl}: network or SSH failure. ` +
198
+ `Check connectivity, your SSH agent, and the remote host's availability.`);
199
+ }
200
+ // Branch / ref-specific failures.
201
+ if (/Remote branch .* not found in upstream origin|couldn't find remote ref/i.test(raw)) {
202
+ return (`Failed to clone ${safeUrl}: the requested branch/tag does not exist on the remote. ` +
203
+ `Verify the ref name and re-run.`);
204
+ }
205
+ const detail = raw || spawnMsg || "unknown error";
206
+ return `Failed to clone ${safeUrl}: ${redactUrlUserinfo(detail)}`;
207
+ }
208
+ function redactUrlUserinfo(text) {
209
+ return text.replace(/\b([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^\s/@]+)@/g, "$1[REDACTED]@");
210
+ }
@@ -0,0 +1,234 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { createHash } from "node:crypto";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import { TYPE_DIRS } from "../../core/asset/asset-spec.js";
8
+ import { ConfigError, UsageError } from "../../core/errors.js";
9
+ import { getRegistryIndexCacheDir } from "../../core/paths.js";
10
+ import { validateGitUrl } from "../../registry/resolve.js";
11
+ import { registerSourceProvider } from "../provider-factory.js";
12
+ import { cloneRepo, runGit, syncRegistryGitRef } from "./git-install.js";
13
+ import { isExpired, sanitizeString } from "./provider-utils.js";
14
+ /** Cache TTL before refreshing the mirrored repo (12 hours). */
15
+ const CACHE_TTL_MS = 12 * 60 * 60 * 1000;
16
+ /** Maximum stale age allowed when refresh fails (7 days). */
17
+ const CACHE_STALE_MS = 7 * 24 * 60 * 60 * 1000;
18
+ /**
19
+ * Git source provider — clones (and re-pulls) a remote repo into a local
20
+ * cache directory. Implements the v1 {@link SourceProvider} interface (spec
21
+ * §2.1, §2.5): `{ name, kind, init, path, sync }`.
22
+ *
23
+ * Reading is the indexer's job — this class doesn't implement `search` or
24
+ * `show`. The install-time helpers `syncRegistryGitRef` / `syncMirroredRepo`
25
+ * live below as standalone functions used by `akm add` / `akm update`.
26
+ */
27
+ export class GitSourceProvider {
28
+ kind = "git";
29
+ name;
30
+ #config;
31
+ #path = null;
32
+ constructor(config) {
33
+ this.#config = config;
34
+ this.name = config.name ?? "git";
35
+ }
36
+ path() {
37
+ if (this.#path == null) {
38
+ // Lazy resolution: providers are sometimes constructed without an
39
+ // explicit init() call (e.g. by legacy callers that just want the
40
+ // path). Resolve on demand and cache.
41
+ this.#path = resolveGitContentDir(this.#config);
42
+ }
43
+ return this.#path;
44
+ }
45
+ async sync(options) {
46
+ // Two execution modes:
47
+ // 1. Long-lived configured source (config.url) — mirror into the
48
+ // registry-index cache and serve as a read-only working tree.
49
+ // 2. One-shot install ref (options.ref like "git:..." / "github:...") —
50
+ // delegate to the install-time pipeline.
51
+ if (typeof this.#config.options?.ref === "string" && this.#config.options.ref) {
52
+ await syncRegistryGitRef(String(this.#config.options.ref), { force: options?.force });
53
+ return;
54
+ }
55
+ await syncMirroredRepo(this.#config, { force: options?.force });
56
+ }
57
+ }
58
+ /** Resolve the on-disk content directory for a configured git source. */
59
+ function resolveGitContentDir(config) {
60
+ if (config.path)
61
+ return config.path;
62
+ if (config.url) {
63
+ const repo = parseGitRepoUrl(config.url);
64
+ return getCachePaths(repo.canonicalUrl).repoDir;
65
+ }
66
+ throw new ConfigError("git source entry must have either `path` or `url`");
67
+ }
68
+ // ── Self-register ───────────────────────────────────────────────────────────
69
+ registerSourceProvider("git", (config) => new GitSourceProvider(config));
70
+ // ── Cache management ────────────────────────────────────────────────────────
71
+ export function getCachePaths(repoUrl) {
72
+ const key = createHash("sha256").update(repoUrl).digest("hex").slice(0, 16);
73
+ const cacheRoot = getRegistryIndexCacheDir();
74
+ const rootDir = path.join(cacheRoot, `git-${key}`);
75
+ return {
76
+ rootDir,
77
+ repoDir: path.join(rootDir, "repo"),
78
+ indexPath: path.join(rootDir, "index.json"),
79
+ };
80
+ }
81
+ export async function ensureGitMirror(repo, cachePaths, options) {
82
+ const requireRepoDir = options?.requireRepoDir === true;
83
+ const writable = options?.writable === true;
84
+ const force = options?.force === true;
85
+ // Check if cache is fresh
86
+ let mtime = 0;
87
+ try {
88
+ mtime = fs.statSync(cachePaths.indexPath).mtimeMs;
89
+ }
90
+ catch {
91
+ /* no cached index */
92
+ }
93
+ if (!force && mtime && !isExpired(mtime, CACHE_TTL_MS) && (!requireRepoDir || hasExtractedRepo(cachePaths.repoDir))) {
94
+ return;
95
+ }
96
+ try {
97
+ fs.mkdirSync(cachePaths.rootDir, { recursive: true });
98
+ if (writable && fs.existsSync(path.join(cachePaths.repoDir, ".git"))) {
99
+ // Writable repo already cloned — pull instead of re-clone to preserve local changes
100
+ pullRepo(cachePaths.repoDir);
101
+ }
102
+ else {
103
+ cloneRepo(repo.cloneUrl, repo.ref, cachePaths.repoDir, writable);
104
+ }
105
+ // Touch index file to track freshness
106
+ fs.writeFileSync(cachePaths.indexPath, "[]", { encoding: "utf8", mode: 0o600 });
107
+ }
108
+ catch (err) {
109
+ if (mtime && !isExpired(mtime, CACHE_STALE_MS) && (!requireRepoDir || hasExtractedRepo(cachePaths.repoDir))) {
110
+ return;
111
+ }
112
+ throw err;
113
+ }
114
+ }
115
+ /**
116
+ * Sync mode for a long-lived configured git stash. Mirrors the repo into the
117
+ * shared registry-index cache (12h TTL) and exposes the working tree as the
118
+ * stash content directory.
119
+ */
120
+ export async function syncMirroredRepo(config, options) {
121
+ if (!config.url) {
122
+ throw new ConfigError("git stash entry requires a URL when no install ref is supplied");
123
+ }
124
+ const repo = parseGitRepoUrl(config.url);
125
+ const cachePaths = getCachePaths(repo.canonicalUrl);
126
+ await ensureGitMirror(repo, cachePaths, {
127
+ requireRepoDir: true,
128
+ writable: options?.writable ?? config.writable === true,
129
+ force: options?.force,
130
+ });
131
+ const syncedAt = (options?.now ?? new Date()).toISOString();
132
+ const contentDir = cachePaths.repoDir;
133
+ return {
134
+ id: repo.canonicalUrl,
135
+ source: "git",
136
+ ref: repo.canonicalUrl,
137
+ artifactUrl: repo.canonicalUrl,
138
+ contentDir,
139
+ cacheDir: cachePaths.rootDir,
140
+ extractedDir: contentDir,
141
+ writable: options?.writable ?? config.writable === true,
142
+ syncedAt,
143
+ };
144
+ }
145
+ function pullRepo(repoDir) {
146
+ const result = runGit(["-C", repoDir, "pull", "--ff-only"], {
147
+ timeout: 120_000,
148
+ });
149
+ if (result.status !== 0) {
150
+ const err = result.stderr?.trim() || result.error?.message || "unknown error";
151
+ throw new Error(`Failed to pull ${repoDir}: ${err}`);
152
+ }
153
+ }
154
+ function hasExtractedRepo(repoDir) {
155
+ try {
156
+ if (!fs.statSync(repoDir).isDirectory())
157
+ return false;
158
+ if (fs.statSync(path.join(repoDir, "content")).isDirectory())
159
+ return true;
160
+ }
161
+ catch {
162
+ /* fall through to root-layout detection */
163
+ }
164
+ try {
165
+ if (!fs.statSync(repoDir).isDirectory())
166
+ return false;
167
+ return Object.values(TYPE_DIRS).some((dirName) => fs.existsSync(path.join(repoDir, dirName)));
168
+ }
169
+ catch {
170
+ return false;
171
+ }
172
+ }
173
+ export function parseGitRepoUrl(rawUrl) {
174
+ if (!rawUrl) {
175
+ throw new ConfigError("Git provider requires a repository URL");
176
+ }
177
+ // SSH shorthand: git@host:path — valid as-is, delegated to system git credentials
178
+ if (/^git@[^:]+:.+$/.test(rawUrl)) {
179
+ return { cloneUrl: rawUrl, ref: null, canonicalUrl: rawUrl };
180
+ }
181
+ // Validate URL scheme is safe before parsing
182
+ try {
183
+ validateGitUrl(rawUrl);
184
+ }
185
+ catch (err) {
186
+ if (err instanceof UsageError)
187
+ throw new ConfigError(err.message);
188
+ throw err;
189
+ }
190
+ let parsed;
191
+ try {
192
+ parsed = new URL(rawUrl);
193
+ }
194
+ catch {
195
+ throw new ConfigError(`Git provider URL is not valid: "${rawUrl}"`);
196
+ }
197
+ // GitHub web URLs: extract a clean clone URL and optional branch from /tree/<ref>
198
+ if (parsed.hostname === "github.com" && parsed.protocol === "https:") {
199
+ const segments = parsed.pathname.split("/").filter(Boolean);
200
+ if (segments.length < 2) {
201
+ throw new ConfigError(`Git provider URL must point to a repository, got "${rawUrl}"`);
202
+ }
203
+ const owner = sanitizeString(segments[0]);
204
+ const repo = sanitizeString(segments[1].replace(/\.git$/i, ""));
205
+ if (!owner || !repo || !/^[A-Za-z0-9_.-]+$/.test(owner) || !/^[A-Za-z0-9_.-]+$/.test(repo)) {
206
+ throw new ConfigError(`Unsupported repository URL: "${rawUrl}"`);
207
+ }
208
+ let ref = null;
209
+ if (segments[2] === "tree" && segments.length >= 4) {
210
+ const rawRef = sanitizeString(segments.slice(3).join("/"), 255);
211
+ if (rawRef && !rawRef.includes("..") && /^[A-Za-z0-9._/-]+$/.test(rawRef)) {
212
+ ref = rawRef;
213
+ }
214
+ }
215
+ const cloneUrl = `https://github.com/${owner}/${repo}`;
216
+ const canonicalUrl = ref ? `${cloneUrl}/tree/${ref}` : cloneUrl;
217
+ return { cloneUrl, ref, canonicalUrl };
218
+ }
219
+ // Any other valid git URL: use as-is for cloning, but strip embedded credentials
220
+ // from canonicalUrl so secrets don't leak into cache keys or warning messages.
221
+ let canonicalUrl = rawUrl;
222
+ try {
223
+ const u = new URL(rawUrl);
224
+ u.username = "";
225
+ u.password = "";
226
+ u.search = "";
227
+ u.hash = "";
228
+ canonicalUrl = u.toString();
229
+ }
230
+ catch {
231
+ // URL failed to parse — fall back to raw (validateGitUrl already accepted it)
232
+ }
233
+ return { cloneUrl: rawUrl, ref: null, canonicalUrl };
234
+ }