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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/CHANGELOG.md +1770 -44
  2. package/README.md +38 -47
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +499 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +74 -89
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +148 -88
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/llm-usage.js +2 -1
  57. package/dist/commands/health/md-report.js +5 -9
  58. package/dist/commands/health/metrics.js +62 -20
  59. package/dist/commands/health/renderers.js +47 -0
  60. package/dist/commands/health/report-view-model.js +4 -5
  61. package/dist/commands/health/stash-exposure.js +1 -1
  62. package/dist/commands/health/surfaces.js +4 -122
  63. package/dist/commands/health/task-runs.js +3 -67
  64. package/dist/commands/health/types-improve.js +7 -0
  65. package/dist/commands/health.js +160 -35
  66. package/dist/commands/improve/anti-collapse.js +2 -2
  67. package/dist/commands/improve/autonomy-gate.js +68 -0
  68. package/dist/commands/improve/collapse-detector.js +41 -40
  69. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  70. package/dist/commands/improve/consolidate/merge.js +4 -0
  71. package/dist/commands/improve/consolidate.js +140 -1000
  72. package/dist/commands/improve/distill/promote-memory.js +12 -12
  73. package/dist/commands/improve/distill/quality-gate.js +11 -6
  74. package/dist/commands/improve/distill.js +58 -69
  75. package/dist/commands/improve/eligibility.js +101 -55
  76. package/dist/commands/improve/extract-cli.js +14 -133
  77. package/dist/commands/improve/improve-cli.js +98 -114
  78. package/dist/commands/improve/improve-result-file.js +1 -28
  79. package/dist/commands/improve/improve-strategies.js +8 -5
  80. package/dist/commands/improve/improve.js +254 -101
  81. package/dist/commands/improve/locks.js +7 -0
  82. package/dist/commands/improve/loop-stages.js +182 -20
  83. package/dist/commands/improve/memory/derived-ref.js +45 -43
  84. package/dist/commands/improve/memory/memory-belief.js +1 -1
  85. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  86. package/dist/commands/improve/memory/memory-improve.js +14 -5
  87. package/dist/commands/improve/outcome-loop.js +22 -65
  88. package/dist/commands/improve/preparation.js +114 -123
  89. package/dist/commands/improve/proactive-maintenance.js +2 -5
  90. package/dist/commands/improve/reflect.js +56 -160
  91. package/dist/commands/improve/run-context.js +5 -0
  92. package/dist/commands/improve/salience.js +11 -122
  93. package/dist/commands/improve/session-asset.js +4 -0
  94. package/dist/commands/improve/source-identity.js +10 -38
  95. package/dist/commands/lint/base-linter.js +51 -131
  96. package/dist/commands/lint/env-key-rules.js +31 -47
  97. package/dist/commands/lint/index.js +444 -88
  98. package/dist/commands/lint/types.js +22 -1
  99. package/dist/commands/{events.js → log.js} +33 -38
  100. package/dist/commands/migrate-cli.js +92 -12
  101. package/dist/commands/migration-tool.js +46 -0
  102. package/dist/commands/observability-cli.js +70 -209
  103. package/dist/commands/proposal/drain.js +101 -29
  104. package/dist/commands/proposal/proposal-cli.js +76 -48
  105. package/dist/commands/proposal/proposal.js +54 -18
  106. package/dist/commands/proposal/propose-cli.js +88 -0
  107. package/dist/commands/proposal/propose.js +23 -15
  108. package/dist/commands/proposal/repository.js +718 -279
  109. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  110. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  111. package/dist/commands/proposal/validators/proposals.js +4 -7
  112. package/dist/commands/read/curate.js +34 -53
  113. package/dist/commands/read/knowledge.js +150 -95
  114. package/dist/commands/read/registry-search.js +2 -2
  115. package/dist/commands/read/remember-cli.js +42 -15
  116. package/dist/commands/read/search-cli.js +180 -78
  117. package/dist/commands/read/search.js +58 -43
  118. package/dist/commands/read/show.js +197 -141
  119. package/dist/commands/registry-cli.js +12 -51
  120. package/dist/commands/remember.js +14 -57
  121. package/dist/commands/sources/add-cli.js +100 -31
  122. package/dist/commands/sources/bundle-cli.js +166 -0
  123. package/dist/commands/sources/bundle-config-ops.js +7 -2
  124. package/dist/commands/sources/info.js +30 -7
  125. package/dist/commands/sources/init.js +12 -12
  126. package/dist/commands/sources/installed-stashes.js +387 -98
  127. package/dist/commands/sources/schema-repair.js +3 -2
  128. package/dist/commands/sources/self-update.js +131 -38
  129. package/dist/commands/sources/source-add.js +72 -17
  130. package/dist/commands/sources/source-clone.js +129 -45
  131. package/dist/commands/sources/source-manage.js +43 -23
  132. package/dist/commands/sources/sources-cli.js +57 -208
  133. package/dist/commands/sources/stash-cli.js +46 -53
  134. package/dist/commands/tasks/tasks-cli.js +91 -97
  135. package/dist/commands/tasks/tasks.js +284 -423
  136. package/dist/commands/workflow-cli.js +181 -450
  137. package/dist/core/abort-deadline.js +28 -0
  138. package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
  139. package/dist/core/adapter/adapters/akm-adapter.js +60 -38
  140. package/dist/core/adapter/adapters/akm-lint.js +109 -42
  141. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  142. package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
  143. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  144. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  145. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  146. package/dist/core/adapter/adapters/index.js +6 -6
  147. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  148. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  149. package/dist/core/adapter/adapters/shared.js +3 -19
  150. package/dist/core/adapter/adapters/tool-dir-shared.js +13 -6
  151. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  152. package/dist/core/adapter/detect-adapter.js +17 -0
  153. package/dist/core/adapter/recognize-match.js +6 -4
  154. package/dist/core/adapter/validate-context.js +214 -0
  155. package/dist/core/asset/akm-markdown.js +63 -0
  156. package/dist/core/asset/asset-placement.js +20 -6
  157. package/dist/core/asset/asset-ref.js +11 -9
  158. package/dist/core/asset/frontmatter-lint.js +30 -0
  159. package/dist/core/asset/frontmatter.js +29 -9
  160. package/dist/core/asset/markdown.js +40 -51
  161. package/dist/core/asset/resolve-ref.js +89 -18
  162. package/dist/core/asset/stash-meta.js +1 -1
  163. package/dist/core/bundle-id.js +51 -0
  164. package/dist/core/common.js +218 -44
  165. package/dist/core/concurrent.js +32 -0
  166. package/dist/core/config/config-io.js +12 -1
  167. package/dist/core/config/config-schema.js +35 -8
  168. package/dist/core/config/config-sources.js +55 -11
  169. package/dist/core/config/config-walker.js +25 -9
  170. package/dist/core/config/config.js +9 -48
  171. package/dist/core/config/experimental.js +21 -0
  172. package/dist/core/config/schema/embedding.js +5 -1
  173. package/dist/core/config/schema/experimental.js +30 -0
  174. package/dist/core/config/schema/improve-processes.js +0 -6
  175. package/dist/core/config/schema/improve.js +21 -3
  176. package/dist/core/config/schema/index-config.js +8 -15
  177. package/dist/core/config/schema/output.js +4 -1
  178. package/dist/core/config/schema/setup.js +9 -18
  179. package/dist/core/config/schema/sources-bundles.js +49 -33
  180. package/dist/core/config/schema/workflow.js +14 -3
  181. package/dist/core/env-secret-ref.js +76 -46
  182. package/dist/core/errors.js +43 -12
  183. package/dist/core/events.js +76 -152
  184. package/dist/core/file-change.js +6 -5
  185. package/dist/core/file-lock.js +7 -1
  186. package/dist/core/fs-txn.js +83 -7
  187. package/dist/core/git-message.js +2 -2
  188. package/dist/core/improve-result.js +6 -100
  189. package/dist/core/json-schema.js +327 -9
  190. package/dist/core/lesson-lint.js +1 -17
  191. package/dist/core/logs-db.js +2 -1
  192. package/dist/core/loopback.js +89 -0
  193. package/dist/core/migration-operation.js +33 -2
  194. package/dist/core/mutation-target.js +78 -0
  195. package/dist/core/parse.js +4 -1
  196. package/dist/core/path-access.js +107 -0
  197. package/dist/core/paths.js +17 -20
  198. package/dist/core/recognition-util.js +12 -14
  199. package/dist/core/redaction.js +98 -17
  200. package/dist/core/spawn-env.js +234 -0
  201. package/dist/core/standards/resolve-standards-context.js +2 -14
  202. package/dist/core/standards/resolve-stash-standards.js +2 -2
  203. package/dist/core/standards/resolve-type-conventions.js +2 -2
  204. package/dist/core/state/migrations.js +41 -18
  205. package/dist/core/state-db-scope.js +134 -0
  206. package/dist/core/state-db.js +5 -14
  207. package/dist/core/structured.js +1 -1
  208. package/dist/core/subprocess.js +182 -36
  209. package/dist/core/text-truncation.js +9 -5
  210. package/dist/core/type-presentation.js +3 -3
  211. package/dist/core/warn.js +0 -3
  212. package/dist/core/write-provenance.js +85 -0
  213. package/dist/core/write-source.js +778 -95
  214. package/dist/indexer/bundle-identity-guard.js +3 -2
  215. package/dist/indexer/db/graph-db.js +17 -30
  216. package/dist/indexer/ensure-index.js +11 -3
  217. package/dist/indexer/graph/graph-boost.js +9 -34
  218. package/dist/indexer/graph/graph-extraction.js +8 -5
  219. package/dist/indexer/index-writer-lock.js +53 -17
  220. package/dist/indexer/index-written-assets.js +33 -24
  221. package/dist/indexer/indexer.js +551 -253
  222. package/dist/indexer/installations.js +14 -96
  223. package/dist/indexer/passes/dir-staleness.js +16 -9
  224. package/dist/indexer/passes/memory-inference.js +15 -9
  225. package/dist/indexer/passes/metadata.js +113 -47
  226. package/dist/indexer/scan/doc-to-entry.js +38 -1
  227. package/dist/indexer/scan/drain-dir.js +13 -23
  228. package/dist/indexer/search/db-search.js +107 -69
  229. package/dist/indexer/search/fts-query.js +47 -24
  230. package/dist/indexer/search/ranking-contributors.js +42 -20
  231. package/dist/indexer/search/ranking.js +18 -99
  232. package/dist/indexer/search/search-fields.js +7 -2
  233. package/dist/indexer/search/search-source.js +82 -93
  234. package/dist/indexer/usage/usage-events.js +0 -89
  235. package/dist/indexer/walk/file-context.js +2 -1
  236. package/dist/indexer/walk/matchers.js +30 -43
  237. package/dist/indexer/walk/path-resolver.js +7 -2
  238. package/dist/indexer/walk/walker.js +38 -12
  239. package/dist/integrations/agent/builders.js +0 -6
  240. package/dist/integrations/agent/config.js +2 -2
  241. package/dist/integrations/agent/detect.js +49 -19
  242. package/dist/integrations/agent/engine-fallback.js +76 -0
  243. package/dist/integrations/agent/engine-resolution.js +24 -11
  244. package/dist/integrations/agent/model-aliases.js +1 -1
  245. package/dist/integrations/agent/profiles.js +23 -1
  246. package/dist/integrations/agent/prompts.js +12 -8
  247. package/dist/integrations/agent/runner-dispatch.js +4 -2
  248. package/dist/integrations/agent/runner.js +0 -1
  249. package/dist/integrations/agent/spawn.js +20 -93
  250. package/dist/integrations/github.js +1 -1
  251. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  252. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  253. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  254. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  255. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  256. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  257. package/dist/integrations/harnesses/index.js +3 -7
  258. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  259. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  260. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  261. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  262. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  263. package/dist/integrations/lockfile.js +132 -8
  264. package/dist/integrations/session-logs/index.js +3 -28
  265. package/dist/llm/client.js +136 -100
  266. package/dist/llm/embedders/remote.js +13 -5
  267. package/dist/llm/feature-gate.js +4 -12
  268. package/dist/llm/graph-extract.js +5 -11
  269. package/dist/llm/memory-infer.js +144 -1
  270. package/dist/llm/metadata-enhance.js +5 -7
  271. package/dist/llm/structured-call.js +1 -1
  272. package/dist/llm/usage-persist.js +26 -5
  273. package/dist/llm/usage-telemetry.js +25 -2
  274. package/dist/output/cli-hints.js +1 -2
  275. package/dist/output/context.js +22 -7
  276. package/dist/output/format-exempt.js +80 -0
  277. package/dist/output/generic-render.js +259 -0
  278. package/dist/output/render-registry.js +57 -0
  279. package/dist/output/renderers.js +14 -36
  280. package/dist/output/shapes/curate.js +10 -1
  281. package/dist/output/shapes/events.js +12 -7
  282. package/dist/output/shapes/helpers.js +56 -83
  283. package/dist/output/shapes/migrate.js +8 -0
  284. package/dist/output/shapes/passthrough.js +7 -41
  285. package/dist/output/shapes/proposal/producer.js +15 -7
  286. package/dist/output/shapes.js +2 -9
  287. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  288. package/dist/output/text/bundle-show.js +7 -0
  289. package/dist/output/text/command-format.js +164 -96
  290. package/dist/output/text/env.js +1 -3
  291. package/dist/output/text/events.js +8 -7
  292. package/dist/output/text/health-format.js +103 -0
  293. package/dist/output/text/health.js +7 -0
  294. package/dist/output/text/helpers.js +10 -8
  295. package/dist/output/text/lint-format.js +56 -0
  296. package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
  297. package/dist/output/text/migrate.js +88 -0
  298. package/dist/output/text/proposal/producer.js +4 -2
  299. package/dist/output/text/proposal-format.js +44 -72
  300. package/dist/output/text/registry-commands.js +1 -2
  301. package/dist/output/text/show-directives.js +15 -7
  302. package/dist/output/text/status-list.js +32 -0
  303. package/dist/output/text/{save.js → sync.js} +2 -2
  304. package/dist/output/text/workflow-format.js +24 -203
  305. package/dist/output/text/workflow.js +1 -7
  306. package/dist/output/text.js +16 -17
  307. package/dist/registry/factory.js +4 -6
  308. package/dist/registry/origin-resolve.js +16 -27
  309. package/dist/registry/providers/skills-sh.js +3 -3
  310. package/dist/registry/providers/static-index.js +13 -23
  311. package/dist/registry/resolve.js +42 -7
  312. package/dist/registry/semver.js +34 -84
  313. package/dist/runtime.js +2 -23
  314. package/dist/scripts/akm-migrate-node.js +60972 -0
  315. package/dist/scripts/akm-migrate.js +60310 -0
  316. package/dist/setup/detect.js +42 -15
  317. package/dist/setup/registry-stash-loader.js +2 -2
  318. package/dist/setup/setup.js +236 -136
  319. package/dist/setup/steps/connection.js +7 -9
  320. package/dist/setup/steps/platforms.js +9 -9
  321. package/dist/setup/steps/semantic.js +15 -3
  322. package/dist/setup/steps/sources.js +12 -13
  323. package/dist/setup/steps/stashdir.js +2 -3
  324. package/dist/setup/steps/tasks.js +237 -120
  325. package/dist/sources/freshness.js +1 -1
  326. package/dist/sources/provider-factory.js +11 -17
  327. package/dist/sources/providers/filesystem.js +2 -3
  328. package/dist/sources/providers/git-install.js +278 -34
  329. package/dist/sources/providers/git-provider.js +25 -23
  330. package/dist/sources/providers/git-stash.js +414 -106
  331. package/dist/sources/providers/git.js +2 -2
  332. package/dist/sources/providers/npm.js +16 -19
  333. package/dist/sources/providers/provider-utils.js +7 -4
  334. package/dist/sources/providers/sync-from-ref.js +3 -9
  335. package/dist/sources/providers/website.js +6 -1
  336. package/dist/sources/resolve.js +6 -5
  337. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  338. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  339. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  340. package/dist/sources/snapshot-fetchers/github.js +100 -0
  341. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  342. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  343. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  344. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  345. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  346. package/dist/sources/snapshot-fetchers/website-ingest.js +686 -282
  347. package/dist/sources/snapshot-fetchers/x.js +910 -0
  348. package/dist/storage/database.js +13 -1
  349. package/dist/storage/engines/sqlite-migrations.js +23 -111
  350. package/dist/storage/managed-db.js +21 -2
  351. package/dist/storage/repositories/canaries-repository.js +1 -1
  352. package/dist/storage/repositories/events-repository.js +27 -11
  353. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  354. package/dist/storage/repositories/index-connection.js +44 -8
  355. package/dist/storage/repositories/index-entries-repository.js +157 -241
  356. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  357. package/dist/storage/repositories/index-fts-repository.js +5 -2
  358. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  359. package/dist/storage/repositories/index-meta-repository.js +2 -3
  360. package/dist/storage/repositories/index-schema.js +10 -25
  361. package/dist/storage/repositories/index-utility-repository.js +15 -28
  362. package/dist/storage/repositories/index-vec-repository.js +6 -1
  363. package/dist/storage/repositories/outcome-repository.js +119 -0
  364. package/dist/storage/repositories/proposals-repository.js +296 -59
  365. package/dist/storage/repositories/registry-cache.js +19 -0
  366. package/dist/storage/repositories/salience-repository.js +172 -0
  367. package/dist/storage/repositories/task-history-repository.js +15 -13
  368. package/dist/storage/repositories/workflow-runs-repository.js +118 -53
  369. package/dist/tasks/backends/cron.js +105 -15
  370. package/dist/tasks/backends/index.js +1 -1
  371. package/dist/tasks/backends/launchd.js +85 -38
  372. package/dist/tasks/backends/schtasks.js +135 -15
  373. package/dist/tasks/embedded.js +56 -40
  374. package/dist/tasks/log-redaction.js +156 -0
  375. package/dist/tasks/parser.js +87 -160
  376. package/dist/tasks/resolve-akm-bin.js +137 -59
  377. package/dist/tasks/runner.js +209 -44
  378. package/dist/tasks/scheduler-invocation.js +220 -10
  379. package/dist/tasks/schema.js +89 -1
  380. package/dist/tasks/task-id.js +1 -3
  381. package/dist/tasks/validator.js +20 -6
  382. package/dist/workflows/authoring/authoring.js +94 -143
  383. package/dist/workflows/authoring/scope-key.js +1 -1
  384. package/dist/workflows/concurrency-policy.js +95 -1
  385. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  386. package/dist/workflows/exec/exec-unit.js +542 -0
  387. package/dist/workflows/exec/frozen-judge.js +130 -32
  388. package/dist/workflows/exec/native-executor.js +513 -274
  389. package/dist/workflows/exec/param-secrets.js +12 -11
  390. package/dist/workflows/exec/run-workflow.js +496 -237
  391. package/dist/workflows/exec/step-work.js +616 -412
  392. package/dist/workflows/exec/unit-dispatch.js +34 -0
  393. package/dist/workflows/exec/unit-writer.js +53 -13
  394. package/dist/workflows/exec/worktree.js +454 -41
  395. package/dist/workflows/ir/compile.js +166 -271
  396. package/dist/workflows/ir/freeze.js +119 -42
  397. package/dist/workflows/ir/params.js +135 -11
  398. package/dist/workflows/ir/plan-hash.js +1 -1
  399. package/dist/workflows/ir/schema.js +129 -45
  400. package/dist/workflows/parser.js +1070 -307
  401. package/dist/workflows/program/expressions.js +20 -208
  402. package/dist/workflows/program/schema.js +31 -10
  403. package/dist/workflows/renderer.js +123 -68
  404. package/dist/workflows/resource-limits.js +184 -0
  405. package/dist/workflows/runtime/checkin.js +3 -3
  406. package/dist/workflows/runtime/plan-classifier.js +16 -75
  407. package/dist/workflows/runtime/runs.js +331 -132
  408. package/dist/workflows/runtime/unit-checkin.js +1 -1
  409. package/dist/workflows/runtime/unit-phases.js +2 -2
  410. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  411. package/dist/workflows/schema.js +1 -11
  412. package/dist/workflows/validate-summary.js +47 -38
  413. package/dist/workflows/validator.js +21 -62
  414. package/docs/README.md +110 -0
  415. package/docs/migration/README.md +8 -0
  416. package/docs/migration/release-notes/0.7.0.md +11 -11
  417. package/docs/migration/release-notes/0.9.0.md +209 -27
  418. package/docs/migration/v0.7-to-v0.8.md +48 -48
  419. package/docs/migration/v0.8-to-v0.9.md +567 -209
  420. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  421. package/docs/reference/README.md +19 -0
  422. package/docs/reference/bundle-types.md +19 -0
  423. package/docs/reference/cli.md +2342 -0
  424. package/docs/reference/configuration.md +371 -0
  425. package/docs/reference/data-and-telemetry.md +129 -46
  426. package/docs/reference/supported-formats.md +50 -0
  427. package/docs/reference/workflow-schema.md +1014 -0
  428. package/docs/reference/workflows.md +51 -0
  429. package/package.json +31 -13
  430. package/schemas/akm-asset-envelope.json +93 -0
  431. package/schemas/akm-config.json +89 -128
  432. package/schemas/akm-task.json +27 -5
  433. package/schemas/akm-workflow.json +157 -81
  434. package/dist/assets/tasks/core/backup.yml +0 -5
  435. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  436. package/dist/cli/config-migrate.js +0 -1878
  437. package/dist/cli/config-validate.js +0 -41
  438. package/dist/commands/backup-cli.js +0 -56
  439. package/dist/commands/bundle/bundle-cli.js +0 -68
  440. package/dist/commands/bundle/bundle.js +0 -219
  441. package/dist/commands/graph/graph-cli.js +0 -124
  442. package/dist/commands/graph/graph.js +0 -489
  443. package/dist/commands/improve/extract-watch.js +0 -140
  444. package/dist/commands/mv-cli.js +0 -1221
  445. package/dist/commands/sources/history.js +0 -201
  446. package/dist/commands/tasks/default-tasks.js +0 -186
  447. package/dist/core/migration-backup.js +0 -1231
  448. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  449. package/dist/llm/memory-infer-impl.js +0 -138
  450. package/dist/migrate/legacy/config-source-migration.js +0 -257
  451. package/dist/migrate/legacy/content-migration.js +0 -350
  452. package/dist/migrate/legacy/legacy-layout.js +0 -779
  453. package/dist/migrate/legacy/legacy-paths.js +0 -25
  454. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  455. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  456. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  457. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  458. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  459. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  460. package/dist/migrate/legacy-ref-grammar.js +0 -214
  461. package/dist/output/shapes/distill.js +0 -14
  462. package/dist/output/shapes/history.js +0 -11
  463. package/dist/output/text/distill.js +0 -6
  464. package/dist/output/text/enable-disable.js +0 -8
  465. package/dist/output/text/history.js +0 -6
  466. package/dist/registry/build-index.js +0 -382
  467. package/dist/schemas/akm-config.json +0 -4704
  468. package/dist/schemas/akm-task.json +0 -87
  469. package/dist/schemas/akm-workflow.json +0 -372
  470. package/dist/scripts/migrate-storage.js +0 -3816
  471. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  472. package/dist/workflows/cli.js +0 -53
  473. package/dist/workflows/exec/brief.js +0 -481
  474. package/dist/workflows/exec/report.js +0 -1460
  475. package/dist/workflows/exec/watch.js +0 -116
  476. package/dist/workflows/program/parser.js +0 -813
  477. package/dist/workflows/program/project.js +0 -104
