akm-cli 0.9.0-rc.0 → 0.9.0-rc.13

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 (598) hide show
  1. package/CHANGELOG.md +1283 -22
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -0,0 +1,286 @@
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
+ /**
5
+ * Shared helpers for the concrete `BundleAdapter` implementations under
6
+ * `src/core/adapter/adapters/` — akm 0.9.0 chunk-2, WI-A (the `okf` adapter).
7
+ *
8
+ * ── Cycle-safety (chunk-2 "cycle-safety watch") ──
9
+ *
10
+ * The base-check logic below is a PORT, not an import, of
11
+ * `src/commands/lint/base-linter.ts`'s `runBaseChecks` (the 4 checks every
12
+ * non-`DefaultLinter` class inherits: unquoted-colon, missing-updated,
13
+ * stale-path, missing-ref). Importing `base-linter.ts` directly would add a
14
+ * new `src/core/adapter` -> `src/commands/lint` edge; `base-linter.ts`
15
+ * itself imports `core/asset/asset-spec.ts` (a baseline cycle participant,
16
+ * `scripts/lint-import-cycles.ts`) and `commands/lint/markdown-insertion.ts`
17
+ * — pulling either into `src/core/adapter/`'s import graph risks adding a
18
+ * 19th cycle participant against the zero-tolerance ratchet (baseline 18).
19
+ * Per the brief's explicit instruction ("copy the pure logic to a leaf ...
20
+ * do NOT use dynamic import to launder"), the pure regex/string logic is
21
+ * duplicated here, adapted to `Diagnostic`/`ValidateContext`. Verified
22
+ * cycle-safe: `bun scripts/lint-import-cycles.ts` stays at 18 with this file
23
+ * present (see the WI-A report).
24
+ *
25
+ * ── What did NOT get ported ──
26
+ *
27
+ * `bundle-adapter.ts`'s own doc comment is explicit that `validate()`
28
+ * "MUST NOT write and MUST NOT read the live filesystem" — reads go through
29
+ * `ValidateContext`, which serves the run snapshot WITH pending changes
30
+ * overlaid. `stale-path`'s legacy check called `fs.existsSync` directly on
31
+ * arbitrary absolute paths found in content; here it goes through
32
+ * `ctx.readFile(candidate)` instead (a `null` result means "does not
33
+ * exist") — the interface-compliant translation of the same check, not a
34
+ * behavior change for any path that genuinely exists or doesn't.
35
+ *
36
+ * The `refs:`-frontmatter-array authoritative-list carve-out (session-
37
+ * checkpoint memories; `base-linter.ts`'s `extractFrontmatterRefs`) is NOT
38
+ * ported here — it is memory/session-specific and no OKF concept uses it. A
39
+ * later memory/note adapter should extend `extractProseRefTokens`'s caller with
40
+ * that carve-out when it ports memory/session.
41
+ *
42
+ * The fence-strip (`stripFencedBlocksSimple` below) is a simplified,
43
+ * same-INTENT reimplementation of `markdown-insertion.ts`'s
44
+ * `findFenceRegions` (which is also table/HTML-region aware, because it
45
+ * additionally powers safe line INSERTION, a concern this module doesn't
46
+ * have) — not byte-identical on every edge case, but sufficient for "don't
47
+ * flag a ref/path inside a ``` example," which is the only thing this
48
+ * module needs the strip for.
49
+ */
50
+ import { createHash } from "node:crypto";
51
+ import { parseFrontmatter } from "../../asset/frontmatter.js";
52
+ // ── Small pure helpers, reused across the concrete adapters ──────────────────
53
+ /** Content hash feeding `IndexDocument.hash` (incrementality/fingerprints, `types.ts` doc comment). */
54
+ export function hashContent(content) {
55
+ return createHash("sha256").update(content, "utf8").digest("hex");
56
+ }
57
+ /** Trim a possibly-non-string frontmatter value down to a non-empty string or `undefined` (mirrors `core/common.ts#asNonEmptyString`, duplicated locally to avoid importing a cycle-participant module for a 4-line helper). */
58
+ export function nonEmptyString(value) {
59
+ if (typeof value !== "string")
60
+ return undefined;
61
+ const trimmed = value.trim();
62
+ return trimmed.length > 0 ? trimmed : undefined;
63
+ }
64
+ /** Read a frontmatter `tags:` array down to its non-empty string entries, or `undefined` when there are none (mirrors `output/renderers.ts#readFrontmatterTags`). */
65
+ export function readTags(value) {
66
+ if (!Array.isArray(value))
67
+ return undefined;
68
+ const tags = value.filter((tag) => typeof tag === "string" && tag.trim().length > 0);
69
+ return tags.length > 0 ? tags : undefined;
70
+ }
71
+ // ── Base validate checks (port of `BaseLinter.runBaseChecks`) ────────────────
72
+ function checkUnquotedColon(frontmatterText) {
73
+ if (!frontmatterText)
74
+ return null;
75
+ for (const line of frontmatterText.split(/\r?\n/)) {
76
+ const match = line.match(/^description:\s*(.*)/);
77
+ if (!match)
78
+ continue;
79
+ const value = match[1].trim();
80
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
81
+ return null;
82
+ }
83
+ if (value.includes(":")) {
84
+ return `description value contains unquoted colon: ${value}`;
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+ function checkMissingUpdated(data, frontmatterText) {
90
+ return frontmatterText !== null && !("updated" in data);
91
+ }
92
+ function findStalePathCandidates(body) {
93
+ const pathRe = /(?:\/home\/|\/tmp\/|\/var\/|\/root\/|\/opt\/)[^\s"'`)\]>,\n]+/g;
94
+ const out = [];
95
+ let match;
96
+ // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex loop
97
+ while ((match = pathRe.exec(body)) !== null)
98
+ out.push(match[0]);
99
+ return out;
100
+ }
101
+ /** See file header: same intent as `markdown-insertion.ts#findFenceRegions`, not byte-identical. */
102
+ function stripFencedBlocksSimple(body) {
103
+ const lines = body.split(/\r?\n/);
104
+ let inFence = false;
105
+ const out = lines.map((line) => {
106
+ if (/^\s*```/.test(line)) {
107
+ const wasInFence = inFence;
108
+ inFence = !inFence;
109
+ return wasInFence || inFence ? "" : line;
110
+ }
111
+ return inFence ? "" : line;
112
+ });
113
+ return out.join("\n");
114
+ }
115
+ // Ref-token grammar — copied from `base-linter.ts`'s `BUNDLE_REF_RE`
116
+ // (`core/asset/asset-ref.ts`, spec §11.1 / ref-grammar decision D-R3) as a VALUE,
117
+ // not an import (same cycle-avoidance rationale as the file header — the source
118
+ // string is duplicated so no new `core/adapter` -> `core/asset` edge is added for
119
+ // this scan). Recognizes the 0.9.0 FULLY-QUALIFIED `<bundle>//<concept-id>
120
+ // [#fragment]` prose body-ref anchored form ONLY. Per D-R3, a bare short
121
+ // conceptId in prose is NOT a ref, so it is never flagged here (it is a ref only
122
+ // in the ref-LIST channels — {@link refTokenFromListValue}). The pre-0.9.0
123
+ // `<type>:<slug>` colon grammar is gone from every recognition surface.
124
+ const BUNDLE_BODY_REF_RE_SRC = "(?:^|[\\s`\"'(,[])([^\\s:.#/`\"'()[\\],<>]+\\/\\/[^\\s\"'`)\\]>,\\n]+)";
125
+ /**
126
+ * A conceptId that is a lint false positive rather than a real ref — mirrors the
127
+ * `isNonRefName` guards `base-linter.ts` applies deep in its existence check:
128
+ * template placeholders (`skills/<name>`) and degenerate/incomplete tokens.
129
+ */
130
+ function isNonRefConceptId(conceptId) {
131
+ const id = conceptId.split("#", 1)[0]; // drop the export #fragment selector
132
+ if (!id || id.length <= 1 || id === "**")
133
+ return true;
134
+ if (id.includes("<"))
135
+ return true; // template placeholder, e.g. `skills/<name>`
136
+ return false;
137
+ }
138
+ /**
139
+ * Extract the fully-qualified `bundle//conceptId` refs from PROSE — the flipped
140
+ * `base-linter.ts#checkMissingRefs` semantics (`BUNDLE_REF_RE`, qualified-only;
141
+ * bare short conceptIds are NOT refs, D-R3). Fenced code is stripped first so
142
+ * example refs inside ``` are not flagged; shell-substitution (`$(`/`${`) and
143
+ * ACP (`::`) false positives are dropped.
144
+ */
145
+ function extractProseRefTokens(text) {
146
+ const re = new RegExp(BUNDLE_BODY_REF_RE_SRC, "gm");
147
+ const scanBody = stripFencedBlocksSimple(text);
148
+ const refs = [];
149
+ let match;
150
+ // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex loop
151
+ while ((match = re.exec(scanBody)) !== null) {
152
+ const token = match[1];
153
+ if (token.includes("$(") || token.includes("${") || token.includes("::"))
154
+ continue;
155
+ if (isNonRefConceptId(token.slice(token.indexOf("//") + 2)))
156
+ continue;
157
+ refs.push(token);
158
+ }
159
+ return refs;
160
+ }
161
+ /**
162
+ * Recognize a single frontmatter ref-LIST value (`xrefs:`/`supersededBy:`/
163
+ * `contradictedBy:`) as a whole ref — the flipped `base-linter.ts#
164
+ * checkMissingRefsInList` semantics. Unlike prose, a bare short `conceptId` IS a
165
+ * ref here (the value's whole purpose is to name one asset). Returns the ref
166
+ * token, or `null` for a false positive or a legacy `origin//type:name` value
167
+ * (the colon-grammar is retired — D-R3).
168
+ */
169
+ function refTokenFromListValue(value) {
170
+ const trimmed = value.trim();
171
+ if (!trimmed || trimmed.includes("$(") || trimmed.includes("${") || trimmed.includes("::"))
172
+ return null;
173
+ const boundary = trimmed.indexOf("//");
174
+ // Qualified `bundle//conceptId`: a colon in the tail marks a legacy/remote
175
+ // `origin//type:name` — not the 0.9.0 grammar — so skip it. Un-prefixed values
176
+ // are a 0.9.0 short conceptId; a colon there is likewise the retired grammar.
177
+ const conceptId = boundary >= 0 ? trimmed.slice(boundary + 2) : trimmed;
178
+ if (conceptId.includes(":") || isNonRefConceptId(conceptId))
179
+ return null;
180
+ return trimmed;
181
+ }
182
+ const XREF_FRONTMATTER_KEYS = ["xrefs", "supersededBy", "contradictedBy"];
183
+ function readRefStringOrArray(value) {
184
+ if (typeof value === "string") {
185
+ const trimmed = value.trim();
186
+ return trimmed ? [trimmed] : null;
187
+ }
188
+ if (!Array.isArray(value))
189
+ return null;
190
+ const out = value.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim());
191
+ return out.length > 0 ? out : null;
192
+ }
193
+ /**
194
+ * Port of `BaseLinter.runBaseChecks`, adapted to the read-only
195
+ * `ValidateContext`/`Diagnostic` shapes (adapter spec §12.1). Reproduces
196
+ * unquoted-colon / missing-updated / stale-path / missing-ref for ONE file's
197
+ * already-parsed content.
198
+ *
199
+ * `fixed` is always `false`: `BundleAdapter.validate` MUST NOT write the
200
+ * filesystem, so there is no fix-apply mechanism at this layer — a documented,
201
+ * intentional behavior narrowing from the legacy `--fix`-capable linter.
202
+ *
203
+ * `componentRoot` is `BundleComponent.root` — used only for the stale-path
204
+ * "portable form" hint (mirrors legacy `ctx.stashRoot`-relative substitution;
205
+ * no I/O).
206
+ */
207
+ export async function runBaseValidateChecks(relPath, parsed, componentRoot, ctx) {
208
+ const diagnostics = [];
209
+ const { data, content: body, frontmatter } = parsed;
210
+ const unquotedColonDetail = checkUnquotedColon(frontmatter);
211
+ if (unquotedColonDetail) {
212
+ diagnostics.push({ file: relPath, issue: "unquoted-colon", detail: unquotedColonDetail, fixed: false });
213
+ }
214
+ if (checkMissingUpdated(data, frontmatter)) {
215
+ diagnostics.push({
216
+ file: relPath,
217
+ issue: "missing-updated",
218
+ detail: "no updated field in frontmatter",
219
+ fixed: false,
220
+ });
221
+ }
222
+ const staleCandidates = [
223
+ ...findStalePathCandidates(body),
224
+ ...(frontmatter ? findStalePathCandidates(frontmatter) : []),
225
+ ];
226
+ for (const candidate of staleCandidates) {
227
+ const found = await ctx.readFile(candidate);
228
+ if (found !== null)
229
+ continue;
230
+ const portableHint = candidate.startsWith(componentRoot)
231
+ ? ` (portable form: $AKM_BUNDLE_DIR${candidate.slice(componentRoot.length)})`
232
+ : "";
233
+ diagnostics.push({
234
+ file: relPath,
235
+ issue: "stale-path",
236
+ detail: `nonexistent path: ${candidate}${portableHint}`,
237
+ fixed: false,
238
+ });
239
+ }
240
+ for (const ref of extractProseRefTokens(body)) {
241
+ const { exists } = await ctx.resolveRef(ref);
242
+ if (!exists)
243
+ diagnostics.push({ file: relPath, issue: "missing-ref", detail: `missing ref: ${ref}`, fixed: false });
244
+ }
245
+ if (frontmatter !== null) {
246
+ for (const key of XREF_FRONTMATTER_KEYS) {
247
+ const values = readRefStringOrArray(data[key]);
248
+ if (values === null)
249
+ continue;
250
+ for (const value of values) {
251
+ const ref = refTokenFromListValue(value);
252
+ if (ref === null)
253
+ continue;
254
+ const { exists } = await ctx.resolveRef(ref);
255
+ if (!exists) {
256
+ diagnostics.push({
257
+ file: relPath,
258
+ issue: "missing-ref",
259
+ detail: `missing ref: ${ref} (frontmatter ${key})`,
260
+ fixed: false,
261
+ });
262
+ }
263
+ }
264
+ }
265
+ }
266
+ return diagnostics;
267
+ }
268
+ /**
269
+ * Shared per-change loop: for every non-delete change with readable content,
270
+ * parse frontmatter and run {@link runBaseValidateChecks}. Adapters with extra
271
+ * per-type checks (e.g. `okf`'s missing-type / OKF-link warnings) run their own
272
+ * loop and call {@link runBaseValidateChecks} directly instead.
273
+ */
274
+ export async function validateChangesWithBaseChecks(c, changes, ctx) {
275
+ const diagnostics = [];
276
+ for (const change of changes) {
277
+ if (change.op === "delete")
278
+ continue;
279
+ const raw = change.after ?? (await ctx.readFile(change.path));
280
+ if (typeof raw !== "string")
281
+ continue;
282
+ const parsed = parseFrontmatter(raw);
283
+ diagnostics.push(...(await runBaseValidateChecks(change.path, parsed, c.root, ctx)));
284
+ }
285
+ return diagnostics;
286
+ }
@@ -0,0 +1,217 @@
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
+ /**
5
+ * Shared recognition / placement / validation for the two AGENT TOOL-DIRECTORY
6
+ * adapters — `claude` (`.claude`) and `opencode` (`.opencode`) — akm 0.9.0
7
+ * format-family work item (#46). Both translate a foreign tool layout into the
8
+ * open AKM `type` vocabulary (spec §6/§7): a root instruction file
9
+ * (CLAUDE.md / AGENTS.md) → `instruction`, `commands/*.md` → `command`,
10
+ * `agents/*.md` → `agent`, `skills/<name>/SKILL.md` → `skill` (item = the dir).
11
+ * They differ only in three parameters carried on {@link ToolDirLayout}:
12
+ * - the instruction basename + its concept id,
13
+ * - the accepted subdir spellings (opencode accepts the SINGULAR
14
+ * `command/`/`agent/`/`skill/` aliases per open-question-6, claude does not),
15
+ * - the adapter id + component id.
16
+ *
17
+ * The SKILL.md codec is shared with the `agent-skills` adapter as functions
18
+ * (spec §8): a skill's conceptId is its DIRECTORY, and its `name` is the
19
+ * frontmatter `name` (== the dir name per the Agent Skills §4.5 hard rule).
20
+ *
21
+ * ── D-R6 (reserved files) ──
22
+ *
23
+ * `index.md` / `log.md` are OKF structural files at EVERY depth — never a
24
+ * concept. `classify` excludes them up front so a stray `commands/index.md`
25
+ * never becomes a `command` (mirrors okf/akm/llm-wiki `RESERVED_FILES`).
26
+ *
27
+ * ── validate leniency (spec §6 skill row) ──
28
+ *
29
+ * The tool-dir adapters TOLERATE the tools' native frontmatter (argument-hint,
30
+ * allowed-tools, tools, model, mode, temperature, agent, …) — the strict
31
+ * unknown-frontmatter behavior belongs to `agent-skills`, not here. So `command`
32
+ * / `agent` get a LENIENT name-or-signal check (never the akm `CommandLinter`'s
33
+ * frontmatter `name`+`type` requirement, which these tool files do not carry),
34
+ * `skill` gets the coded `missing-skill-md` directory check
35
+ * ({@link skillDirectoryDiagnostics}), and `instruction` runs the shared base
36
+ * checks (a no-op on a frontmatter-free CLAUDE.md/AGENTS.md). Notably NO base
37
+ * checks run on command/agent/skill: their tool frontmatter carries no `updated`
38
+ * field, so `missing-updated` would fire on every file and contradict the lint
39
+ * golden's clean result.
40
+ *
41
+ * ── Cycle-safety ──
42
+ *
43
+ * Imported only by `claude-adapter.ts` / `opencode-adapter.ts` (themselves
44
+ * imported by the `adapters/index.ts` barrel — NOT test-only: `core/adapter/
45
+ * registry.ts:33` imports it in production for the frozen `BUILTIN_ADAPTERS`
46
+ * list). This leaf still can never gain an inbound edge from a cycle
47
+ * participant, since nothing on that import chain imports back into it. It
48
+ * value-imports only pure leaves (`shared`, `akm-lint`, `frontmatter`) plus
49
+ * Node builtins.
50
+ */
51
+ import path from "node:path";
52
+ import { parseFrontmatter } from "../../asset/frontmatter.js";
53
+ import { skillDirectoryDiagnostics } from "./akm-lint.js";
54
+ import { hashContent, nonEmptyString, readTags, runBaseValidateChecks } from "./shared.js";
55
+ /** OKF reserved structural files (D-R6) — excluded at every depth, case-insensitive. */
56
+ const RESERVED_FILES = new Set(["index.md", "log.md"]);
57
+ /** Upper bound on the bounded `content` FTS field (mirrors okf-adapter). */
58
+ const MAX_CONTENT_CHARS = 100_000;
59
+ /** The canonical (plural) subdir spellings writes normalize to (open-question-6). */
60
+ const CANONICAL_COMMAND_DIR = "commands";
61
+ const CANONICAL_AGENT_DIR = "agents";
62
+ const CANONICAL_SKILL_DIR = "skills";
63
+ const SKILL_MANIFEST = "SKILL.md";
64
+ function toPosix(p) {
65
+ return p.replace(/\\/g, "/");
66
+ }
67
+ function isReserved(base) {
68
+ return RESERVED_FILES.has(base.toLowerCase());
69
+ }
70
+ /** Classify one component-root-relative file into a tool-dir `type` + conceptId, or abstain (null). */
71
+ function classify(relPath, layout) {
72
+ const posix = toPosix(relPath);
73
+ const segs = posix.split("/").filter((s) => s.length > 0);
74
+ if (segs.length === 0)
75
+ return null;
76
+ const base = segs[segs.length - 1];
77
+ if (isReserved(base))
78
+ return null;
79
+ // Root instruction file.
80
+ if (segs.length === 1 && base === layout.instructionFile) {
81
+ return { type: "instruction", conceptId: layout.instructionConceptId, name: layout.instructionConceptId };
82
+ }
83
+ const head = segs[0];
84
+ const ext = path.extname(base).toLowerCase();
85
+ // skill: <skillDir>/<name>/SKILL.md — the item is the DIRECTORY. Any other
86
+ // file under a skill dir (bundled resources) is part of the item, not a concept.
87
+ if (layout.skillDirs.has(head)) {
88
+ if (segs.length >= 3 && base === SKILL_MANIFEST) {
89
+ return { type: "skill", conceptId: `${segs[0]}/${segs[1]}`, name: segs[1] };
90
+ }
91
+ return null;
92
+ }
93
+ if (layout.commandDirs.has(head) && ext === ".md" && segs.length >= 2) {
94
+ return { type: "command", conceptId: posix.replace(/\.md$/i, ""), name: base.replace(/\.md$/i, "") };
95
+ }
96
+ if (layout.agentDirs.has(head) && ext === ".md" && segs.length >= 2) {
97
+ return { type: "agent", conceptId: posix.replace(/\.md$/i, ""), name: base.replace(/\.md$/i, "") };
98
+ }
99
+ return null;
100
+ }
101
+ /** recognize() for a tool-dir adapter: derive the open `type`, project the OKF-shaped fields. */
102
+ export function recognizeToolDir(layout, c, file) {
103
+ const cls = classify(file.relPath, layout);
104
+ if (cls === null)
105
+ return null;
106
+ const raw = file.content();
107
+ const parsed = parseFrontmatter(raw);
108
+ const data = parsed.data;
109
+ const body = parsed.content;
110
+ // For a skill, `name` is the frontmatter `name` (== dir name); otherwise the
111
+ // basename-derived concept name.
112
+ const name = cls.type === "skill" ? (nonEmptyString(data.name) ?? cls.name) : cls.name;
113
+ const description = nonEmptyString(data.description);
114
+ const tags = readTags(data.tags);
115
+ const doc = {
116
+ ref: `${c.id}//${cls.conceptId}`,
117
+ bundle: c.id,
118
+ component: layout.componentId,
119
+ conceptId: cls.conceptId,
120
+ path: file.absPath,
121
+ hash: hashContent(raw),
122
+ adapterId: layout.adapterId,
123
+ type: cls.type,
124
+ name,
125
+ content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
126
+ };
127
+ if (cls.type === "instruction")
128
+ doc.ownsPresentation = true;
129
+ if (description !== undefined)
130
+ doc.description = description;
131
+ if (tags !== undefined)
132
+ doc.tags = tags;
133
+ return doc;
134
+ }
135
+ /**
136
+ * placeNew() for a tool-dir adapter (spec §7: "AKM workspace layout IS the tool
137
+ * dir minus the prefix"). Writes NORMALIZE to the canonical plural subdir
138
+ * (open-question-6): a `command/foo` concept still places at `commands/foo.md`.
139
+ * The instruction file is fixed at the component root.
140
+ */
141
+ export function placeNewToolDir(layout, c, conceptId) {
142
+ const posix = toPosix(conceptId);
143
+ if (posix === layout.instructionConceptId)
144
+ return path.join(c.root, layout.instructionFile);
145
+ const segs = posix.split("/").filter((s) => s.length > 0);
146
+ const head = segs[0];
147
+ const rest = segs.slice(1).join("/");
148
+ if (rest.length > 0) {
149
+ if (layout.skillDirs.has(head))
150
+ return path.join(c.root, CANONICAL_SKILL_DIR, rest, SKILL_MANIFEST);
151
+ if (layout.commandDirs.has(head))
152
+ return path.join(c.root, CANONICAL_COMMAND_DIR, `${rest}.md`);
153
+ if (layout.agentDirs.has(head))
154
+ return path.join(c.root, CANONICAL_AGENT_DIR, `${rest}.md`);
155
+ }
156
+ return path.join(c.root, `${posix}.md`);
157
+ }
158
+ /** LENIENT command/agent check: a diagnostic only when NEITHER a name/description NOR a type-shaped signal is present. */
159
+ function nameOrSignalDiagnostics(type, relPath, data, body) {
160
+ if (nonEmptyString(data.name) !== undefined || nonEmptyString(data.description) !== undefined)
161
+ return [];
162
+ // command-shaped body/frontmatter signals (spec §6 command row): $ARGUMENTS / $1 / an `agent` frontmatter key.
163
+ if (type === "command" &&
164
+ (/\$ARGUMENTS\b/.test(body) || /\$\d/.test(body) || nonEmptyString(data.agent) !== undefined)) {
165
+ return [];
166
+ }
167
+ return [
168
+ {
169
+ file: relPath,
170
+ issue: "missing-name-or-type",
171
+ detail: `${type} has neither a name/description nor a ${type}-shaped signal`,
172
+ fixed: false,
173
+ },
174
+ ];
175
+ }
176
+ /** validate() for a tool-dir adapter — see file header for the leniency contract. */
177
+ export async function validateToolDir(layout, c, changes, ctx) {
178
+ const diagnostics = [];
179
+ const seenSkillDirs = new Set();
180
+ for (const change of changes) {
181
+ if (change.op === "delete")
182
+ continue;
183
+ const raw = change.after ?? (await ctx.readFile(change.path));
184
+ if (typeof raw !== "string")
185
+ continue;
186
+ const relPath = toPosix(change.path);
187
+ // The one coded skill check (missing-skill-md) fires on ANY change under a
188
+ // `skills/<name>/…` package (self-gated + deduped), even a bundled resource —
189
+ // mirrors the akm adapter's per-change SkillLinter.lintDirectory pass.
190
+ diagnostics.push(...(await skillDirectoryDiagnostics(relPath, seenSkillDirs, ctx)));
191
+ const cls = classify(change.path, layout);
192
+ if (cls === null)
193
+ continue;
194
+ if (cls.type === "instruction") {
195
+ diagnostics.push(...(await runBaseValidateChecks(relPath, parseFrontmatter(raw), c.root, ctx)));
196
+ }
197
+ else if (cls.type === "command" || cls.type === "agent") {
198
+ const parsed = parseFrontmatter(raw);
199
+ diagnostics.push(...nameOrSignalDiagnostics(cls.type, relPath, parsed.data, parsed.content));
200
+ }
201
+ // skill: covered by skillDirectoryDiagnostics above; no additional per-file check.
202
+ }
203
+ return diagnostics;
204
+ }
205
+ /** Build the concrete `BundleAdapter` from a layout + a `looksLikeRoot` probe (claude/opencode share everything else). */
206
+ export function makeToolDirAdapter(layout, looksLikeRoot) {
207
+ return {
208
+ id: layout.adapterId,
209
+ version: "0.9.0",
210
+ extensions: [".md"],
211
+ recognize: (c, file) => recognizeToolDir(layout, c, file),
212
+ validate: (c, changes, ctx) => validateToolDir(layout, c, changes, ctx),
213
+ placeNew: (c, conceptId) => placeNewToolDir(layout, c, conceptId),
214
+ directoryList: () => [CANONICAL_COMMAND_DIR, CANONICAL_AGENT_DIR, CANONICAL_SKILL_DIR],
215
+ looksLikeRoot,
216
+ };
217
+ }
@@ -0,0 +1,155 @@
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
+ /**
5
+ * The `website-snapshot` adapter — akm 0.9.0 format-family work item (#46).
6
+ *
7
+ * A READ-ONLY (Mode A) view over a materialized crawl snapshot written by
8
+ * `src/sources/snapshot-fetchers/website-ingest.ts` (spec §6/§7). The on-disk
9
+ * pages are KNOWLEDGE-shaped markdown under `stash/knowledge/**` with no `type:`
10
+ * field; this adapter RE-TYPES them to the open `type: website`. Per
11
+ * open-question-3 (RESOLVED, maintainer 2026-07) the re-type is GATED — it
12
+ * applies only to a page carrying the `website` tag / a `sourceUrl` (a page
13
+ * lacking both is not re-typed); the document KEEPS its `sourceUrl` (surfaced as
14
+ * `sourceRef` on `documentJson`); and the conceptId STRIPS the
15
+ * `stash/knowledge/` prefix. `manifest.json` is snapshot PROVENANCE
16
+ * (`{url, fetchedAt}`), read by the probe but never indexed as a concept.
17
+ *
18
+ * Read-only ⇒ NO `placeNew` (Mode B export routes content through the
19
+ * DESTINATION adapter, which owns placement). `validate` runs base checks only
20
+ * (a mirror the adapter does not author has no native validators); `updated`
21
+ * never appears on a snapshot page, so `missing-updated` is filtered out.
22
+ *
23
+ * Conformance oracle (authored, DO NOT modify): fixture
24
+ * `tests/fixtures/bundles/website-snapshot/` + goldens
25
+ * `tests/fixtures/format-family-goldens/website-snapshot/{recognition,placement,lint,renderer}.json`.
26
+ */
27
+ import fs from "node:fs";
28
+ import path from "node:path";
29
+ import { parse as parseYaml } from "yaml";
30
+ import { parseFrontmatter, parseFrontmatterBlock } from "../../asset/frontmatter.js";
31
+ import { hashContent, nonEmptyString, runBaseValidateChecks } from "./shared.js";
32
+ /** A snapshot bundle is single-component; its one component is `main`. */
33
+ const COMPONENT_ID = "main";
34
+ /** The crawl page root the snapshot writer materializes pages under. */
35
+ const PAGES_PREFIX = "stash/knowledge/";
36
+ /** The provenance manifest basename (probe marker only, never indexed). */
37
+ const MANIFEST_FILE = "manifest.json";
38
+ /** The tag the snapshot writer stamps on every crawled page. */
39
+ const WEBSITE_TAG = "website";
40
+ /** Upper bound on the bounded `content` FTS field (mirrors okf-adapter). */
41
+ const MAX_CONTENT_CHARS = 100_000;
42
+ function toPosix(p) {
43
+ return p.replace(/\\/g, "/");
44
+ }
45
+ function parseSnapshotFrontmatter(raw) {
46
+ const out = { tags: [] };
47
+ const block = parseFrontmatterBlock(raw);
48
+ if (!block)
49
+ return out;
50
+ let data = {};
51
+ try {
52
+ const value = parseYaml(block.frontmatter);
53
+ if (value && typeof value === "object" && !Array.isArray(value))
54
+ data = value;
55
+ }
56
+ catch {
57
+ return out;
58
+ }
59
+ out.title = nonEmptyString(data.title);
60
+ out.description = nonEmptyString(data.description);
61
+ out.sourceUrl = nonEmptyString(data.sourceUrl);
62
+ if (Array.isArray(data.tags)) {
63
+ out.tags = data.tags.filter((t) => typeof t === "string" && t.trim().length > 0).map((t) => t.trim());
64
+ }
65
+ return out;
66
+ }
67
+ /** True when a page is a crawl snapshot page under `stash/knowledge/**`. */
68
+ function isSnapshotPage(relPath) {
69
+ const posix = toPosix(relPath);
70
+ return posix.startsWith(PAGES_PREFIX) && posix.toLowerCase().endsWith(".md");
71
+ }
72
+ /** The GATED re-type predicate (open-question-3): the page carries the `website` tag OR a `sourceUrl`. */
73
+ function isReTypeGated(fm) {
74
+ return fm.sourceUrl !== undefined || fm.tags.includes(WEBSITE_TAG);
75
+ }
76
+ /** conceptId = page path − `stash/knowledge/` prefix − `.md`. */
77
+ function conceptIdOf(relPath) {
78
+ return toPosix(relPath).slice(PAGES_PREFIX.length).replace(/\.md$/i, "");
79
+ }
80
+ function recognize(c, file) {
81
+ if (file.ext !== ".md" || !isSnapshotPage(file.relPath))
82
+ return null;
83
+ const raw = file.content();
84
+ const fm = parseSnapshotFrontmatter(raw);
85
+ if (!isReTypeGated(fm))
86
+ return null; // not a re-typed website page
87
+ const conceptId = conceptIdOf(file.relPath);
88
+ const name = fm.title ?? conceptId.split("/").pop() ?? conceptId;
89
+ const body = parseFrontmatter(raw).content;
90
+ const doc = {
91
+ ref: `${c.id}//${conceptId}`,
92
+ bundle: c.id,
93
+ component: COMPONENT_ID,
94
+ conceptId,
95
+ path: file.absPath,
96
+ hash: hashContent(raw),
97
+ adapterId: "website-snapshot",
98
+ ownsPresentation: true,
99
+ type: "website",
100
+ name,
101
+ content: body.length > MAX_CONTENT_CHARS ? body.slice(0, MAX_CONTENT_CHARS) : body,
102
+ };
103
+ if (fm.description !== undefined)
104
+ doc.description = fm.description;
105
+ if (fm.sourceUrl !== undefined)
106
+ doc.documentJson = { sourceRef: fm.sourceUrl };
107
+ return doc;
108
+ }
109
+ async function validate(c, changes, ctx) {
110
+ const diagnostics = [];
111
+ for (const change of changes) {
112
+ if (change.op === "delete")
113
+ continue;
114
+ const raw = change.after ?? (await ctx.readFile(change.path));
115
+ if (typeof raw !== "string")
116
+ continue;
117
+ if (!isSnapshotPage(change.path))
118
+ continue;
119
+ const base = await runBaseValidateChecks(toPosix(change.path), parseFrontmatter(raw), c.root, ctx);
120
+ // A read-only crawl mirror carries `fetchedAt` provenance, never an `updated`
121
+ // field — so `missing-updated` is not a defect on a snapshot page.
122
+ diagnostics.push(...base.filter((d) => d.issue !== "missing-updated"));
123
+ }
124
+ return diagnostics;
125
+ }
126
+ export const websiteSnapshotAdapter = {
127
+ id: "website-snapshot",
128
+ version: "0.9.0",
129
+ extensions: [".md"],
130
+ recognize,
131
+ validate,
132
+ // No placeNew: the snapshot is READ-ONLY (Mode A). Export (Mode B) routes
133
+ // content through the DESTINATION adapter, which owns placement.
134
+ /**
135
+ * Install-time probe (§1.2): a root carrying the snapshot provenance
136
+ * `manifest.json` (`{url, fetchedAt}`). No other bundle root carries this
137
+ * exact marker, so the probe is unambiguous.
138
+ */
139
+ looksLikeRoot(root) {
140
+ let raw;
141
+ try {
142
+ raw = fs.readFileSync(path.join(root, MANIFEST_FILE), "utf8");
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ try {
148
+ const manifest = JSON.parse(raw);
149
+ return typeof manifest.url === "string" && typeof manifest.fetchedAt === "string";
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ },
155
+ };
@@ -0,0 +1,4 @@
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
+ export {};