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
@@ -1,13 +1,13 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- // crontab backend for `akm tasks` (Linux default).
4
+ // crontab backend for `akm task` (Linux default).
5
5
  //
6
6
  // Each akm-owned entry is wrapped in markers so a hand-edited crontab keeps
7
7
  // its other lines untouched:
8
8
  //
9
9
  // # akm:task <id> BEGIN
10
- // [SCHED] /abs/akm tasks run <id> >> /home/.../tasks/logs/<id>.log 2>&1
10
+ // [SCHED] /abs/akm task run <id> >> /home/.../tasks/logs/<id>.log 2>&1
11
11
  // # akm:task <id> END
12
12
  //
13
13
  // The backend reads/writes the user's crontab via `crontab -l` and
@@ -25,59 +25,100 @@
25
25
  //
26
26
  // Tests inject a fake exec so unit tests don't touch the real crontab.
27
27
  import { spawnSync } from "node:child_process";
28
- import fs from "node:fs";
29
28
  import path from "node:path";
30
29
  import { ConfigError } from "../../core/errors.js";
31
30
  import { getTaskLogDir } from "../../core/paths.js";
32
31
  import { resolveAkmInvocation } from "../resolve-akm-bin.js";
33
32
  import { parseSchedule, translateToCron } from "../schedule.js";
34
- const BEGIN = (id) => `# akm:task ${id} BEGIN`;
35
- const END = (id) => `# akm:task ${id} END`;
33
+ import { buildScheduledTaskInvocation, parseScheduledTaskArgv, resolveScheduledTaskContext, schedulerContextDescriptor, schedulerContextPath, } from "../scheduler-invocation.js";
34
+ import { nodeFs, throwIfNotOk } from "./exec-utils.js";
35
+ const BEGIN = (id) => `# akm:task ${assertCronValue(id)} BEGIN`;
36
+ const END = (id) => `# akm:task ${assertCronValue(id)} END`;
36
37
  const DISABLED_PREFIX = "# akm:disabled ";
37
38
  const BLOCK_RE = /^# akm:task ([\w.@:_-]+) BEGIN$/;