@@ -2,7 +2,7 @@
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
4
  /**
5
- * `akm tasks` — register, inspect, run, and remove scheduled task assets.
5
+ * `akm task` — register, inspect, run, and remove scheduled task assets.
6
6
  *
7
7
  * Each handler exported here is a pure function that performs the real work;
8
8
  * `src/cli.ts` wraps these in citty `defineCommand`s and shapes their return
@@ -15,18 +15,20 @@ import { assetPathForName } from "../../core/asset/asset-placement.js";
15
15
  import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
16
16
  import { isWithin, resolveStashDir } from "../../core/common.js";
17
17
  import { loadConfig } from "../../core/config/config.js";
18
+ import { IMPROVE_AUTONOMY_CONFIG_KEY, isImproveAutonomyEnabled } from "../../core/config/experimental.js";
18
19
  import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
19
20
  import { getTaskHistoryDir, getTaskLogDir } from "../../core/paths.js";
20
- import { commitWriteTargetBoundary, deleteAssetFromSource, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
21
- import { resolveAssetPath } from "../../sources/resolve.js";
21
+ import { commitWriteTargetBoundary, deleteAssetFromSource, prepareWriteTargetForMutation, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
22
+ import { withEngineFallback } from "../../integrations/agent/engine-fallback.js";
22
23
  import { backendNameForPlatform, selectBackend } from "../../tasks/backends/index.js";
23
- import { findBareAkmExecutableIndex } from "../../tasks/command-executable.js";
24
24
  import { parseTaskDocument } from "../../tasks/parser.js";
25
25
  import { resolveAkmInvocation } from "../../tasks/resolve-akm-bin.js";
26
26
  import { exitCodeForStatus, INVALID_TASK_ATTEMPT_ID, readTaskHistory, recordTaskAttemptFailure, runTask, } from "../../tasks/runner.js";
27
- import { parseSchedule, SCHEDULE_SUPPORTED_SUBSET_HINT, translateToCron } from "../../tasks/schedule.js";
27
+ import { parseSchedule, SCHEDULE_SUPPORTED_SUBSET_HINT } from "../../tasks/schedule.js";
28
+ import { schedulerContextDescriptor, validateSchedulerContextDescriptor, writeSchedulerContextDescriptor, } from "../../tasks/scheduler-invocation.js";
28
29
  import { normaliseTaskId } from "../../tasks/task-id.js";
29
30
  import { validateTaskDocument } from "../../tasks/validator.js";
31
+ import { applyAutonomyGate, configuredDirectAutonomyLanes, describeGatedLanes } from "../improve/autonomy-gate.js";
30
32
  import { resolveImproveStrategy } from "../improve/improve-strategies.js";
31
33
  export async function akmTasksAdd(input, deps = {}) {
32
34
  const id = normaliseTaskId(input.id);
@@ -38,8 +40,12 @@ export async function akmTasksAdd(input, deps = {}) {
38
40
  if (targetCount !== 1) {
39
41
  throw new UsageError("Pass exactly one of --workflow <ref>, --prompt <asset-ref|./file.md|text>, or --command <shell-command>.", "INVALID_FLAG_VALUE");
40
42
  }
41
- if (input.workflow && (input.engine !== undefined || input.model !== undefined || input.timeoutMs !== undefined)) {
42
- throw new UsageError("Workflow tasks accept only --params; engine, model, and timeout are prompt-task fields.", "INVALID_FLAG_VALUE");
43
+ // `--timeout-ms` IS valid on a workflow task: it is the whole-run bound the
44
+ // task runner turns into an abort signal (issue 11), the same one
45
+ // `akm workflow run --timeout` applies interactively. Engine and model stay
46
+ // prompt-only — a workflow's engines come from its frozen plan.
47
+ if (input.workflow && (input.engine !== undefined || input.model !== undefined)) {
48
+ throw new UsageError("Workflow tasks accept --params and --timeout-ms; engine and model are prompt-task fields.", "INVALID_FLAG_VALUE");
43
49
  }
44
50
  if (hasCommand && (input.engine !== undefined || input.model !== undefined)) {
45
51
  throw new UsageError("Command tasks accept --timeout-ms but not --engine or --model.", "INVALID_FLAG_VALUE");
@@ -49,15 +55,22 @@ export async function akmTasksAdd(input, deps = {}) {
49
55
  // from `deps.backend` when present — retiring the `_setBackendsForTests` seam.
50
56
  const backend = deps.backend?.name ?? backendNameForPlatform();
51
57
  parseSchedule(input.schedule, backend);
52
- const target = resolveTaskWriteTarget();
53
- const stashDir = target.source.path;
58
+ const bundle = resolveTaskBundle(input.target, { requireWritable: true });
59
+ const writeTarget = bundle.resolved;
60
+ const stashDir = bundle.stashDir;
61
+ const installOpts = bundle.installTarget !== undefined ? { target: bundle.installTarget } : undefined;
54
62
  const typeRoot = path.join(stashDir, "tasks");
55
63
  const assetPath = assetPathForName("task", typeRoot, id);
56
64
  if (!isWithin(assetPath, typeRoot)) {
57
65
  throw new UsageError(`Resolved task path escapes the stash: "${id}".`, "PATH_ESCAPE_VIOLATION");
58
66
  }
59
- if (fs.existsSync(assetPath) && !input.force) {
60
- throw new UsageError(`Task "${id}" already exists. Pass --force to overwrite, or use \`akm tasks remove ${id}\` first.`, "RESOURCE_ALREADY_EXISTS");
67
+ // Pre-0.8.0 tasks were markdown; the 0.8.0 cutover moved them to pure YAML
68
+ // (see the tasks dir rule in src/indexer/walk/matchers.ts). A leftover
69
+ // `<id>.md` still names the same task, so creating `<id>.yml` beside it
70
+ // must collide loudly rather than silently minting a duplicate.
71
+ const legacyAssetPath = path.join(typeRoot, `${id}.md`);
72
+ if ((fs.existsSync(assetPath) || fs.existsSync(legacyAssetPath)) && !input.force) {
73
+ throw new UsageError(`Task "${id}" already exists. Pass --force to overwrite, or delete its file and run \`akm task sync\` first.`, "RESOURCE_ALREADY_EXISTS");
61
74
  }
62
75
  const yaml = renderTaskYaml({
63
76
  id,
@@ -77,9 +90,6 @@ export async function akmTasksAdd(input, deps = {}) {
77
90
  });
78
91
  const task = parseTaskDocument({ yaml, filePath: assetPath, id });
79
92
  await validateTaskDocument(task, { backend, stashDir });
80
- const obsoleteReason = obsoleteBackupTaskReason(task);
81
- if (obsoleteReason)
82
- throw new UsageError(obsoleteReason, "INVALID_FLAG_VALUE");
83
93
  const ref = taskAssetRef(id);
84
94
  const previousYaml = fs.existsSync(assetPath) ? fs.readFileSync(assetPath, "utf8") : undefined;
85
95
  let previousTask;
@@ -96,15 +106,19 @@ export async function akmTasksAdd(input, deps = {}) {
96
106
  const writeAsset = deps.writeAsset ?? writeAssetToSource;
97
107
  const deleteAsset = deps.deleteAsset ?? deleteAssetFromSource;
98
108
  const commitBoundary = deps.commitBoundary ?? commitWriteTargetBoundary;
99
- const wasInstalled = previousYaml !== undefined && (await sched.list()).some((entry) => entry.id === id);
109
+ const installedEntries = await sched.list();
110
+ assertNoForeignSchedule(installedEntries, id, bundle.installTarget);
111
+ const wasInstalled = previousYaml !== undefined && installedEntries.some((entry) => entry.id === id);
112
+ const installedEntry = installedEntries.find((entry) => entry.id === id);
113
+ const runtimeOpts = schedulerInstallOptions(installOpts, installedEntry, deps, installedEntry ? false : input.rebind === true, `create scheduler entry for task "${id}"`);
100
114
  let sourceRestoreArmed = false;
101
115
  let installSucceeded = false;
102
116
  try {
103
117
  sourceRestoreArmed = true;
104
- await writeAsset(target.source, target.config, ref, yaml);
105
- await sched.install(task);
118
+ await writeAsset(writeTarget.source, writeTarget.config, ref, yaml);
119
+ await sched.install(task, runtimeOpts);
106
120
  installSucceeded = true;
107
- commitBoundary(target, `Update tasks/${id}`);
121
+ commitBoundary(writeTarget, `Update tasks/${id}`);
108
122
  }
109
123
  catch (err) {
110
124
  const rollbackErrors = [];
@@ -113,12 +127,12 @@ export async function akmTasksAdd(input, deps = {}) {
113
127
  try {
114
128
  if (previousYaml === undefined) {
115
129
  if (fs.existsSync(assetPath)) {
116
- await deleteAsset(target.source, target.config, ref);
130
+ await deleteAsset(writeTarget.source, writeTarget.config, ref);
117
131
  sourceRestored = true;
118
132
  }
119
133
  }
120
134
  else {
121
- await restoreTaskSourceBytes(writeAsset, target.source, target.config, ref, assetPath, previousYaml);
135
+ await restoreTaskSourceBytes(writeAsset, writeTarget.source, writeTarget.config, ref, assetPath, previousYaml);
122
136
  sourceRestored = true;
123
137
  }
124
138
  }
@@ -136,7 +150,7 @@ export async function akmTasksAdd(input, deps = {}) {
136
150
  }
137
151
  else if (installSucceeded && previousTask) {
138
152
  try {
139
- await sched.install(previousTask);
153
+ await sched.install(previousTask, runtimeOpts);
140
154
  }
141
155
  catch (rollbackError) {
142
156
  rollbackErrors.push(rollbackError);
@@ -162,7 +176,7 @@ export async function akmTasksAdd(input, deps = {}) {
162
176
  }
163
177
  if (sourceRestored) {
164
178
  try {
165
- commitBoundary(target, `Restore tasks/${id}`);
179
+ commitBoundary(writeTarget, `Restore tasks/${id}`);
166
180
  }
167
181
  catch (rollbackError) {
168
182
  rollbackErrors.push(rollbackError);
@@ -178,252 +192,13 @@ export async function akmTasksAdd(input, deps = {}) {
178
192
  id,
179
193
  ref: conceptIdFromTypeName("task", id),
180
194
  path: assetPath,
181
- stashDir,
195
+ bundleDir: stashDir,
182
196
  schedule: task.schedule,
183
197
  enabled: task.enabled,
184
198
  backend,
185
199
  target: task.target,
186
200
  };
187
201
  }
188
- /**
189
- * Emit a single grouped stderr warning for legacy `.md` task files in the
190
- * tasks directory. 0.8.0 requires task definitions to be pure `.yml`; any
191
- * leftover `.md` files from 0.7.x would otherwise be silently skipped, which
192
- * makes scheduled tasks vanish without operator notice. We do NOT auto-migrate
193
- * — that is a separate workstream — but operators must see the affected files.
194
- *
195
- * `seen` is module-level so the warning is emitted at most once per process,
196
- * even when both `akm tasks list` and `akm tasks sync` are invoked in the same
197
- * akm run.
198
- */
199
- const warnedLegacyMdDirs = new Set();
200
- function warnLegacyMdTaskFiles(typeRoot) {
201
- if (warnedLegacyMdDirs.has(typeRoot))
202
- return;
203
- let mdFiles;
204
- try {
205
- mdFiles = fs.readdirSync(typeRoot).filter((f) => f.endsWith(".md"));
206
- }
207
- catch {
208
- return;
209
- }
210
- if (mdFiles.length === 0)
211
- return;
212
- warnedLegacyMdDirs.add(typeRoot);
213
- const affected = mdFiles.map((f) => `tasks/${f}`).join(", ");
214
- process.stderr.write(`WARNING: ${mdFiles.length} task file(s) use the legacy .md format and were ignored.\n` +
215
- ` AKM 0.8.0 requires tasks as pure .yml. See docs/migration/v0.7-to-v0.8.md#task-definition-files-mdfrontmatter--yml.\n` +
216
- ` Affected: ${affected}\n`);
217
- }
218
- /**
219
- * Reset the legacy `.md` task warning de-duplication state. Test-only escape
220
- * hatch — production code should never call this.
221
- */
222
- export function _resetLegacyMdTaskWarningStateForTests() {
223
- warnedLegacyMdDirs.clear();
224
- }
225
- export async function akmTasksList() {
226
- const stashDir = resolveStashDir();
227
- const typeRoot = path.join(stashDir, "tasks");
228
- if (!fs.existsSync(typeRoot))
229
- return { tasks: [], stale: [] };
230
- const entries = fs.readdirSync(typeRoot);
231
- warnLegacyMdTaskFiles(typeRoot);
232
- const files = entries.filter((f) => f.endsWith(".yml"));
233
- const tasks = [];
234
- const stale = [];
235
- for (const file of files) {
236
- const id = file.slice(0, -4);
237
- const filePath = path.join(typeRoot, file);
238
- let task;
239
- try {
240
- task = parseTaskDocument({ yaml: fs.readFileSync(filePath, "utf8"), filePath, id });
241
- }
242
- catch (err) {
243
- if (isStaleTaskError(err))
244
- stale.push(id);
245
- continue; // skip malformed files; `akm tasks show <id>` will surface the error
246
- }
247
- tasks.push({
248
- id: task.id,
249
- ref: conceptIdFromTypeName("task", task.id),
250
- path: filePath,
251
- schedule: task.schedule,
252
- enabled: task.enabled,
253
- target: task.target,
254
- name: task.name,
255
- description: task.description,
256
- when_to_use: task.when_to_use,
257
- tags: task.tags,
258
- });
259
- }
260
- if (stale.length > 0)
261
- warnStaleTaskFiles(stale);
262
- return { tasks, stale };
263
- }
264
- export async function akmTasksShow(id) {
265
- const normalised = normaliseTaskId(id);
266
- const stashDir = resolveStashDir();
267
- const typeRoot = path.join(stashDir, "tasks");
268
- if (fs.existsSync(typeRoot))
269
- warnLegacyMdTaskFiles(typeRoot);
270
- const filePath = await resolveAssetPath(stashDir, "task", normalised);
271
- const task = parseTaskDocument({
272
- yaml: fs.readFileSync(filePath, "utf8"),
273
- filePath,
274
- id: normalised,
275
- });
276
- const spec = parseSchedule(task.schedule, backendNameForPlatform());
277
- return {
278
- id: task.id,
279
- ref: conceptIdFromTypeName("task", task.id),
280
- path: filePath,
281
- schedule: task.schedule,
282
- cron: translateToCron(spec),
283
- enabled: task.enabled,
284
- target: task.target,
285
- name: task.name,
286
- description: task.description,
287
- when_to_use: task.when_to_use,
288
- tags: task.tags,
289
- };
290
- }
291
- export async function akmTasksRemove(id, deps = {}) {
292
- const normalised = normaliseTaskId(id);
293
- const target = resolveTaskWriteTarget();
294
- const stashDir = target.source.path;
295
- const typeRoot = path.join(stashDir, "tasks");
296
- if (fs.existsSync(typeRoot))
297
- warnLegacyMdTaskFiles(typeRoot);
298
- const filePath = await resolveAssetPath(stashDir, "task", normalised);
299
- const yaml = fs.readFileSync(filePath, "utf8");
300
- const ref = taskAssetRef(normalised);
301
- const sched = deps.backend ?? selectBackend();
302
- const writeAsset = deps.writeAsset ?? writeAssetToSource;
303
- const deleteAsset = deps.deleteAsset ?? deleteAssetFromSource;
304
- const commitBoundary = deps.commitBoundary ?? commitWriteTargetBoundary;
305
- const wasInstalled = (await sched.list()).some((entry) => entry.id === normalised);
306
- const previousTask = wasInstalled ? parseTaskDocument({ yaml, filePath, id: normalised }) : undefined;
307
- let uninstallAttempted = false;
308
- let deleteAttempted = false;
309
- try {
310
- uninstallAttempted = true;
311
- await sched.uninstall(normalised);
312
- deleteAttempted = true;
313
- await deleteAsset(target.source, target.config, ref);
314
- commitBoundary(target, `Remove tasks/${normalised}`);
315
- }
316
- catch (err) {
317
- const rollbackErrors = [];
318
- let sourceRestored = false;
319
- if (deleteAttempted) {
320
- try {
321
- await restoreTaskSourceBytes(writeAsset, target.source, target.config, ref, filePath, yaml);
322
- sourceRestored = true;
323
- }
324
- catch (rollbackError) {
325
- rollbackErrors.push(rollbackError);
326
- }
327
- }
328
- if (uninstallAttempted && previousTask) {
329
- try {
330
- await sched.install(previousTask);
331
- }
332
- catch (rollbackError) {
333
- rollbackErrors.push(rollbackError);
334
- }
335
- }
336
- if (sourceRestored) {
337
- try {
338
- commitBoundary(target, `Restore tasks/${normalised}`);
339
- }
340
- catch (rollbackError) {
341
- rollbackErrors.push(rollbackError);
342
- }
343
- }
344
- if (rollbackErrors.length > 0) {
345
- const message = err instanceof Error ? err.message : String(err);
346
- throw new AggregateError([err, ...rollbackErrors], `${message}; rollback for task "${normalised}" was incomplete.`);
347
- }
348
- throw err;
349
- }
350
- return { id: normalised, removed: true, backend: sched.name };
351
- }
352
- export async function akmTasksSetEnabled(id, enabled, deps = {}) {
353
- const normalised = normaliseTaskId(id);
354
- const target = resolveTaskWriteTarget();
355
- const stashDir = target.source.path;
356
- const typeRoot = path.join(stashDir, "tasks");
357
- if (fs.existsSync(typeRoot))
358
- warnLegacyMdTaskFiles(typeRoot);
359
- const filePath = await resolveAssetPath(stashDir, "task", normalised);
360
- const yaml = fs.readFileSync(filePath, "utf8");
361
- // Parse before writing so unsupported tasks are diagnosed without changing
362
- // the source file or its installed scheduler entry.
363
- const previousTask = parseTaskDocument({ yaml, filePath, id: normalised });
364
- const updated = setEnabledInYaml(yaml, enabled);
365
- const task = parseTaskDocument({ yaml: updated, filePath, id: normalised });
366
- const obsoleteReason = obsoleteBackupTaskReason(task);
367
- if (obsoleteReason)
368
- throw new UsageError(obsoleteReason, "INVALID_FLAG_VALUE");
369
- const ref = taskAssetRef(normalised);
370
- const sched = deps.backend ?? selectBackend();
371
- const writeAsset = deps.writeAsset ?? writeAssetToSource;
372
- const commitBoundary = deps.commitBoundary ?? commitWriteTargetBoundary;
373
- const wasInstalled = (await sched.list()).some((entry) => entry.id === normalised);
374
- let sourceRestoreArmed = false;
375
- let installSucceeded = false;
376
- try {
377
- sourceRestoreArmed = true;
378
- await writeAsset(target.source, target.config, ref, updated);
379
- // Reinstall from the (just-updated) definition rather than only toggling
380
- // the comment. A plain toggle leaves a stale schedule in place if the
381
- // .yml's `schedule:` changed while the task was disabled — re-enabling
382
- // would silently keep the old cron line. install() renders the block with
383
- // both the current schedule and the new enabled state, and is idempotent.
384
- await sched.install(task);
385
- installSucceeded = true;
386
- commitBoundary(target, `Update tasks/${normalised}`);
387
- }
388
- catch (err) {
389
- const rollbackErrors = [];
390
- let sourceRestored = false;
391
- if (sourceRestoreArmed) {
392
- try {
393
- await restoreTaskSourceBytes(writeAsset, target.source, target.config, ref, filePath, yaml);
394
- sourceRestored = true;
395
- }
396
- catch (rollbackError) {
397
- rollbackErrors.push(rollbackError);
398
- }
399
- }
400
- if (installSucceeded) {
401
- try {
402
- if (wasInstalled)
403
- await sched.install(previousTask);
404
- else
405
- await sched.uninstall(normalised);
406
- }
407
- catch (rollbackError) {
408
- rollbackErrors.push(rollbackError);
409
- }
410
- }
411
- if (sourceRestored) {
412
- try {
413
- commitBoundary(target, `Restore tasks/${normalised}`);
414
- }
415
- catch (rollbackError) {
416
- rollbackErrors.push(rollbackError);
417
- }
418
- }
419
- if (rollbackErrors.length > 0) {
420
- const message = err instanceof Error ? err.message : String(err);
421
- throw new AggregateError([err, ...rollbackErrors], `${message}; rollback for task "${normalised}" was incomplete.`);
422
- }
423
- throw err;
424
- }
425
- return { id: normalised, enabled, backend: sched.name };
426
- }
427
202
  export async function akmTasksRun(id, options = {}) {
428
203
  const startedAt = new Date();
429
204
  let normalised;
@@ -441,7 +216,13 @@ export async function akmTasksRun(id, options = {}) {
441
216
  }
442
217
  let stashDir;
443
218
  try {
444
- stashDir = resolveStashDir();
219
+ // No --bundle uses the primary stash. With --bundle, resolve (read-only)
220
+ // the named bundle so the task file and
221
+ // its relative asset refs load from that bundle's path.
222
+ stashDir =
223
+ options.target !== undefined
224
+ ? resolveWriteTarget(loadConfig(), options.target, { requireWritable: false }).source.path
225
+ : resolveStashDir();
445
226
  }
446
227
  catch (failure) {
447
228
  recordTaskAttemptFailure({
@@ -452,9 +233,6 @@ export async function akmTasksRun(id, options = {}) {
452
233
  });
453
234
  throw failure;
454
235
  }
455
- const typeRoot = path.join(stashDir, "tasks");
456
- if (fs.existsSync(typeRoot))
457
- warnLegacyMdTaskFiles(typeRoot);
458
236
  const result = await runTask(normalised, { stashDir, scheduled: options.scheduled === true });
459
237
  const exitCode = result.status === "failed" && result.target.kind === "command" && result.detail?.exitCode === 78
460
238
  ? 78
@@ -468,26 +246,32 @@ export async function akmTasksRun(id, options = {}) {
468
246
  export async function akmTasksHistory(input) {
469
247
  const limit = input.limit !== undefined && input.limit > 0 ? input.limit : 50;
470
248
  const id = input.id ? normaliseTaskId(input.id) : undefined;
471
- const stashDir = resolveStashDir();
472
- const typeRoot = path.join(stashDir, "tasks");
473
- if (fs.existsSync(typeRoot))
474
- warnLegacyMdTaskFiles(typeRoot);
249
+ // History rows are keyed by task id in state.db, not per bundle.
475
250
  return { rows: readTaskHistory({ id, limit }) };
476
251
  }
477
252
  /**
478
- * Reconcile the on-disk task files with the OS scheduler.
253
+ * Reconcile the on-disk task files of ONE bundle with the OS scheduler.
479
254
  * • install missing tasks (after validating them — invalid files are
480
255
  * skipped with a per-task reason rather than aborting the whole sync)
481
256
  * • reinstall tasks whose schedule or enabled state changed in the .yml
482
257
  * (drift detected by comparing the backend's installed signature against
483
258
  * the signature the current definition would produce)
484
259
  * • remove orphan scheduler entries that no longer have a backing file
260
+ *
261
+ * `--bundle <bundle>` scopes the reconciliation to that bundle: the file set is
262
+ * the bundle's `tasks/*.yml` and — crucially — the scheduler entries considered
263
+ * are ONLY those attributed to the same bundle (parsed from the installed
264
+ * `--bundle` token; absent ⇒ primary). This is the security boundary that keeps
265
+ * "registering a bundle never activates code": a plain (primary) sync never
266
+ * installs from, updates, or removes another bundle's entries, and sync never
267
+ * scans all bundles. Activation happens only through explicit `add --bundle`
268
+ * (or `sync --bundle` on a bundle whose task files are already present).
485
269
  */
486
- export async function akmTasksSync(deps = {}) {
487
- const stashDir = resolveStashDir();
270
+ export async function akmTasksSync(deps = {}, bundleTarget, options = {}) {
271
+ const stashDir = resolveTaskInspectDir(bundleTarget);
272
+ // Primary-bundle scheduler entries omit --bundle; other bundles carry it.
273
+ const syncTarget = bundleTarget !== undefined && !isPrimaryStashPath(stashDir) ? bundleTarget : undefined;
488
274
  const typeRoot = path.join(stashDir, "tasks");
489
- if (fs.existsSync(typeRoot))
490
- warnLegacyMdTaskFiles(typeRoot);
491
275
  const fileIds = fs.existsSync(typeRoot)
492
276
  ? fs
493
277
  .readdirSync(typeRoot)
@@ -496,22 +280,30 @@ export async function akmTasksSync(deps = {}) {
496
280
  : [];
497
281
  const sched = deps.backend ?? selectBackend();
498
282
  const backend = sched.name;
499
- // Map id → installed signature so sync can detect schedule/enabled drift on
500
- // tasks that already exist in the scheduler, not just presence/absence.
501
- const present = new Map((await sched.list()).map((t) => [t.id, t.signature]));
283
+ const installOpts = syncTarget !== undefined ? { target: syncTarget } : undefined;
284
+ const allEntries = options.rebind && sched.listForRebind ? await sched.listForRebind() : await sched.list();
285
+ // Attribution filter: only entries installed from THIS bundle are reconciled
286
+ // here. Entries carrying a `--bundle` for a different bundle are invisible to
287
+ // this sync — never removed, never touched.
288
+ const present = new Map(allEntries.filter((t) => sameBundle(t.target, syncTarget)).map((t) => [t.id, t]));
502
289
  const installed = [];
503
290
  const updated = [];
504
291
  const unchanged = [];
505
292
  const skipped = [];
293
+ const warnings = [];
506
294
  for (const id of fileIds) {
507
295
  const filePath = path.join(typeRoot, `${id}.yml`);
508
296
  let task;
509
297
  try {
510
298
  task = parseTaskDocument({ yaml: fs.readFileSync(filePath, "utf8"), filePath, id });
511
299
  await validateTaskDocument(task, { backend, stashDir });
512
- const obsoleteReason = obsoleteBackupTaskReason(task);
513
- if (obsoleteReason)
514
- throw new UsageError(obsoleteReason, "INVALID_FLAG_VALUE");
300
+ // A bare id can only be scheduled from ONE bundle at a time (scheduler ids
301
+ // are never namespaced). If this id is already scheduled from a different
302
+ // bundle, refuse rather than clobber it — surface it as a per-task skip so
303
+ // the rest of the sync still proceeds.
304
+ const foreign = allEntries.find((e) => e.id === id && !sameBundle(e.target, syncTarget));
305
+ if (foreign)
306
+ throw new UsageError(foreignScheduleMessage(id, foreign.target), "RESOURCE_ALREADY_EXISTS");
515
307
  }
516
308
  catch (err) {
517
309
  skipped.push({ id, reason: err instanceof Error ? err.message : String(err) });
@@ -531,8 +323,14 @@ export async function akmTasksSync(deps = {}) {
531
323
  continue;
532
324
  }
533
325
  if (!present.has(id)) {
534
- await sched.install(task);
535
- installed.push(id);
326
+ try {
327
+ const runtimeOpts = schedulerInstallOptions(installOpts, undefined, deps, options.rebind === true, `create scheduler entry for task "${id}"`, warnings);
328
+ await sched.install(task, runtimeOpts);
329
+ installed.push(id);
330
+ }
331
+ catch (error) {
332
+ skipped.push({ id, reason: error instanceof Error ? error.message : String(error) });
333
+ }
536
334
  continue;
537
335
  }
538
336
  // Already installed — reconcile against the current definition. Compare the
@@ -540,13 +338,15 @@ export async function akmTasksSync(deps = {}) {
540
338
  // When the backend can't produce a signature (no expectedSignature, or it
541
339
  // didn't record one), reinstall unconditionally — install() is idempotent,
542
340
  // so the cost is one crontab write and correctness is guaranteed.
543
- const installedSig = present.get(id);
544
- const expectedSig = sched.expectedSignature?.(task);
341
+ const installedEntry = present.get(id);
342
+ const runtimeOpts = schedulerInstallOptions(installOpts, options.rebind ? undefined : installedEntry, deps, options.rebind === true, `rebind scheduler entry for task "${id}"`, warnings);
343
+ const installedSig = installedEntry.signature;
344
+ const expectedSig = sched.expectedSignature?.(task, runtimeOpts);
545
345
  if (installedSig !== undefined && expectedSig !== undefined && installedSig === expectedSig) {
546
346
  unchanged.push(id);
547
347
  }
548
348
  else {
549
- await sched.install(task);
349
+ await sched.install(task, runtimeOpts);
550
350
  updated.push(id);
551
351
  }
552
352
  }
@@ -557,48 +357,69 @@ export async function akmTasksSync(deps = {}) {
557
357
  removed.push(installedId);
558
358
  }
559
359
  }
560
- return { installed, updated, removed, unchanged, skipped, backend: sched.name };
561
- }
562
- function obsoleteBackupTaskReason(task) {
563
- if (!task.enabled || task.target.kind !== "command")
564
- return undefined;
565
- const executableIndex = findBareAkmExecutableIndex(task.target.cmd);
566
- if (executableIndex === undefined)
567
- return undefined;
568
- const args = task.target.cmd.slice(executableIndex + 1);
569
- if (args.length !== 2 || args[0] !== "db" || args[1] !== "backups")
570
- return undefined;
571
- return (`Task "${task.id}" invokes obsolete \`akm db backups\`, which only listed legacy backups and is not a 0.9 backup command. ` +
572
- "AKM will not install or enable it; sync will keep any existing scheduler entry disabled, and the task file is unchanged. " +
573
- "Use `akm backup create --for 0.9.0` for an explicit migration recovery snapshot.");
360
+ return {
361
+ installed,
362
+ updated,
363
+ removed,
364
+ unchanged,
365
+ skipped,
366
+ backend: sched.name,
367
+ ...(warnings.length > 0 ? { warnings } : {}),
368
+ };
574
369
  }
575
- export async function akmTasksDoctor() {
370
+ export async function akmTasksDoctor(deps = {}) {
576
371
  const warnings = [];
577
- let invocation = { argv: [], via: "unresolved" };
372
+ let invocation = {
373
+ argv: [],
374
+ via: "unresolved",
375
+ };
578
376
  try {
579
- const r = resolveAkmInvocation();
580
- invocation = { argv: r.argv, via: r.via };
377
+ const r = (deps.resolveInvocation ?? resolveAkmInvocation)();
378
+ invocation = { argv: r.argv, via: r.via, kind: r.kind, eligible: r.eligible };
581
379
  }
582
380
  catch (err) {
583
381
  warnings.push(err instanceof Error ? err.message : String(err));
584
382
  }
585
- try {
586
- const stashDir = resolveStashDir();
587
- const typeRoot = path.join(stashDir, "tasks");
588
- if (fs.existsSync(typeRoot))
589
- warnLegacyMdTaskFiles(typeRoot);
383
+ const skipNativeInspection = process.env.BUN_TEST === "1" && !deps.backend;
384
+ const sched = deps.backend ?? (skipNativeInspection ? undefined : selectBackend());
385
+ const backend = sched?.name ?? backendNameForPlatform();
386
+ let installed = [];
387
+ if (skipNativeInspection) {
388
+ warnings.push("Native scheduler inspection is skipped inside the bun test harness.");
590
389
  }
591
- catch {
592
- // doctor must never fail on stash-resolution; the warning is best-effort
390
+ else {
391
+ try {
392
+ installed = await sched.list();
393
+ }
394
+ catch (error) {
395
+ warnings.push(`Unable to inspect installed ${backend} definitions: ${error instanceof Error ? error.message : String(error)}`);
396
+ }
593
397
  }
594
- const backend = backendNameForPlatform();
595
- const config = loadConfig();
398
+ const bindings = groupInstalledBindings(installed, invocation);
399
+ // Report the EFFECTIVE engine view — the same one the runner resolves —
400
+ // so doctor never says "no engine" on an install where tasks actually run.
401
+ const { config } = withEngineFallback(loadConfig());
596
402
  const defaultEngine = config.defaults?.engine;
597
403
  const engines = Object.keys(config.engines ?? {});
598
404
  // §6.1: surface the effective triage settings for the default improve
599
405
  // strategy. The struct is a fixed shape, so this is a deliberate addition.
600
406
  const improveStrategyName = typeof config.defaults?.improveStrategy === "string" ? config.defaults.improveStrategy : "default";
601
- const triage = resolveImproveStrategy(config.defaults?.improveStrategy, config).config.processes?.triage;
407
+ // D8 — report the EFFECTIVE strategy, not the raw one. Resolving the raw
408
+ // strategy here would report `applyMode: "promote"` for a promote strategy
409
+ // under a review-first config, while the run actually uses "queue" — a doctor
410
+ // command lying about the thing it exists to diagnose.
411
+ const rawStrategy = resolveImproveStrategy(config.defaults?.improveStrategy, config).config;
412
+ const { config: effectiveStrategy, gated } = applyAutonomyGate(rawStrategy, config);
413
+ const autonomyEnabled = isImproveAutonomyEnabled(config);
414
+ // Memory cleanup has no strategy flag to downgrade, so add that direct lane
415
+ // to the strategy-derived gate report.
416
+ const allGated = autonomyEnabled ? [] : [...gated, ...describeGatedLanes(configuredDirectAutonomyLanes())];
417
+ const improveAutonomy = {
418
+ enabled: autonomyEnabled,
419
+ configKey: IMPROVE_AUTONOMY_CONFIG_KEY,
420
+ gatedLanes: allGated.map((entry) => ({ lane: entry.lane, reason: entry.reason })),
421
+ };
422
+ const triage = effectiveStrategy.processes?.triage;
602
423
  const improveTriage = triage
603
424
  ? {
604
425
  defaultStrategy: improveStrategyName,
@@ -610,17 +431,111 @@ export async function akmTasksDoctor() {
610
431
  return {
611
432
  backend,
612
433
  akm: invocation,
434
+ caller: invocation,
435
+ bindings,
436
+ ...(bindings.some((binding) => !binding.status.includes("ok"))
437
+ ? { remediation: "akm task sync --rebind" }
438
+ : {}),
613
439
  logDir: getTaskLogDir(),
614
440
  historyDir: getTaskHistoryDir(),
615
441
  engine: { defaultEngine, available: engines },
616
- stale: collectStaleTaskIds(),
617
- staleGeneratedCommands: collectStaleGeneratedCommands(),
618
442
  scheduleSubset: SCHEDULE_SUPPORTED_SUBSET_HINT,
619
443
  warnings,
444
+ improveAutonomy,
620
445
  ...(improveTriage ? { improveTriage } : {}),
621
446
  };
622
447
  }
623
448
  // ── helpers ─────────────────────────────────────────────────────────────────
449
+ function schedulerInstallOptions(base, installed, deps, explicitRebind, operation, warnings = []) {
450
+ if (installed && !explicitRebind) {
451
+ return {
452
+ ...base,
453
+ binding: installed.binding,
454
+ contextPath: installed.contextPath,
455
+ };
456
+ }
457
+ // Injected backends can own their default runtime unless a resolver is supplied.
458
+ if (deps.backend && !deps.schedulerRuntime)
459
+ return base;
460
+ const runtime = deps.schedulerRuntime?.() ?? prepareSchedulerRuntime(explicitRebind, operation);
461
+ // --rebind bypasses the eligibility refusal in prepareSchedulerRuntime; warn once
462
+ // per sync run rather than silently writing a mutable/unproven binary into cron.
463
+ if (explicitRebind && runtime.eligible === false && warnings.length === 0) {
464
+ warnings.push(`--rebind bound scheduled tasks to an ineligible ${runtime.kind ?? "unknown"} invocation (${runtime.binding.join(" ")}); scheduled runs will invoke a mutable, unproven binary. Install akm via \`npm install --global akm-cli\` or a standalone release, then re-run \`akm task sync --rebind\`.`);
465
+ }
466
+ return { ...base, binding: runtime.binding, contextPath: runtime.contextPath };
467
+ }
468
+ export function prepareSchedulerRuntime(explicitRebind, operation, deps = {}) {
469
+ const invocation = (deps.resolveInvocation ?? resolveAkmInvocation)();
470
+ if (!invocation.eligible && !explicitRebind) {
471
+ throw new UsageError(`Refusing to ${operation} from an ineligible ${invocation.kind ?? "unknown"} invocation (${invocation.argv.join(" ")}).`, "INVALID_FLAG_VALUE", "npm-global ownership could not be verified. Run `npm install --global akm-cli` and use that launcher, use a standalone installation, or explicitly repeat the operation with --rebind.");
472
+ }
473
+ const contextPath = (deps.writeDescriptor ?? writeSchedulerContextDescriptor)(schedulerContextDescriptor());
474
+ return { binding: invocation.argv, contextPath, eligible: invocation.eligible, kind: invocation.kind };
475
+ }
476
+ function groupInstalledBindings(entries, invocation) {
477
+ const groups = new Map();
478
+ for (const entry of entries) {
479
+ const argv = entry.binding;
480
+ const status = inspectInstalledBinding(entry, invocation);
481
+ const key = JSON.stringify([argv, entry.contextPath, status]);
482
+ const existing = groups.get(key);
483
+ if (existing) {
484
+ existing.taskIds.push(entry.id);
485
+ continue;
486
+ }
487
+ groups.set(key, {
488
+ argv,
489
+ contextPath: entry.contextPath,
490
+ taskIds: [entry.id],
491
+ status,
492
+ });
493
+ }
494
+ return [...groups.values()].map((group) => ({ ...group, taskIds: group.taskIds.sort() }));
495
+ }
496
+ function inspectInstalledBinding(entry, invocation) {
497
+ const status = [];
498
+ const binding = entry.binding;
499
+ if (!(invocation.eligible === true && sameArgv(binding, invocation.argv)) &&
500
+ binding.some((part) => /(?:^|[\\/])src[\\/]cli\.ts$|(?:^|[\\/])dist[\\/](?:cli\.js|cli-node\.mjs)$/i.test(part) ||
501
+ (path.isAbsolute(part) && hasGitAncestor(part)))) {
502
+ status.push("checkout");
503
+ }
504
+ if (binding.some((part) => part === "akm" || part === "bun" || part === "node"))
505
+ status.push("path-selected");
506
+ try {
507
+ validateSchedulerContextDescriptor(entry.contextPath);
508
+ }
509
+ catch {
510
+ status.push("invalid-context");
511
+ }
512
+ const absolutePaths = [...binding.filter((part) => path.isAbsolute(part)), entry.contextPath];
513
+ if (absolutePaths.some((part) => !fs.existsSync(part)))
514
+ status.push("missing-path");
515
+ if (status.length === 0)
516
+ status.push("ok");
517
+ return status;
518
+ }
519
+ function sameArgv(left, right) {
520
+ return left.length === right.length && left.every((value, index) => value === right[index]);
521
+ }
522
+ function hasGitAncestor(file) {
523
+ let current;
524
+ try {
525
+ current = path.dirname(fs.realpathSync(file));
526
+ }
527
+ catch {
528
+ return false;
529
+ }
530
+ for (;;) {
531
+ if (fs.existsSync(path.join(current, ".git")))
532
+ return true;
533
+ const parent = path.dirname(current);
534
+ if (parent === current)
535
+ return false;
536
+ current = parent;
537
+ }
538
+ }
624
539
  function taskAssetRef(id) {
625
540
  return { type: "task", name: id };
626
541
  }
@@ -629,8 +544,55 @@ async function restoreTaskSourceBytes(writeAsset, source, config, ref, filePath,
629
544
  // The normal write path adds a trailing newline; rollback restores the raw snapshot exactly.
630
545
  fs.writeFileSync(filePath, yaml, "utf8");
631
546
  }
632
- function resolveTaskWriteTarget() {
633
- return resolveWriteTarget(loadConfig());
547
+ /**
548
+ * Resolve the bundle a mutating/run task command targets. Returns the resolved
549
+ * write/read target, its stash path, and the `--bundle <bundle>` token to embed
550
+ * in scheduled invocations. The primary bundle uses the target-less form.
551
+ */
552
+ function resolveTaskBundle(target, opts) {
553
+ const selected = resolveWriteTarget(loadConfig(), target, { requireWritable: opts.requireWritable });
554
+ const resolved = opts.requireWritable ? prepareWriteTargetForMutation(selected) : selected;
555
+ const stashDir = resolved.source.path;
556
+ const installTarget = isPrimaryStashPath(stashDir) ? undefined : (resolved.selector ?? resolved.source.name);
557
+ return { resolved, stashDir, installTarget };
558
+ }
559
+ /**
560
+ * Resolve the tasks/ directory a read/inspect command operates on. No
561
+ * `--bundle` uses the primary stash; `--bundle X` resolves bundle X read-only.
562
+ */
563
+ function resolveTaskInspectDir(target) {
564
+ if (target === undefined)
565
+ return resolveStashDir();
566
+ return resolveWriteTarget(loadConfig(), target, { requireWritable: false }).source.path;
567
+ }
568
+ /** True when `candidate` resolves to the same directory as the primary stash. */
569
+ function isPrimaryStashPath(candidate) {
570
+ let primary;
571
+ try {
572
+ primary = path.resolve(resolveStashDir());
573
+ }
574
+ catch {
575
+ return false;
576
+ }
577
+ return path.resolve(candidate) === primary;
578
+ }
579
+ /** Two bundle attributions match when both are the primary (undefined) or equal names. */
580
+ function sameBundle(a, b) {
581
+ return (a ?? undefined) === (b ?? undefined);
582
+ }
583
+ function foreignScheduleMessage(id, existingTarget) {
584
+ const where = existingTarget === undefined ? "the default bundle" : `bundle "${existingTarget}"`;
585
+ return `Task id "${id}" is already scheduled from ${where}; rename the task or disable the existing one first.`;
586
+ }
587
+ /**
588
+ * Refuse to schedule an id already installed from a DIFFERENT bundle. Scheduler
589
+ * ids are the bare task id (never namespaced), so a single id can be active from
590
+ * only one bundle at a time — a collision is a hard error, not an auto-rename.
591
+ */
592
+ function assertNoForeignSchedule(entries, id, installTarget) {
593
+ const foreign = entries.find((entry) => entry.id === id && !sameBundle(entry.target, installTarget));
594
+ if (foreign)
595
+ throw new UsageError(foreignScheduleMessage(id, foreign.target), "RESOURCE_ALREADY_EXISTS");
634
596
  }
635
597
  function renderTaskYaml(input) {
636
598
  const obj = { version: 2, schedule: input.schedule, enabled: input.enabled };
@@ -639,6 +601,8 @@ function renderTaskYaml(input) {
639
601
  if (input.params) {
640
602
  obj.params = parseJsonObjectArg(input.params);
641
603
  }
604
+ if (input.timeoutMs !== undefined)
605
+ obj.timeoutMs = input.timeoutMs;
642
606
  }
643
607
  else if (input.prompt) {
644
608
  obj.prompt = input.prompt;
@@ -667,101 +631,6 @@ function renderTaskYaml(input) {
667
631
  obj.tags = input.tags;
668
632
  return yamlStringify(obj);
669
633
  }
670
- function isStaleTaskError(err) {
671
- return err instanceof UsageError && err.code === "TASK_SCHEMA_VERSION_UNSUPPORTED";
672
- }
673
- function warnStaleTaskFiles(ids) {
674
- process.stderr.write(`WARNING: ${ids.length} task file(s) use an unsupported task schema version and were not loaded.\n` +
675
- ` Use version: 2. See docs/migration/v0.8-to-v0.9.md#engine-and-task-assets.\n` +
676
- ` Affected: ${ids.map((id) => `tasks/${id}.yml`).join(", ")}\n`);
677
- }
678
- function collectStaleTaskIds() {
679
- const typeRoot = path.join(resolveStashDir(), "tasks");
680
- if (!fs.existsSync(typeRoot))
681
- return [];
682
- const stale = [];
683
- for (const file of fs.readdirSync(typeRoot)) {
684
- if (!file.endsWith(".yml"))
685
- continue;
686
- const id = file.slice(0, -4);
687
- try {
688
- parseTaskDocument({
689
- yaml: fs.readFileSync(path.join(typeRoot, file), "utf8"),
690
- filePath: path.join(typeRoot, file),
691
- id,
692
- });
693
- }
694
- catch (err) {
695
- if (isStaleTaskError(err))
696
- stale.push(id);
697
- }
698
- }
699
- return stale;
700
- }
701
- // Old-spelling match keys for the default-task commands whose retired
702
- // `--auto-accept safe` flag is dropped in the `replacement`. Each id lists BOTH
703
- // deprecated spellings that can survive in a v2 task file: the original
704
- // `--profile X --auto-accept safe` and the intermediate `--strategy X
705
- // --auto-accept safe` (what the 0.8→0.9 migration writes — parser normalization
706
- // only rewrites `--profile`→`--strategy` for legacy v1 files, so a stored v2
707
- // file keeps whichever spelling it was minted with). Without the `--strategy`
708
- // variant, migrated default tasks match no key and warn on every run until 0.10
709
- // (chunk-6 ledger residue).
710
- const STALE_GENERATED_COMMANDS = {
711
- "akm-improve-frequent": {
712
- commands: [
713
- "akm improve --profile frequent --auto-accept safe",
714
- "akm improve --strategy frequent --auto-accept safe",
715
- ],
716
- replacement: "akm improve --strategy frequent",
717
- },
718
- "akm-improve-consolidate": {
719
- commands: [
720
- "akm improve --profile consolidate --auto-accept safe",
721
- "akm improve --strategy consolidate --auto-accept safe",
722
- ],
723
- replacement: "akm improve --strategy consolidate",
724
- },
725
- "akm-improve-nightly": {
726
- commands: [
727
- "akm improve --profile thorough --auto-accept safe",
728
- "akm improve --strategy thorough --auto-accept safe",
729
- ],
730
- replacement: "akm improve --strategy thorough",
731
- },
732
- "akm-improve-catchup": {
733
- commands: ["akm improve --profile catchup --auto-accept safe", "akm improve --strategy catchup --auto-accept safe"],
734
- replacement: "akm improve --strategy catchup",
735
- },
736
- "akm-graph-refresh-weekly": {
737
- commands: [
738
- "akm improve --profile graph-refresh --auto-accept safe",
739
- "akm improve --strategy graph-refresh --auto-accept safe",
740
- ],
741
- replacement: "akm improve --strategy graph-refresh",
742
- },
743
- };
744
- function collectStaleGeneratedCommands() {
745
- const typeRoot = path.join(resolveStashDir(), "tasks");
746
- if (!fs.existsSync(typeRoot))
747
- return [];
748
- const stale = [];
749
- for (const [id, expected] of Object.entries(STALE_GENERATED_COMMANDS)) {
750
- const filePath = path.join(typeRoot, `${id}.yml`);
751
- if (!fs.existsSync(filePath))
752
- continue;
753
- try {
754
- const task = parseTaskDocument({ yaml: fs.readFileSync(filePath, "utf8"), filePath, id });
755
- if (task.target.kind === "command" && expected.commands.includes(task.target.cmd.join(" "))) {
756
- stale.push({ id, replacement: expected.replacement });
757
- }
758
- }
759
- catch {
760
- // Stale-schema reporting owns files that cannot be parsed.
761
- }
762
- }
763
- return stale;
764
- }
765
634
  function parseJsonObjectArg(raw) {
766
635
  let parsed;
767
636
  try {
@@ -799,11 +668,9 @@ export function setEnabledInYaml(yaml, enabled) {
799
668
  // Re-exported so tests can verify the validator path directly.
800
669
  // Re-export error classes consumed by callers that want to instanceof-check.
801
670
  // Re-export this so the CLI can decide what process exit code to use after
802
- // `akm tasks run` completes.
671
+ // `akm task run` completes.
803
672
  export { ConfigError, exitCodeForStatus, NotFoundError, parseTaskDocument, UsageError };
804
- // Accept a bare task id or the canonical 0.9.0 `[bundle//]tasks/<id>` ref
805
- // (ref-grammar decision D-R3). The pre-0.9.0 `task:<id>` colon grammar is
806
- // retired and rejected loudly — it appears NOWHERE after the flip.
673
+ // Accept a bare task id or the canonical `[bundle//]tasks/<id>` ref.
807
674
  export function parseTaskRef(input) {
808
675
  const trimmed = input.trim();
809
676
  // Canonical conceptId form: `[bundle//]tasks/<id>`. A `/` unambiguously marks
@@ -821,11 +688,5 @@ export function parseTaskRef(input) {
821
688
  }
822
689
  throw new UsageError(`Expected a task id or tasks/<id> ref, got "${input}".`, "INVALID_FLAG_VALUE");
823
690
  }
824
- // Legacy `task:<id>` grammar is gone in 0.9.0 (D-R3) — reject it with a typed
825
- // error that names the new form so muscle-memory callers get a clear fix.
826
- if (trimmed.includes(":")) {
827
- const legacyName = trimmed.slice(trimmed.indexOf(":") + 1);
828
- throw new UsageError(`The \`task:<id>\` ref grammar was removed in 0.9.0 — use the bare id or \`tasks/${legacyName || "<id>"}\`.`, "INVALID_FLAG_VALUE");
829
- }
830
691
  return { id: normaliseTaskId(trimmed) };
831
692
  }