akm-cli 0.9.0-rc.9 → 0.9.1-beta.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 (477) hide show
  1. package/CHANGELOG.md +1770 -44
  2. package/README.md +38 -47
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +499 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +74 -89
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +148 -88
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/llm-usage.js +2 -1
  57. package/dist/commands/health/md-report.js +5 -9
  58. package/dist/commands/health/metrics.js +62 -20
  59. package/dist/commands/health/renderers.js +47 -0
  60. package/dist/commands/health/report-view-model.js +4 -5
  61. package/dist/commands/health/stash-exposure.js +1 -1
  62. package/dist/commands/health/surfaces.js +4 -122
  63. package/dist/commands/health/task-runs.js +3 -67
  64. package/dist/commands/health/types-improve.js +7 -0
  65. package/dist/commands/health.js +160 -35
  66. package/dist/commands/improve/anti-collapse.js +2 -2
  67. package/dist/commands/improve/autonomy-gate.js +68 -0
  68. package/dist/commands/improve/collapse-detector.js +41 -40
  69. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  70. package/dist/commands/improve/consolidate/merge.js +4 -0
  71. package/dist/commands/improve/consolidate.js +140 -1000
  72. package/dist/commands/improve/distill/promote-memory.js +12 -12
  73. package/dist/commands/improve/distill/quality-gate.js +11 -6
  74. package/dist/commands/improve/distill.js +58 -69
  75. package/dist/commands/improve/eligibility.js +101 -55
  76. package/dist/commands/improve/extract-cli.js +14 -133
  77. package/dist/commands/improve/improve-cli.js +98 -114
  78. package/dist/commands/improve/improve-result-file.js +1 -28
  79. package/dist/commands/improve/improve-strategies.js +8 -5
  80. package/dist/commands/improve/improve.js +254 -101
  81. package/dist/commands/improve/locks.js +7 -0
  82. package/dist/commands/improve/loop-stages.js +182 -20
  83. package/dist/commands/improve/memory/derived-ref.js +45 -43
  84. package/dist/commands/improve/memory/memory-belief.js +1 -1
  85. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  86. package/dist/commands/improve/memory/memory-improve.js +14 -5
  87. package/dist/commands/improve/outcome-loop.js +22 -65
  88. package/dist/commands/improve/preparation.js +114 -123
  89. package/dist/commands/improve/proactive-maintenance.js +2 -5
  90. package/dist/commands/improve/reflect.js +56 -160
  91. package/dist/commands/improve/run-context.js +5 -0
  92. package/dist/commands/improve/salience.js +11 -122
  93. package/dist/commands/improve/session-asset.js +4 -0
  94. package/dist/commands/improve/source-identity.js +10 -38
  95. package/dist/commands/lint/base-linter.js +51 -131
  96. package/dist/commands/lint/env-key-rules.js +31 -47
  97. package/dist/commands/lint/index.js +444 -88
  98. package/dist/commands/lint/types.js +22 -1
  99. package/dist/commands/{events.js → log.js} +33 -38
  100. package/dist/commands/migrate-cli.js +92 -12
  101. package/dist/commands/migration-tool.js +46 -0
  102. package/dist/commands/observability-cli.js +70 -209
  103. package/dist/commands/proposal/drain.js +101 -29
  104. package/dist/commands/proposal/proposal-cli.js +76 -48
  105. package/dist/commands/proposal/proposal.js +54 -18
  106. package/dist/commands/proposal/propose-cli.js +88 -0
  107. package/dist/commands/proposal/propose.js +23 -15
  108. package/dist/commands/proposal/repository.js +718 -279
  109. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  110. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  111. package/dist/commands/proposal/validators/proposals.js +4 -7
  112. package/dist/commands/read/curate.js +34 -53
  113. package/dist/commands/read/knowledge.js +150 -95
  114. package/dist/commands/read/registry-search.js +2 -2
  115. package/dist/commands/read/remember-cli.js +42 -15
  116. package/dist/commands/read/search-cli.js +180 -78
  117. package/dist/commands/read/search.js +58 -43
  118. package/dist/commands/read/show.js +197 -141
  119. package/dist/commands/registry-cli.js +12 -51
  120. package/dist/commands/remember.js +14 -57
  121. package/dist/commands/sources/add-cli.js +100 -31
  122. package/dist/commands/sources/bundle-cli.js +166 -0
  123. package/dist/commands/sources/bundle-config-ops.js +7 -2
  124. package/dist/commands/sources/info.js +30 -7
  125. package/dist/commands/sources/init.js +12 -12
  126. package/dist/commands/sources/installed-stashes.js +387 -98
  127. package/dist/commands/sources/schema-repair.js +3 -2
  128. package/dist/commands/sources/self-update.js +131 -38
  129. package/dist/commands/sources/source-add.js +72 -17
  130. package/dist/commands/sources/source-clone.js +129 -45
  131. package/dist/commands/sources/source-manage.js +43 -23
  132. package/dist/commands/sources/sources-cli.js +57 -208
  133. package/dist/commands/sources/stash-cli.js +46 -53
  134. package/dist/commands/tasks/tasks-cli.js +91 -97
  135. package/dist/commands/tasks/tasks.js +284 -423
  136. package/dist/commands/workflow-cli.js +181 -450
  137. package/dist/core/abort-deadline.js +28 -0
  138. package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
  139. package/dist/core/adapter/adapters/akm-adapter.js +60 -38
  140. package/dist/core/adapter/adapters/akm-lint.js +109 -42
  141. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  142. package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
  143. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  144. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  145. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  146. package/dist/core/adapter/adapters/index.js +6 -6
  147. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  148. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  149. package/dist/core/adapter/adapters/shared.js +3 -19
  150. package/dist/core/adapter/adapters/tool-dir-shared.js +13 -6
  151. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  152. package/dist/core/adapter/detect-adapter.js +17 -0
  153. package/dist/core/adapter/recognize-match.js +6 -4
  154. package/dist/core/adapter/validate-context.js +214 -0
  155. package/dist/core/asset/akm-markdown.js +63 -0
  156. package/dist/core/asset/asset-placement.js +20 -6
  157. package/dist/core/asset/asset-ref.js +11 -9
  158. package/dist/core/asset/frontmatter-lint.js +30 -0
  159. package/dist/core/asset/frontmatter.js +29 -9
  160. package/dist/core/asset/markdown.js +40 -51
  161. package/dist/core/asset/resolve-ref.js +89 -18
  162. package/dist/core/asset/stash-meta.js +1 -1
  163. package/dist/core/bundle-id.js +51 -0
  164. package/dist/core/common.js +218 -44
  165. package/dist/core/concurrent.js +32 -0
  166. package/dist/core/config/config-io.js +12 -1
  167. package/dist/core/config/config-schema.js +35 -8
  168. package/dist/core/config/config-sources.js +55 -11
  169. package/dist/core/config/config-walker.js +25 -9
  170. package/dist/core/config/config.js +9 -48
  171. package/dist/core/config/experimental.js +21 -0
  172. package/dist/core/config/schema/embedding.js +5 -1
  173. package/dist/core/config/schema/experimental.js +30 -0
  174. package/dist/core/config/schema/improve-processes.js +0 -6
  175. package/dist/core/config/schema/improve.js +21 -3
  176. package/dist/core/config/schema/index-config.js +8 -15
  177. package/dist/core/config/schema/output.js +4 -1
  178. package/dist/core/config/schema/setup.js +9 -18
  179. package/dist/core/config/schema/sources-bundles.js +49 -33
  180. package/dist/core/config/schema/workflow.js +14 -3
  181. package/dist/core/env-secret-ref.js +76 -46
  182. package/dist/core/errors.js +43 -12
  183. package/dist/core/events.js +76 -152
  184. package/dist/core/file-change.js +6 -5
  185. package/dist/core/file-lock.js +7 -1
  186. package/dist/core/fs-txn.js +83 -7
  187. package/dist/core/git-message.js +2 -2
  188. package/dist/core/improve-result.js +6 -100
  189. package/dist/core/json-schema.js +327 -9
  190. package/dist/core/lesson-lint.js +1 -17
  191. package/dist/core/logs-db.js +2 -1
  192. package/dist/core/loopback.js +89 -0
  193. package/dist/core/migration-operation.js +33 -2
  194. package/dist/core/mutation-target.js +78 -0
  195. package/dist/core/parse.js +4 -1
  196. package/dist/core/path-access.js +107 -0
  197. package/dist/core/paths.js +17 -20
  198. package/dist/core/recognition-util.js +12 -14
  199. package/dist/core/redaction.js +98 -17
  200. package/dist/core/spawn-env.js +234 -0
  201. package/dist/core/standards/resolve-standards-context.js +2 -14
  202. package/dist/core/standards/resolve-stash-standards.js +2 -2
  203. package/dist/core/standards/resolve-type-conventions.js +2 -2
  204. package/dist/core/state/migrations.js +41 -18
  205. package/dist/core/state-db-scope.js +134 -0
  206. package/dist/core/state-db.js +5 -14
  207. package/dist/core/structured.js +1 -1
  208. package/dist/core/subprocess.js +182 -36
  209. package/dist/core/text-truncation.js +9 -5
  210. package/dist/core/type-presentation.js +3 -3
  211. package/dist/core/warn.js +0 -3
  212. package/dist/core/write-provenance.js +85 -0
  213. package/dist/core/write-source.js +778 -95
  214. package/dist/indexer/bundle-identity-guard.js +3 -2
  215. package/dist/indexer/db/graph-db.js +17 -30
  216. package/dist/indexer/ensure-index.js +11 -3
  217. package/dist/indexer/graph/graph-boost.js +9 -34
  218. package/dist/indexer/graph/graph-extraction.js +8 -5
  219. package/dist/indexer/index-writer-lock.js +53 -17
  220. package/dist/indexer/index-written-assets.js +33 -24
  221. package/dist/indexer/indexer.js +551 -253
  222. package/dist/indexer/installations.js +14 -96
  223. package/dist/indexer/passes/dir-staleness.js +16 -9
  224. package/dist/indexer/passes/memory-inference.js +15 -9
  225. package/dist/indexer/passes/metadata.js +113 -47
  226. package/dist/indexer/scan/doc-to-entry.js +38 -1
  227. package/dist/indexer/scan/drain-dir.js +13 -23
  228. package/dist/indexer/search/db-search.js +107 -69
  229. package/dist/indexer/search/fts-query.js +47 -24
  230. package/dist/indexer/search/ranking-contributors.js +42 -20
  231. package/dist/indexer/search/ranking.js +18 -99
  232. package/dist/indexer/search/search-fields.js +7 -2
  233. package/dist/indexer/search/search-source.js +82 -93
  234. package/dist/indexer/usage/usage-events.js +0 -89
  235. package/dist/indexer/walk/file-context.js +2 -1
  236. package/dist/indexer/walk/matchers.js +30 -43
  237. package/dist/indexer/walk/path-resolver.js +7 -2
  238. package/dist/indexer/walk/walker.js +38 -12
  239. package/dist/integrations/agent/builders.js +0 -6
  240. package/dist/integrations/agent/config.js +2 -2
  241. package/dist/integrations/agent/detect.js +49 -19
  242. package/dist/integrations/agent/engine-fallback.js +76 -0
  243. package/dist/integrations/agent/engine-resolution.js +24 -11
  244. package/dist/integrations/agent/model-aliases.js +1 -1
  245. package/dist/integrations/agent/profiles.js +23 -1
  246. package/dist/integrations/agent/prompts.js +12 -8
  247. package/dist/integrations/agent/runner-dispatch.js +4 -2
  248. package/dist/integrations/agent/runner.js +0 -1
  249. package/dist/integrations/agent/spawn.js +20 -93
  250. package/dist/integrations/github.js +1 -1
  251. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  252. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  253. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  254. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  255. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  256. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  257. package/dist/integrations/harnesses/index.js +3 -7
  258. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  259. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  260. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  261. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  262. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  263. package/dist/integrations/lockfile.js +132 -8
  264. package/dist/integrations/session-logs/index.js +3 -28
  265. package/dist/llm/client.js +136 -100
  266. package/dist/llm/embedders/remote.js +13 -5
  267. package/dist/llm/feature-gate.js +4 -12
  268. package/dist/llm/graph-extract.js +5 -11
  269. package/dist/llm/memory-infer.js +144 -1
  270. package/dist/llm/metadata-enhance.js +5 -7
  271. package/dist/llm/structured-call.js +1 -1
  272. package/dist/llm/usage-persist.js +26 -5
  273. package/dist/llm/usage-telemetry.js +25 -2
  274. package/dist/output/cli-hints.js +1 -2
  275. package/dist/output/context.js +22 -7
  276. package/dist/output/format-exempt.js +80 -0
  277. package/dist/output/generic-render.js +259 -0
  278. package/dist/output/render-registry.js +57 -0
  279. package/dist/output/renderers.js +14 -36
  280. package/dist/output/shapes/curate.js +10 -1
  281. package/dist/output/shapes/events.js +12 -7
  282. package/dist/output/shapes/helpers.js +56 -83
  283. package/dist/output/shapes/migrate.js +8 -0
  284. package/dist/output/shapes/passthrough.js +7 -41
  285. package/dist/output/shapes/proposal/producer.js +15 -7
  286. package/dist/output/shapes.js +2 -9
  287. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  288. package/dist/output/text/bundle-show.js +7 -0
  289. package/dist/output/text/command-format.js +164 -96
  290. package/dist/output/text/env.js +1 -3
  291. package/dist/output/text/events.js +8 -7
  292. package/dist/output/text/health-format.js +103 -0
  293. package/dist/output/text/health.js +7 -0
  294. package/dist/output/text/helpers.js +10 -8
  295. package/dist/output/text/lint-format.js +56 -0
  296. package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
  297. package/dist/output/text/migrate.js +88 -0
  298. package/dist/output/text/proposal/producer.js +4 -2
  299. package/dist/output/text/proposal-format.js +44 -72
  300. package/dist/output/text/registry-commands.js +1 -2
  301. package/dist/output/text/show-directives.js +15 -7
  302. package/dist/output/text/status-list.js +32 -0
  303. package/dist/output/text/{save.js → sync.js} +2 -2
  304. package/dist/output/text/workflow-format.js +24 -203
  305. package/dist/output/text/workflow.js +1 -7
  306. package/dist/output/text.js +16 -17
  307. package/dist/registry/factory.js +4 -6
  308. package/dist/registry/origin-resolve.js +16 -27
  309. package/dist/registry/providers/skills-sh.js +3 -3
  310. package/dist/registry/providers/static-index.js +13 -23
  311. package/dist/registry/resolve.js +42 -7
  312. package/dist/registry/semver.js +34 -84
  313. package/dist/runtime.js +2 -23
  314. package/dist/scripts/akm-migrate-node.js +60972 -0
  315. package/dist/scripts/akm-migrate.js +60310 -0
  316. package/dist/setup/detect.js +42 -15
  317. package/dist/setup/registry-stash-loader.js +2 -2
  318. package/dist/setup/setup.js +236 -136
  319. package/dist/setup/steps/connection.js +7 -9
  320. package/dist/setup/steps/platforms.js +9 -9
  321. package/dist/setup/steps/semantic.js +15 -3
  322. package/dist/setup/steps/sources.js +12 -13
  323. package/dist/setup/steps/stashdir.js +2 -3
  324. package/dist/setup/steps/tasks.js +237 -120
  325. package/dist/sources/freshness.js +1 -1
  326. package/dist/sources/provider-factory.js +11 -17
  327. package/dist/sources/providers/filesystem.js +2 -3
  328. package/dist/sources/providers/git-install.js +278 -34
  329. package/dist/sources/providers/git-provider.js +25 -23
  330. package/dist/sources/providers/git-stash.js +414 -106
  331. package/dist/sources/providers/git.js +2 -2
  332. package/dist/sources/providers/npm.js +16 -19
  333. package/dist/sources/providers/provider-utils.js +7 -4
  334. package/dist/sources/providers/sync-from-ref.js +3 -9
  335. package/dist/sources/providers/website.js +6 -1
  336. package/dist/sources/resolve.js +6 -5
  337. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  338. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  339. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  340. package/dist/sources/snapshot-fetchers/github.js +100 -0
  341. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  342. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  343. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  344. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  345. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  346. package/dist/sources/snapshot-fetchers/website-ingest.js +686 -282
  347. package/dist/sources/snapshot-fetchers/x.js +910 -0
  348. package/dist/storage/database.js +13 -1
  349. package/dist/storage/engines/sqlite-migrations.js +23 -111
  350. package/dist/storage/managed-db.js +21 -2
  351. package/dist/storage/repositories/canaries-repository.js +1 -1
  352. package/dist/storage/repositories/events-repository.js +27 -11
  353. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  354. package/dist/storage/repositories/index-connection.js +44 -8
  355. package/dist/storage/repositories/index-entries-repository.js +157 -241
  356. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  357. package/dist/storage/repositories/index-fts-repository.js +5 -2
  358. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  359. package/dist/storage/repositories/index-meta-repository.js +2 -3
  360. package/dist/storage/repositories/index-schema.js +10 -25
  361. package/dist/storage/repositories/index-utility-repository.js +15 -28
  362. package/dist/storage/repositories/index-vec-repository.js +6 -1
  363. package/dist/storage/repositories/outcome-repository.js +119 -0
  364. package/dist/storage/repositories/proposals-repository.js +296 -59
  365. package/dist/storage/repositories/registry-cache.js +19 -0
  366. package/dist/storage/repositories/salience-repository.js +172 -0
  367. package/dist/storage/repositories/task-history-repository.js +15 -13
  368. package/dist/storage/repositories/workflow-runs-repository.js +118 -53
  369. package/dist/tasks/backends/cron.js +105 -15
  370. package/dist/tasks/backends/index.js +1 -1
  371. package/dist/tasks/backends/launchd.js +85 -38
  372. package/dist/tasks/backends/schtasks.js +135 -15
  373. package/dist/tasks/embedded.js +56 -40
  374. package/dist/tasks/log-redaction.js +156 -0
  375. package/dist/tasks/parser.js +87 -160
  376. package/dist/tasks/resolve-akm-bin.js +137 -59
  377. package/dist/tasks/runner.js +209 -44
  378. package/dist/tasks/scheduler-invocation.js +220 -10
  379. package/dist/tasks/schema.js +89 -1
  380. package/dist/tasks/task-id.js +1 -3
  381. package/dist/tasks/validator.js +20 -6
  382. package/dist/workflows/authoring/authoring.js +94 -143
  383. package/dist/workflows/authoring/scope-key.js +1 -1
  384. package/dist/workflows/concurrency-policy.js +95 -1
  385. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  386. package/dist/workflows/exec/exec-unit.js +542 -0
  387. package/dist/workflows/exec/frozen-judge.js +130 -32
  388. package/dist/workflows/exec/native-executor.js +513 -274
  389. package/dist/workflows/exec/param-secrets.js +12 -11
  390. package/dist/workflows/exec/run-workflow.js +496 -237
  391. package/dist/workflows/exec/step-work.js +616 -412
  392. package/dist/workflows/exec/unit-dispatch.js +34 -0
  393. package/dist/workflows/exec/unit-writer.js +53 -13
  394. package/dist/workflows/exec/worktree.js +454 -41
  395. package/dist/workflows/ir/compile.js +166 -271
  396. package/dist/workflows/ir/freeze.js +119 -42
  397. package/dist/workflows/ir/params.js +135 -11
  398. package/dist/workflows/ir/plan-hash.js +1 -1
  399. package/dist/workflows/ir/schema.js +129 -45
  400. package/dist/workflows/parser.js +1070 -307
  401. package/dist/workflows/program/expressions.js +20 -208
  402. package/dist/workflows/program/schema.js +31 -10
  403. package/dist/workflows/renderer.js +123 -68
  404. package/dist/workflows/resource-limits.js +184 -0
  405. package/dist/workflows/runtime/checkin.js +3 -3
  406. package/dist/workflows/runtime/plan-classifier.js +16 -75
  407. package/dist/workflows/runtime/runs.js +331 -132
  408. package/dist/workflows/runtime/unit-checkin.js +1 -1
  409. package/dist/workflows/runtime/unit-phases.js +2 -2
  410. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  411. package/dist/workflows/schema.js +1 -11
  412. package/dist/workflows/validate-summary.js +47 -38
  413. package/dist/workflows/validator.js +21 -62
  414. package/docs/README.md +110 -0
  415. package/docs/migration/README.md +8 -0
  416. package/docs/migration/release-notes/0.7.0.md +11 -11
  417. package/docs/migration/release-notes/0.9.0.md +209 -27
  418. package/docs/migration/v0.7-to-v0.8.md +48 -48
  419. package/docs/migration/v0.8-to-v0.9.md +567 -209
  420. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  421. package/docs/reference/README.md +19 -0
  422. package/docs/reference/bundle-types.md +19 -0
  423. package/docs/reference/cli.md +2342 -0
  424. package/docs/reference/configuration.md +371 -0
  425. package/docs/reference/data-and-telemetry.md +129 -46
  426. package/docs/reference/supported-formats.md +50 -0
  427. package/docs/reference/workflow-schema.md +1014 -0
  428. package/docs/reference/workflows.md +51 -0
  429. package/package.json +31 -13
  430. package/schemas/akm-asset-envelope.json +93 -0
  431. package/schemas/akm-config.json +89 -128
  432. package/schemas/akm-task.json +27 -5
  433. package/schemas/akm-workflow.json +157 -81
  434. package/dist/assets/tasks/core/backup.yml +0 -5
  435. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  436. package/dist/cli/config-migrate.js +0 -1878
  437. package/dist/cli/config-validate.js +0 -41
  438. package/dist/commands/backup-cli.js +0 -56
  439. package/dist/commands/bundle/bundle-cli.js +0 -68
  440. package/dist/commands/bundle/bundle.js +0 -219
  441. package/dist/commands/graph/graph-cli.js +0 -124
  442. package/dist/commands/graph/graph.js +0 -489
  443. package/dist/commands/improve/extract-watch.js +0 -140
  444. package/dist/commands/mv-cli.js +0 -1221
  445. package/dist/commands/sources/history.js +0 -201
  446. package/dist/commands/tasks/default-tasks.js +0 -186
  447. package/dist/core/migration-backup.js +0 -1231
  448. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  449. package/dist/llm/memory-infer-impl.js +0 -138
  450. package/dist/migrate/legacy/config-source-migration.js +0 -257
  451. package/dist/migrate/legacy/content-migration.js +0 -350
  452. package/dist/migrate/legacy/legacy-layout.js +0 -779
  453. package/dist/migrate/legacy/legacy-paths.js +0 -25
  454. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  455. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  456. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  457. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  458. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  459. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  460. package/dist/migrate/legacy-ref-grammar.js +0 -214
  461. package/dist/output/shapes/distill.js +0 -14
  462. package/dist/output/shapes/history.js +0 -11
  463. package/dist/output/text/distill.js +0 -6
  464. package/dist/output/text/enable-disable.js +0 -8
  465. package/dist/output/text/history.js +0 -6
  466. package/dist/registry/build-index.js +0 -382
  467. package/dist/schemas/akm-config.json +0 -4704
  468. package/dist/schemas/akm-task.json +0 -87
  469. package/dist/schemas/akm-workflow.json +0 -372
  470. package/dist/scripts/migrate-storage.js +0 -3816
  471. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  472. package/dist/workflows/cli.js +0 -53
  473. package/dist/workflows/exec/brief.js +0 -481
  474. package/dist/workflows/exec/report.js +0 -1460
  475. package/dist/workflows/exec/watch.js +0 -116
  476. package/dist/workflows/program/parser.js +0 -813
  477. package/dist/workflows/program/project.js +0 -104