39
+ const BLOCK_END_RE = /^# akm:task ([\w.@:_-]+) END$/;
40
+ export const PORTABLE_CRON_LINE_LIMIT = 1000;
38
41
  export function CRON_BACKEND(options = {}) {
39
42
  const exec = options.exec ?? defaultCronExec();
43
+ const fsLike = options.fs ?? nodeFs();
40
44
  const logDir = options.logDir ?? getTaskLogDir();
41
45
  const akmArgv = options.akmArgv ?? resolveAkmInvocation().argv;
46
+ const envPath = options.envPath === false ? undefined : (options.envPath ?? process.env.PATH);
47
+ const scheduledContext = options.scheduledContext ?? resolveScheduledTaskContext();
48
+ const defaultContextPath = schedulerContextPath(schedulerContextDescriptor(scheduledContext, envPath ?? ""));
42
49
  return {
43
50
  name: "cron",
44
- install(task) {
51
+ install(task, opts) {
45
52
  // Create the log directory before writing the crontab line — cron
46
53
  // appends with `>>` and the surrounding shell will fail the entire
47
54
  // entry if the parent directory doesn't exist.
48
- ensureDir(logDir);
49
- const cronLine = buildCronLine(task, akmArgv, logDir);
55
+ const cronLine = buildCronLine(task, [...(opts?.binding ?? akmArgv)], logDir, opts?.contextPath ?? defaultContextPath, opts?.target);
56
+ assertPortableCronLine(cronLine);
57
+ fsLike.ensureDir(logDir);
50
58
  const existing = readCrontab(exec);
51
59
  const block = renderBlock(task.id, cronLine, task.enabled);
52
60
  const next = upsertBlock(existing, task.id, block);
53
- writeCrontab(exec, next);
61
+ replaceCrontab(exec, existing, next);
54
62
  },
55
63
  uninstall(id) {
56
64
  const existing = readCrontab(exec);
57
65
  const next = removeBlock(existing, id);
58
- writeCrontab(exec, next);
66
+ replaceCrontab(exec, existing, next);
59
67
  },
60
68
  setEnabled(id, enabled) {
61
69
  const existing = readCrontab(exec);
62
70
  const next = toggleBlock(existing, id, enabled);
63
- writeCrontab(exec, next);
71
+ replaceCrontab(exec, existing, next);
64
72
  },
65
73
  list() {
66
74
  const existing = readCrontab(exec);
67
- return listBlocks(existing).map(({ id, body }) => ({ id, signature: normalizeSignature(body) }));
75
+ const refs = [];
76
+ for (const { id, body } of listBlocks(existing)) {
77
+ const installed = extractCronInvocation(body);
78
+ // An invocation that no longer parses (e.g. a pre-0.9 `tasks run`
79
+ // entry surviving the scheduler-ABI respelling, or any other
80
+ // foreign/malformed line between our markers) is an orphan of its
81
+ // marker id, not a hard failure: omit it here so `akmTasksSync`
82
+ // treats the id as "not present" and reinstalls it from the current
83
+ // task file — going through the normal eligibility/`--rebind` gate
84
+ // exactly like a fresh install.
85
+ if (!installed)
86
+ continue;
87
+ refs.push({
88
+ id,
89
+ signature: normalizeSignature(body),
90
+ ...(installed.target !== undefined ? { target: installed.target } : {}),
91
+ binding: installed.binding,
92
+ contextPath: installed.contextPath,
93
+ });
94
+ }
95
+ return refs;
96
+ },
97
+ listForRebind() {
98
+ const existing = readCrontab(exec);
99
+ return listBlocks(existing).map(({ id, body }) => {
100
+ const installed = extractCronInvocation(body);
101
+ return {
102
+ id,
103
+ signature: normalizeSignature(body),
104
+ ...(installed?.target !== undefined ? { target: installed.target } : {}),
105
+ };
106
+ });
68
107
  },
69
- expectedSignature(task) {
70
- const cronLine = buildCronLine(task, akmArgv, logDir);
108
+ expectedSignature(task, opts) {
109
+ const cronLine = buildCronLine(task, [...(opts?.binding ?? akmArgv)], logDir, opts?.contextPath ?? defaultContextPath, opts?.target);
110
+ assertPortableCronLine(cronLine);
71
111
  return normalizeSignature(cronBlockBody(cronLine, task.enabled));
72
112
  },
73
113
  };
74
114
  }
75
115
  // ── helpers (exported for tests) ────────────────────────────────────────────
76
- export function buildCronLine(task, akmArgv, logDir) {
116
+ export function buildCronLine(task, akmArgv, logDir, contextPath, target) {
77
117
  const spec = parseSchedule(task.schedule, "cron");
78
118
  const cronExpr = translateToCron(spec);
79
119
  const logPath = path.join(logDir, `${task.id}.log`);
80
- const cmd = [...akmArgv, "tasks", "run", task.id].map((part) => quoteForCron(part)).join(" ");
120
+ const invocation = buildScheduledTaskInvocation(akmArgv, task.id, contextPath, target);
121
+ const cmd = invocation.argv.map((part) => quoteForCron(part)).join(" ");
81
122
  return `${cronExpr} ${cmd} >> ${quoteForCron(logPath)} 2>&1`;
82
123
  }
83
124
  /** The crontab line as it appears inside a block — commented when disabled. */
@@ -93,28 +134,97 @@ export function renderBlock(id, cronLine, enabled) {
93
134
  * drift signature, and exported for tests.
94
135
  */
95
136
  export function listBlocks(existing) {
137
+ return parseBlocks(existing).map(({ id, body }) => ({ id, body }));
138
+ }
139
+ function parseBlocks(existing) {
96
140
  const out = [];
97
141
  const lines = existing.split(/\r?\n/);
98
142
  let currentId = null;
143
+ let start = -1;
99
144
  let body = [];
100
- for (const line of lines) {
145
+ for (const [index, line] of lines.entries()) {
101
146
  const begin = line.match(BLOCK_RE);
102
147
  if (begin) {
148
+ if (currentId !== null)
149
+ throw malformedBlockError(currentId);
103
150
  currentId = begin[1];
151
+ start = index;
104
152
  body = [];
105
153
  continue;
106
154
  }
107
- if (currentId !== null && line === END(currentId)) {
108
- out.push({ id: currentId, body: body.join("\n") });
155
+ const end = line.match(BLOCK_END_RE);
156
+ if (end) {
157
+ if (currentId === null || end[1] !== currentId)
158
+ throw malformedBlockError(currentId ?? end[1]);
159
+ out.push({ id: currentId, body: body.join("\n"), start, end: index });
109
160
  currentId = null;
161
+ start = -1;
110
162
  body = [];
111
163
  continue;
112
164
  }
113
165
  if (currentId !== null)
114
166
  body.push(line);
115
167
  }
168
+ if (currentId !== null)
169
+ throw malformedBlockError(currentId);
116
170
  return out;
117
171
  }
172
+ function malformedBlockError(id) {
173
+ return new ConfigError(`Crontab contains a malformed akm task block for "${id}"; refusing to modify it.`, "INVALID_CONFIG_FILE");
174
+ }
175
+ /**
176
+ * Recover the bundle name from an installed cron block body by reading the
177
+ * `--bundle <bundle>` token embedded in the scheduled `akm task run …`
178
+ * invocation. Returns undefined for the byte-identical primary/default form
179
+ * (no `--bundle`). Bundle slugs never contain whitespace (config-schema
180
+ * `isBundleSlug`), so the quoted token is a single whitespace-delimited field
181
+ * even when cron-quoted — a plain field split recovers it, and
182
+ * the shell-word parser below reverses the quoting.
183
+ */
184
+ export function extractInstalledTarget(body) {
185
+ return extractCronInvocation(body)?.target;
186
+ }
187
+ export function extractCronInvocation(body) {
188
+ const line = body.startsWith(DISABLED_PREFIX) ? body.slice(DISABLED_PREFIX.length) : body;
189
+ const fields = splitCronShellWords(line);
190
+ if (fields.length < 6)
191
+ return undefined;
192
+ let commandStart = 5;
193
+ while (commandStart < fields.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(fields[commandStart]))
194
+ commandStart += 1;
195
+ const redirectIndex = fields.indexOf(">>", commandStart);
196
+ if (redirectIndex === -1)
197
+ return undefined;
198
+ return parseScheduledTaskArgv(fields.slice(commandStart, redirectIndex));
199
+ }
200
+ /** Reverse {@link quoteForCron} for a single whitespace-free token. */
201
+ function splitCronShellWords(value) {
202
+ const words = [];
203
+ let current = "";
204
+ let quoted = false;
205
+ for (let index = 0; index < value.length; index += 1) {
206
+ const char = value[index];
207
+ if (char === "'") {
208
+ quoted = !quoted;
209
+ continue;
210
+ }
211
+ if (char === "\\" && index + 1 < value.length) {
212
+ current += value[index + 1];
213
+ index += 1;
214
+ continue;
215
+ }
216
+ if (!quoted && /\s/.test(char)) {
217
+ if (current)
218
+ words.push(current);
219
+ current = "";
220
+ continue;
221
+ }
222
+ current += char;
223
+ }
224
+ if (current)
225
+ words.push(current);
226
+ return words;
227
+ }
118
228
  /** Collapse incidental whitespace so signature comparison ignores it. */
119
229
  function normalizeSignature(body) {
120
230
  return body
@@ -131,27 +241,17 @@ export function upsertBlock(existing, id, block) {
131
241
  }
132
242
  export function removeBlock(existing, id) {
133
243
  const lines = existing.split(/\r?\n/);
134
- const out = [];
135
- let inBlock = false;
136
- for (const line of lines) {
137
- if (!inBlock && line === BEGIN(id)) {
138
- inBlock = true;
139
- continue;
140
- }
141
- if (inBlock && line === END(id)) {
142
- inBlock = false;
143
- continue;
144
- }
145
- if (inBlock)
146
- continue;
147
- out.push(line);
148
- }
244
+ const blocks = parseBlocks(existing).filter((block) => block.id === id);
245
+ if (blocks.length === 0)
246
+ return existing;
247
+ const out = lines.filter((_, index) => !blocks.some((block) => index >= block.start && index <= block.end));
149
248
  // Collapse trailing blank lines.
150
249
  while (out.length > 0 && out[out.length - 1] === "")
151
250
  out.pop();
152
251
  return out.join("\n");
153
252
  }
154
253
  export function toggleBlock(existing, id, enabled) {
254
+ parseBlocks(existing);
155
255
  const lines = existing.split(/\r?\n/);
156
256
  const out = [];
157
257
  let inBlock = false;
@@ -184,12 +284,29 @@ export function toggleBlock(existing, id, enabled) {
184
284
  return out.join("\n");
185
285
  }
186
286
  function quoteForCron(part) {
287
+ assertCronValue(part);
187
288
  // crontab passes the rest of the line to /bin/sh -c, so quote anything that
188
289
  // isn't a plain shell-safe token. Single-quote and escape embedded single
189
- // quotes via the standard shell idiom: `'foo'\''bar'`.
290
+ // quotes via the standard shell idiom: `'foo'\''bar'`. Cron interprets `%`
291
+ // before the shell, even inside quotes, so close the quote around its escape.
190
292
  if (/^[A-Za-z0-9_\-./@:%=+,]+$/.test(part))
191
- return part;
192
- return `'${part.replace(/'/g, `'\\''`)}'`;
293
+ return part.replaceAll("%", "\\%");
294
+ return `'${part.replace(/'/g, `'\\''`).replace(/%/g, `'\\%'`)}'`;
295
+ }
296
+ function assertCronValue(value) {
297
+ for (const char of value) {
298
+ const code = char.codePointAt(0) ?? 0;
299
+ if (code < 0x20 || (code >= 0x7f && code <= 0x9f)) {
300
+ throw new ConfigError("Cron values must not contain control characters.", "INVALID_CONFIG_FILE");
301
+ }
302
+ }
303
+ return value;
304
+ }
305
+ function assertPortableCronLine(line) {
306
+ const bytes = Buffer.byteLength(line, "utf8");
307
+ if (bytes > PORTABLE_CRON_LINE_LIMIT) {
308
+ throw new ConfigError(`Generated cron definition is ${bytes} bytes; portable cron command lines are limited to ${PORTABLE_CRON_LINE_LIMIT} bytes.`, "INVALID_CONFIG_FILE");
309
+ }
193
310
  }
194
311
  function readCrontab(exec) {
195
312
  const result = exec.read();
@@ -204,18 +321,24 @@ function readCrontab(exec) {
204
321
  }
205
322
  function writeCrontab(exec, content) {
206
323
  const normalised = content.endsWith("\n") || content.length === 0 ? content : `${content}\n`;
207
- const result = exec.write(normalised);
208
- if (result.status !== 0) {
209
- throw new ConfigError(`crontab - failed (exit ${result.status}): ${result.stderr || result.stdout || "no output"}.`, "INVALID_CONFIG_FILE", "Ensure the `crontab` binary is on PATH and your shell can write the user crontab.");
210
- }
324
+ throwIfNotOk(exec.write(normalised), {
325
+ message: (r) => `crontab - failed (exit ${r.status}): ${r.stderr || r.stdout || "no output"}.`,
326
+ hint: "Ensure the `crontab` binary is on PATH and your shell can write the user crontab.",
327
+ });
211
328
  }
212
- function ensureDir(dir) {
329
+ function replaceCrontab(exec, existing, next) {
213
330
  try {
214
- fs.mkdirSync(dir, { recursive: true });
331
+ writeCrontab(exec, next);
215
332
  }
216
- catch {
217
- // Best-effort: the install will surface a clearer error if the cron
218
- // line later fails at runtime due to a missing redirection target.
333
+ catch (err) {
334
+ try {
335
+ writeCrontab(exec, existing);
336
+ }
337
+ catch (rollbackError) {
338
+ const message = err instanceof Error ? err.message : String(err);
339
+ throw new AggregateError([err, rollbackError], `${message}; restoring the prior crontab also failed.`);
340
+ }
341
+ throw err;
219
342
  }
220
343
  }
221
344
  function defaultCronExec() {
@@ -3,6 +3,7 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import { spawnSync } from "node:child_process";
5
5
  import fs from "node:fs";
6
+ import { ConfigError } from "../../core/errors.js";
6
7
  /**
7
8
  * Default exec strategy: run commands synchronously via {@link spawnCommand}.
8
9
  *
@@ -40,13 +41,85 @@ export function nodeFs() {
40
41
  */
41
42
  export function spawnCommand(args) {
42
43
  const [bin, ...rest] = args;
43
- const r = spawnSync(bin, rest, { encoding: "utf8" });
44
+ const r = spawnSync(bin, rest);
44
45
  return {
45
46
  status: r.status ?? 1,
46
- stdout: r.stdout ?? "",
47
- stderr: r.stderr ?? "",
47
+ stdout: decodeCommandOutput(r.stdout),
48
+ stderr: decodeCommandOutput(r.stderr),
48
49
  };
49
50
  }
51
+ /**
52
+ * Throw a `ConfigError` (code `INVALID_CONFIG_FILE`) when `result` doesn't
53
+ * satisfy `isOk` (default: `status === 0`); otherwise return `result`
54
+ * unchanged. Factors out the "run a native scheduler command, throw a
55
+ * formatted error on a bad exit" idiom repeated across the cron/launchd/
56
+ * schtasks backends — NOT the accumulate-into-`rollbackErrors`-and-continue
57
+ * idiom used during rollback, which stays inline (throwing there would
58
+ * change control flow: rollback needs to keep trying its remaining steps).
59
+ */
60
+ export function throwIfNotOk(result, options) {
61
+ const ok = options.isOk ? options.isOk(result) : result.status === 0;
62
+ if (!ok) {
63
+ throw new ConfigError(options.message(result), "INVALID_CONFIG_FILE", options.hint);
64
+ }
65
+ return result;
66
+ }
67
+ /** {@link throwIfNotOk} for the common case of running `args` via `exec` first. */
68
+ export function runOrThrow(exec, args, options) {
69
+ return throwIfNotOk(exec.run(args), options);
70
+ }
71
+ /** Decode native command output, including the UTF-16 XML emitted by schtasks. */
72
+ export function decodeCommandOutput(output) {
73
+ if (output === null || output === undefined)
74
+ return "";
75
+ if (typeof output === "string")
76
+ return output.replace(/^\uFEFF/, "");
77
+ if (output.length === 0)
78
+ return "";
79
+ if (output[0] === 0xef && output[1] === 0xbb && output[2] === 0xbf) {
80
+ return output.toString("utf8", 3);
81
+ }
82
+ if (output[0] === 0xff && output[1] === 0xfe) {
83
+ return output.subarray(2).toString("utf16le");
84
+ }
85
+ if (output[0] === 0xfe && output[1] === 0xff) {
86
+ return decodeUtf16Be(output.subarray(2));
87
+ }
88
+ const pairs = Math.floor(output.length / 2);
89
+ if (pairs > 1) {
90
+ let evenNuls = 0;
91
+ let oddNuls = 0;
92
+ for (let i = 0; i < pairs * 2; i += 2) {
93
+ if (output[i] === 0)
94
+ evenNuls += 1;
95
+ if (output[i + 1] === 0)
96
+ oddNuls += 1;
97
+ }
98
+ if (oddNuls / pairs > 0.6)
99
+ return output.toString("utf16le");
100
+ if (evenNuls / pairs > 0.6)
101
+ return decodeUtf16Be(output);
102
+ }
103
+ return output.toString("utf8");
104
+ }
105
+ /** Return XML with the declaration required by Task Scheduler's UTF-16 input. */
106
+ export function normalizeXmlForUtf16File(xml) {
107
+ const source = xml.replace(/^\uFEFF/, "");
108
+ const declaration = '<?xml version="1.0" encoding="UTF-16"?>';
109
+ if (/^<\?xml\b[^?]*\?>/i.test(source)) {
110
+ return source.replace(/^<\?xml\b[^?]*\?>/i, declaration);
111
+ }
112
+ return `${declaration}\n${source}`;
113
+ }
114
+ function decodeUtf16Be(output) {
115
+ const evenLength = output.length - (output.length % 2);
116
+ const swapped = Buffer.allocUnsafe(evenLength);
117
+ for (let i = 0; i < evenLength; i += 2) {
118
+ swapped[i] = output[i + 1];
119
+ swapped[i + 1] = output[i];
120
+ }
121
+ return swapped.toString("utf16le");
122
+ }
50
123
  /**
51
124
  * Escape a string for safe embedding in an XML attribute or text node.
52
125
  */
@@ -4,14 +4,13 @@
4
4
  import { CRON_BACKEND } from "./cron.js";
5
5
  import { LAUNCHD_BACKEND } from "./launchd.js";
6
6
  import { SCHTASKS_BACKEND } from "./schtasks.js";
7
- let backendsOverrides;
8
- /** TEST-ONLY. Swap backend selection; pass undefined to restore the real implementations. */
9
- export function _setBackendsForTests(fakes) {
10
- backendsOverrides = fakes;
11
- }
7
+ // WI-9.10e: the former `_setBackendsForTests` module-mutation seam was retired.
8
+ // Tests inject a fake `TaskBackend` directly via the `deps.backend` parameter
9
+ // the `akm task` mutation entries already accept (the backend carries its own
10
+ // `name`), so no module-level override binding is needed. `selectBackend`'s
11
+ // `options.platform` covers the platform-steering the seam's second override
12
+ // used to provide.
12
13
  export function selectBackend(options = {}) {
13
- if (backendsOverrides?.selectBackend)
14
- return backendsOverrides.selectBackend(options);
15
14
  const platform = options.platform ?? process.platform;
16
15
  switch (platform) {
17
16
  case "win32":
@@ -23,8 +22,6 @@ export function selectBackend(options = {}) {
23
22
  }
24
23
  }
25
24
  export function backendNameForPlatform(platform = process.platform) {
26
- if (backendsOverrides?.backendNameForPlatform)
27
- return backendsOverrides.backendNameForPlatform(platform);
28
25
  if (platform === "win32")
29
26
  return "schtasks";
30
27
  if (platform === "darwin")