@@ -0,0 +1,100 @@
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 { GITHUB_API_BASE, githubHeaders } from "../../integrations/github.js";
5
+ import { htmlToMarkdown } from "./content-extract.js";
6
+ import { avoidReservedBasename } from "./fetcher-util.js";
7
+ import { fetchPinnedText } from "./host-guard.js";
8
+ const GITHUB_HOSTS = new Set(["github.com", "www.github.com"]);
9
+ const RESERVED_GITHUB_ROOTS = new Set([
10
+ "apps",
11
+ "collections",
12
+ "codespaces",
13
+ "customer-stories",
14
+ "education",
15
+ "enterprise",
16
+ "events",
17
+ "explore",
18
+ "features",
19
+ "issues",
20
+ "login",
21
+ "marketplace",
22
+ "new",
23
+ "notifications",
24
+ "orgs",
25
+ "organizations",
26
+ "pricing",
27
+ "pulls",
28
+ "search",
29
+ "security",
30
+ "settings",
31
+ "solutions",
32
+ "sponsors",
33
+ "topics",
34
+ "trending",
35
+ "users",
36
+ ]);
37
+ const GITHUB_README_BYTE_CAP = 5 * 1024 * 1024;
38
+ const GITHUB_README_BODY_TIMEOUT_MS = 30_000;
39
+ /** Match only repository roots; deeper GitHub pages stay generic web pages. */
40
+ export function extractGithubRepository(url) {
41
+ if (!GITHUB_HOSTS.has(url.hostname.toLowerCase()))
42
+ return null;
43
+ const segments = url.pathname.split("/").filter(Boolean);
44
+ if (segments.length !== 2)
45
+ return null;
46
+ let owner;
47
+ let repo;
48
+ try {
49
+ owner = decodeURIComponent(segments[0] ?? "");
50
+ repo = decodeURIComponent(segments[1] ?? "").replace(/\.git$/i, "");
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/.test(owner))
56
+ return null;
57
+ if (RESERVED_GITHUB_ROOTS.has(owner.toLowerCase()))
58
+ return null;
59
+ if (!/^[A-Za-z0-9._-]{1,100}$/.test(repo) || repo === "." || repo === "..")
60
+ return null;
61
+ return { owner, repo };
62
+ }
63
+ const githubFetcher = {
64
+ name: "github-repository",
65
+ matches(url) {
66
+ return extractGithubRepository(url) !== null;
67
+ },
68
+ async fetch(url, context) {
69
+ const repository = extractGithubRepository(url);
70
+ if (!repository)
71
+ return null;
72
+ const apiUrl = `${GITHUB_API_BASE}/repos/${encodeURIComponent(repository.owner)}/${encodeURIComponent(repository.repo)}/readme`;
73
+ const headers = new Headers(githubHeaders(apiUrl));
74
+ headers.set("Accept", "application/vnd.github.html+json");
75
+ headers.set("User-Agent", "akm-cli github snapshot fetcher");
76
+ const html = await fetchPinnedText(apiUrl, {
77
+ headers,
78
+ byteCap: GITHUB_README_BYTE_CAP,
79
+ bodyTimeoutMs: GITHUB_README_BODY_TIMEOUT_MS,
80
+ timeoutMs: context.timeoutMs,
81
+ signal: context.signal,
82
+ });
83
+ if (!html)
84
+ return null;
85
+ const canonicalUrl = `https://github.com/${repository.owner}/${repository.repo}`;
86
+ const markdown = htmlToMarkdown(html, `${canonicalUrl}/`);
87
+ if (!markdown)
88
+ return null;
89
+ const repoName = repository.repo.startsWith(".") ? `dot-${repository.repo.slice(1)}` : repository.repo;
90
+ const preferredName = `${repository.owner.toLowerCase()}/${repoName.toLowerCase()}`;
91
+ return {
92
+ url: canonicalUrl,
93
+ title: `${repository.owner}/${repository.repo}`,
94
+ markdown,
95
+ preferredName: avoidReservedBasename(preferredName),
96
+ tags: ["github", "repository", repository.owner.toLowerCase()],
97
+ };
98
+ },
99
+ };
100
+ export default githubFetcher;
@@ -0,0 +1,291 @@
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 { lookup as dnsLookup } from "node:dns/promises";
5
+ import { isIP } from "node:net";
6
+ import { fetchWithRetry, readBodyWithByteCap } from "../../core/common.js";
7
+ /** Redirect budget for a single guarded request chain. */
8
+ const MAX_GUARDED_REDIRECTS = 8;
9
+ export function assertWebsiteRequestUrl(rawUrl, ErrorType = Error, options) {
10
+ const parsedUrl = new URL(rawUrl);
11
+ if (parsedUrl.username || parsedUrl.password) {
12
+ throw new ErrorType(`Refusing to fetch URL with embedded credentials: ${parsedUrl.hostname}`);
13
+ }
14
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
15
+ throw new ErrorType(`Refusing to fetch non-http(s) URL on host: ${parsedUrl.hostname}`);
16
+ }
17
+ const hostname = parsedUrl.hostname.toLowerCase();
18
+ if (hostname.endsWith(".invalid")) {
19
+ throw new ErrorType(`Refusing to fetch reserved invalid hostname: ${parsedUrl.hostname}`);
20
+ }
21
+ if (isForbiddenWebsiteHostname(hostname, options)) {
22
+ throw new ErrorType(`Refusing to fetch non-public website host: ${parsedUrl.hostname}`);
23
+ }
24
+ }
25
+ async function defaultResolveHostname(hostname) {
26
+ const records = await dnsLookup(hostname, { all: true });
27
+ return records.map((record) => record.address);
28
+ }
29
+ /**
30
+ * Resolve-then-validate SSRF guard against DNS rebinding / private-range
31
+ * bypasses. {@link assertWebsiteRequestUrl} only rejects IP-literal and
32
+ * well-known-name hosts; a hostname like `private-host.example.com` that
33
+ * resolves to `10.0.0.1` passes those checks and then fetch connects to the
34
+ * private address. Here we resolve EVERY A/AAAA record and validate each
35
+ * against the same forbidden-range rules, failing CLOSED on an empty answer or
36
+ * resolver error.
37
+ *
38
+ * TOCTOU residual (documented, not fully closable here): Bun's `fetch` exposes
39
+ * no custom `lookup`/agent hook, so we cannot pin the socket to the exact IP we
40
+ * validated — a hostile resolver could return a public IP to this lookup and a
41
+ * private IP microseconds later at connect time (classic rebinding). This still
42
+ * removes the TRIVIAL `hostname A 10.0.0.1` bypass, which is the strongest
43
+ * guarantee available without a pinned-connection fetch API. Re-run on every
44
+ * redirect hop (the crawler recurses through `fetchWebsiteResponse`).
45
+ */
46
+ export async function assertResolvedHostAllowed(hostname, options) {
47
+ if (options?.allowPrivateHosts === true)
48
+ return;
49
+ const bare = stripIpv6Brackets(hostname.toLowerCase());
50
+ const literalVersion = isIP(bare);
51
+ if (literalVersion !== 0) {
52
+ const forbidden = literalVersion === 4 ? isForbiddenIpv4(bare) : isForbiddenIpv6(bare);
53
+ if (forbidden)
54
+ throw new Error(`Refusing to fetch non-public website host: ${hostname}`);
55
+ return;
56
+ }
57
+ const resolve = options?.resolveHostname ?? defaultResolveHostname;
58
+ let addresses;
59
+ try {
60
+ addresses = await resolve(bare);
61
+ }
62
+ catch {
63
+ throw new Error(`Refusing to fetch ${hostname}: DNS resolution failed`);
64
+ }
65
+ if (addresses.length === 0) {
66
+ throw new Error(`Refusing to fetch ${hostname}: hostname resolved to no addresses`);
67
+ }
68
+ for (const address of addresses) {
69
+ const version = isIP(address);
70
+ const forbidden = version === 4 ? isForbiddenIpv4(address) : version === 6 ? isForbiddenIpv6(stripIpv6Brackets(address)) : true;
71
+ if (forbidden) {
72
+ throw new Error(`Refusing to fetch ${hostname}: resolves to non-public or unparseable address ${address}`);
73
+ }
74
+ }
75
+ }
76
+ // WHATWG URL.hostname wraps IPv6 literals in brackets (e.g. "[::1]"), but
77
+ // node:net's isIP() only recognizes the bare address form and returns 0 for
78
+ // anything bracketed — silently skipping all IPv6 forbidden-host checks
79
+ // below for every hostname parsed off a URL. Strip the brackets before any
80
+ // isIP()/isForbiddenIpv6() call so those checks actually run.
81
+ function stripIpv6Brackets(hostname) {
82
+ return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
83
+ }
84
+ function isForbiddenWebsiteHostname(hostname, options) {
85
+ if (options?.allowPrivateHosts === true)
86
+ return false;
87
+ if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "metadata.google.internal") {
88
+ return true;
89
+ }
90
+ const bareHostname = stripIpv6Brackets(hostname);
91
+ const ipVersion = isIP(bareHostname);
92
+ if (ipVersion === 4)
93
+ return isForbiddenIpv4(bareHostname);
94
+ if (ipVersion === 6)
95
+ return isForbiddenIpv6(bareHostname);
96
+ return false;
97
+ }
98
+ export function isLoopbackWebsiteHostname(hostname) {
99
+ if (hostname === "localhost" || hostname.endsWith(".localhost"))
100
+ return true;
101
+ const bareHostname = stripIpv6Brackets(hostname);
102
+ const ipVersion = isIP(bareHostname);
103
+ if (ipVersion === 4)
104
+ return bareHostname.startsWith("127.");
105
+ if (ipVersion === 6)
106
+ return bareHostname === "::1";
107
+ return false;
108
+ }
109
+ function isForbiddenIpv4(hostname) {
110
+ const parts = hostname.split(".").map((part) => Number.parseInt(part, 10));
111
+ const [a = -1, b = -1, c = -1, d = -1] = parts;
112
+ if (parts.length !== 4 || [a, b, c, d].some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
113
+ return true;
114
+ }
115
+ return (a === 0 ||
116
+ a === 10 ||
117
+ (a === 100 && b >= 64 && b <= 127) ||
118
+ a === 127 ||
119
+ (a === 169 && b === 254) ||
120
+ (a === 172 && b >= 16 && b <= 31) ||
121
+ (a === 192 && b === 0 && c === 0 && d !== 9 && d !== 10) ||
122
+ (a === 192 && b === 0 && c === 2) ||
123
+ (a === 192 && b === 168) ||
124
+ (a === 192 && b === 88 && c === 99) ||
125
+ (a === 198 && (b === 18 || b === 19)) ||
126
+ (a === 198 && b === 51 && c === 100) ||
127
+ (a === 203 && b === 0 && c === 113) ||
128
+ a >= 224);
129
+ }
130
+ function parseIpv6Words(hostname) {
131
+ let normalized;
132
+ try {
133
+ normalized = stripIpv6Brackets(new URL(`http://[${stripIpv6Brackets(hostname)}]/`).hostname).toLowerCase();
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ const halves = normalized.split("::");
139
+ if (halves.length > 2)
140
+ return null;
141
+ const left = halves[0] ? halves[0].split(":") : [];
142
+ const right = halves[1] ? halves[1].split(":") : [];
143
+ const missing = 8 - left.length - right.length;
144
+ if ((halves.length === 1 && missing !== 0) || (halves.length === 2 && missing < 1))
145
+ return null;
146
+ const parts = halves.length === 2 ? [...left, ...Array(missing).fill("0"), ...right] : left;
147
+ if (parts.length !== 8 || parts.some((part) => !/^[0-9a-f]{1,4}$/.test(part)))
148
+ return null;
149
+ return parts.map((part) => Number.parseInt(part, 16));
150
+ }
151
+ function ipv6Value(hostname) {
152
+ const words = parseIpv6Words(hostname);
153
+ if (!words)
154
+ return null;
155
+ return words.reduce((value, word) => (value << 16n) | BigInt(word), 0n);
156
+ }
157
+ const FORBIDDEN_IPV6_RANGES = [
158
+ ["64:ff9b:1::", 48],
159
+ ["100::", 64],
160
+ ["2001::", 32],
161
+ ["2001:2::", 48],
162
+ ["2001:10::", 28],
163
+ ["2001:20::", 28],
164
+ ["2001:db8::", 32],
165
+ ["3fff::", 20],
166
+ ["5f00::", 16],
167
+ ["fc00::", 7],
168
+ ["fe80::", 10],
169
+ ["fec0::", 10],
170
+ ["ff00::", 8],
171
+ ];
172
+ function embeddedIpv4Address(words) {
173
+ const firstSixZero = words.slice(0, 6).every((word) => word === 0);
174
+ const mapped = words.slice(0, 5).every((word) => word === 0) && words[5] === 0xffff;
175
+ const translated = words.slice(0, 4).every((word) => word === 0) && words[4] === 0xffff && words[5] === 0;
176
+ const wellKnownNat64 = words[0] === 0x64 && words[1] === 0xff9b && words.slice(2, 6).every((word) => word === 0);
177
+ if (words[0] === 0x2002)
178
+ return ipv4FromHextets(words[1] ?? 0, words[2] ?? 0);
179
+ if (!firstSixZero && !mapped && !translated && !wellKnownNat64)
180
+ return null;
181
+ return ipv4FromHextets(words[6] ?? 0, words[7] ?? 0);
182
+ }
183
+ function ipv4FromHextets(high, low) {
184
+ return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;
185
+ }
186
+ function isForbiddenIpv6(hostname) {
187
+ const words = parseIpv6Words(hostname);
188
+ const value = ipv6Value(hostname);
189
+ if (!words || value === null)
190
+ return true;
191
+ const embeddedIpv4 = embeddedIpv4Address(words);
192
+ if (embeddedIpv4)
193
+ return isForbiddenIpv4(embeddedIpv4);
194
+ return FORBIDDEN_IPV6_RANGES.some(([prefix, bits]) => {
195
+ const prefixValue = ipv6Value(prefix);
196
+ if (prefixValue === null)
197
+ return true;
198
+ const shift = BigInt(128 - bits);
199
+ return value >> shift === prefixValue >> shift;
200
+ });
201
+ }
202
+ function redirectInit(init, currentUrl, nextUrl) {
203
+ if (new URL(currentUrl).origin === new URL(nextUrl).origin)
204
+ return init;
205
+ const headers = new Headers(init.headers);
206
+ headers.delete("authorization");
207
+ headers.delete("cookie");
208
+ headers.delete("proxy-authorization");
209
+ return { ...init, headers };
210
+ }
211
+ /**
212
+ * Fetch with the full SSRF guard chain applied to the initial URL and to every
213
+ * redirect hop.
214
+ *
215
+ * `redirect: "manual"` is essential: with the default `follow`, the platform
216
+ * resolves and connects to redirect targets that were never validated, so a
217
+ * public host can bounce a request into the private network.
218
+ */
219
+ export async function fetchGuardedResponse(url, init, opts, redirectCount = 0) {
220
+ assertWebsiteRequestUrl(url, Error, opts);
221
+ await assertResolvedHostAllowed(new URL(url).hostname, opts);
222
+ const response = await fetchWithRetry(url, { ...init, redirect: "manual" }, {
223
+ timeout: opts.timeoutMs,
224
+ retries: opts.retries ?? 1,
225
+ });
226
+ if (response.status >= 300 && response.status < 400) {
227
+ if (redirectCount >= MAX_GUARDED_REDIRECTS) {
228
+ await response.body?.cancel().catch(() => undefined);
229
+ throw new Error(`Too many redirects while fetching ${url}`);
230
+ }
231
+ const location = response.headers.get("location");
232
+ if (!location) {
233
+ await response.body?.cancel().catch(() => undefined);
234
+ throw new Error(`Redirect response from ${url} did not include a Location header`);
235
+ }
236
+ await response.body?.cancel().catch(() => undefined);
237
+ const nextUrl = new URL(location, url).toString();
238
+ return fetchGuardedResponse(nextUrl, redirectInit(init, url, nextUrl), opts, redirectCount + 1);
239
+ }
240
+ return { response, finalUrl: url };
241
+ }
242
+ /**
243
+ * GET text from a compile-time fixed API host, rejecting redirects so request
244
+ * headers (which may contain a token) can never be forwarded elsewhere.
245
+ */
246
+ export async function fetchPinnedText(url, options) {
247
+ const response = await fetchWithRetry(url, { headers: options.headers, signal: options.signal, redirect: "manual" }, { timeout: options.timeoutMs, retries: options.retries ?? 1 });
248
+ if (response.status >= 300 && response.status < 400) {
249
+ await response.body?.cancel().catch(() => undefined);
250
+ return null;
251
+ }
252
+ if (!response.ok) {
253
+ await response.body?.cancel().catch(() => undefined);
254
+ return null;
255
+ }
256
+ try {
257
+ return await readBodyWithByteCap(response, options.byteCap, {
258
+ bodyTimeoutMs: options.bodyTimeoutMs,
259
+ signal: options.signal,
260
+ });
261
+ }
262
+ catch (error) {
263
+ if (options.signal?.aborted)
264
+ throw error;
265
+ return null;
266
+ }
267
+ }
268
+ /**
269
+ * GET a JSON document from a PINNED, constant API host (X, Bluesky XRPC), with
270
+ * a byte-capped read. Returns the parsed value, or null on any 3xx/non-2xx/
271
+ * parse failure.
272
+ *
273
+ * `redirect: "manual"` + rejecting 3xx is load-bearing: these endpoints have no
274
+ * legitimate reason to redirect, and following one would send the next request
275
+ * (bearer token included) to an unvalidated host with none of the SSRF guards
276
+ * the crawl path applies. The full resolve-then-validate DNS guard is
277
+ * intentionally NOT applied here because the host is a compile-time constant,
278
+ * not caller-supplied — the guarded chokepoint is {@link fetchGuardedResponse},
279
+ * which the caller-supplied crawl/feed paths use instead.
280
+ */
281
+ export async function fetchPinnedJson(url, options) {
282
+ const body = await fetchPinnedText(url, options);
283
+ if (body === null)
284
+ return null;
285
+ try {
286
+ return JSON.parse(body);
287
+ }
288
+ catch {
289
+ return null;
290
+ }
291
+ }
@@ -5,10 +5,26 @@ import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
  import { warn } from "../../core/warn.js";
8
+ import blueskyFetcher from "./bluesky.js";
9
+ import githubFetcher from "./github.js";
10
+ import rssFetcher from "./rss.js";
11
+ import xFetcher from "./x.js";
8
12
  import youtubeFetcher from "./youtube.js";
9
13
  const FETCHER_DIR = path.join("scripts", "wiki-fetchers");
10
14
  const FETCHER_FILE_PATTERN = /\.(?:ts|js|mjs)$/i;
11
- const BUILTIN_FETCHERS = [youtubeFetcher];
15
+ /**
16
+ * Order matters: the first fetcher whose `matches` returns true and whose
17
+ * `fetch` returns non-null wins. Host-specific fetchers therefore precede the
18
+ * generic RSS fetcher, whose `matches` is path-shaped and deliberately loose.
19
+ * Any fetcher returning null falls through to the generic website crawler.
20
+ */
21
+ const BUILTIN_FETCHERS = [
22
+ githubFetcher,
23
+ youtubeFetcher,
24
+ blueskyFetcher,
25
+ xFetcher,
26
+ rssFetcher,
27
+ ];
12
28
  function isWikiSnapshotFetcher(value) {
13
29
  if (!value || typeof value !== "object")
14
30
  return false;