akm-cli 0.9.1 → 0.9.2-alpha.2

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 (350) hide show
  1. package/CHANGELOG.md +103 -28
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
  12. package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
  13. package/dist/assets/tasks/core/extract.yml +6 -5
  14. package/dist/assets/tasks/core/improve.yml +6 -5
  15. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  16. package/dist/assets/tasks/core/sync.yml +6 -5
  17. package/dist/assets/tasks/core/version-check.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  21. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  22. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  23. package/dist/cli/confirm.js +2 -2
  24. package/dist/cli/parse-args.js +3 -24
  25. package/dist/cli/retired-commands.js +1 -1
  26. package/dist/cli/shared.js +2 -2
  27. package/dist/cli.js +11 -9
  28. package/dist/commands/agent/agent-dispatch.js +55 -89
  29. package/dist/commands/agent/contribute-cli.js +12 -45
  30. package/dist/commands/command/builtin-action.js +32 -0
  31. package/dist/commands/command/command-cli.js +99 -0
  32. package/dist/commands/command/command-execution.js +308 -0
  33. package/dist/commands/command/execution-source-loader.js +176 -0
  34. package/dist/commands/command/portable-template.js +60 -0
  35. package/dist/commands/config-cli.js +10 -4
  36. package/dist/commands/env/env.js +4 -2
  37. package/dist/commands/feedback-cli.js +1 -1
  38. package/dist/commands/health/checks.js +241 -29
  39. package/dist/commands/health/html-report.js +0 -14
  40. package/dist/commands/health/report-view-model.js +0 -1
  41. package/dist/commands/health/surfaces.js +6 -7
  42. package/dist/commands/health/types.js +0 -2
  43. package/dist/commands/health.js +63 -18
  44. package/dist/commands/improve/collapse-detector.js +5 -6
  45. package/dist/commands/improve/consolidate.js +251 -214
  46. package/dist/commands/improve/distill/promote-memory.js +71 -34
  47. package/dist/commands/improve/distill/quality-gate.js +17 -5
  48. package/dist/commands/improve/distill.js +232 -155
  49. package/dist/commands/improve/eligibility.js +112 -79
  50. package/dist/commands/improve/execution.js +57 -0
  51. package/dist/commands/improve/extract-cli.js +5 -5
  52. package/dist/commands/improve/extract-prompt.js +64 -22
  53. package/dist/commands/improve/extract.js +608 -360
  54. package/dist/commands/improve/improve-strategies.js +43 -14
  55. package/dist/commands/improve/improve.js +249 -29
  56. package/dist/commands/improve/loop-stages.js +11 -17
  57. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  58. package/dist/commands/improve/outcome-loop.js +22 -38
  59. package/dist/commands/improve/planner.js +134 -0
  60. package/dist/commands/improve/preparation.js +730 -409
  61. package/dist/commands/improve/reflect.js +386 -223
  62. package/dist/commands/improve/run-context.js +3 -4
  63. package/dist/commands/improve/salience.js +6 -58
  64. package/dist/commands/improve/session-asset.js +12 -12
  65. package/dist/commands/lint/index.js +101 -29
  66. package/dist/commands/migrate-cli.js +11 -69
  67. package/dist/commands/migration-tool.js +6 -9
  68. package/dist/commands/models-cli.js +27 -0
  69. package/dist/commands/proposal/drain.js +258 -186
  70. package/dist/commands/proposal/proposal-cli.js +32 -10
  71. package/dist/commands/proposal/proposal.js +2 -5
  72. package/dist/commands/proposal/propose.js +192 -172
  73. package/dist/commands/proposal/repository.js +54 -91
  74. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  75. package/dist/commands/read/curate.js +53 -22
  76. package/dist/commands/read/registry-search.js +25 -9
  77. package/dist/commands/read/remember-cli.js +14 -2
  78. package/dist/commands/read/search.js +10 -4
  79. package/dist/commands/read/show.js +139 -153
  80. package/dist/commands/registry-cli.js +16 -7
  81. package/dist/commands/remember.js +33 -18
  82. package/dist/commands/sources/add-cli.js +19 -178
  83. package/dist/commands/sources/bundle-cli.js +15 -3
  84. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  85. package/dist/commands/sources/info.js +2 -1
  86. package/dist/commands/sources/installed-stashes.js +901 -177
  87. package/dist/commands/sources/schema-repair.js +174 -95
  88. package/dist/commands/sources/self-update.js +30 -74
  89. package/dist/commands/sources/source-add.js +3 -5
  90. package/dist/commands/sources/sources-cli.js +2 -15
  91. package/dist/commands/sources/update-transaction.js +220 -0
  92. package/dist/commands/tasks/tasks-cli.js +3 -3
  93. package/dist/commands/tasks/tasks.js +736 -317
  94. package/dist/commands/workflow-cli.js +2 -2
  95. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  96. package/dist/core/adapter/adapters/akm-adapter.js +85 -35
  97. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  98. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  99. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  100. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  101. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  102. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  103. package/dist/core/adapter/adapters/index.js +0 -9
  104. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  105. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  106. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  107. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  108. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  109. package/dist/core/adapter/execution-source.js +308 -0
  110. package/dist/core/adapter/recognize-match.js +36 -13
  111. package/dist/core/adapter/registry.js +0 -9
  112. package/dist/core/asset/stash-meta.js +94 -4
  113. package/dist/core/common.js +6 -11
  114. package/dist/core/config/config-io.js +3 -3
  115. package/dist/core/config/config-schema.js +18 -40
  116. package/dist/core/config/config-sources.js +11 -21
  117. package/dist/core/config/config-walker.js +31 -13
  118. package/dist/core/config/config.js +23 -26
  119. package/dist/core/config/schema/engines.js +8 -7
  120. package/dist/core/config/schema/improve-processes.js +29 -5
  121. package/dist/core/config/schema/index-config.js +0 -27
  122. package/dist/core/config/schema/primitives.js +1 -23
  123. package/dist/core/config/schema/sources-bundles.js +13 -16
  124. package/dist/core/errors.js +2 -0
  125. package/dist/core/events.js +68 -32
  126. package/dist/core/extra-params.js +1 -0
  127. package/dist/core/improve-result.js +315 -0
  128. package/dist/core/lesson-lint.js +0 -6
  129. package/dist/core/maintenance-barrier.js +4 -4
  130. package/dist/core/network-policy.js +152 -0
  131. package/dist/core/paths.js +1 -1
  132. package/dist/core/recognition-util.js +4 -4
  133. package/dist/core/registry-url.js +456 -0
  134. package/dist/core/state/migrations.js +161 -47
  135. package/dist/core/state-db.js +453 -80
  136. package/dist/core/system-error.js +32 -0
  137. package/dist/core/time.js +2 -12
  138. package/dist/core/write-source.js +0 -18
  139. package/dist/execution/directory-identity.js +52 -0
  140. package/dist/execution/executable-identity.js +107 -0
  141. package/dist/execution/guarded-source.js +398 -0
  142. package/dist/execution/json.js +95 -0
  143. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  144. package/dist/execution/record.js +55 -0
  145. package/dist/execution/resolved-request.js +730 -0
  146. package/dist/execution/source.js +320 -0
  147. package/dist/indexer/bundle-identity-guard.js +5 -4
  148. package/dist/indexer/db/graph-db.js +33 -0
  149. package/dist/indexer/graph/graph-boost.js +3 -4
  150. package/dist/indexer/graph/graph-extraction.js +562 -373
  151. package/dist/indexer/index-written-assets.js +78 -39
  152. package/dist/indexer/indexer.js +471 -432
  153. package/dist/indexer/installations.js +6 -0
  154. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  155. package/dist/indexer/materialize-embeddings.js +155 -0
  156. package/dist/indexer/passes/memory-inference.js +227 -174
  157. package/dist/indexer/passes/metadata.js +263 -118
  158. package/dist/indexer/scan/doc-to-entry.js +7 -10
  159. package/dist/indexer/scan/drain-dir.js +51 -23
  160. package/dist/indexer/search/db-search.js +156 -50
  161. package/dist/indexer/search/fts-query.js +40 -40
  162. package/dist/indexer/search/ranking.js +36 -1
  163. package/dist/indexer/search/search-attribution.js +3 -1
  164. package/dist/indexer/search/search-fields.js +23 -14
  165. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  166. package/dist/indexer/search/search-source.js +7 -16
  167. package/dist/indexer/search/semantic-status.js +10 -1
  168. package/dist/indexer/usage/show-usage.js +105 -0
  169. package/dist/indexer/usage/usage-events.js +7 -2
  170. package/dist/indexer/walk/matchers.js +40 -10
  171. package/dist/indexer/walk/path-resolver.js +5 -2
  172. package/dist/indexer/walk/walker.js +20 -2
  173. package/dist/integrations/agent/builder-shared.js +3 -6
  174. package/dist/integrations/agent/conversation-fallback.js +16 -0
  175. package/dist/integrations/agent/engine-resolution.js +87 -87
  176. package/dist/integrations/agent/execution-cascade.js +566 -0
  177. package/dist/integrations/agent/execution-definitions.js +211 -0
  178. package/dist/integrations/agent/execution-lowering.js +811 -0
  179. package/dist/integrations/agent/execution-preparation.js +67 -0
  180. package/dist/integrations/agent/index.js +0 -2
  181. package/dist/integrations/agent/inline-execution.js +74 -0
  182. package/dist/integrations/agent/model-map.js +515 -0
  183. package/dist/integrations/agent/persona-fallback.js +30 -0
  184. package/dist/integrations/agent/request-lowering.js +186 -0
  185. package/dist/integrations/agent/runner-dispatch.js +230 -37
  186. package/dist/integrations/agent/runner.js +12 -83
  187. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/aider/index.js +0 -1
  189. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  191. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  192. package/dist/integrations/harnesses/claude/index.js +1 -5
  193. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  194. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  195. package/dist/integrations/harnesses/codex/index.js +0 -1
  196. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  197. package/dist/integrations/harnesses/copilot/index.js +0 -1
  198. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  199. package/dist/integrations/harnesses/gemini/index.js +0 -1
  200. package/dist/integrations/harnesses/index.js +4 -44
  201. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  202. package/dist/integrations/harnesses/opencode/index.js +0 -2
  203. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  204. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  205. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  206. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  207. package/dist/integrations/harnesses/openhands/index.js +0 -1
  208. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  209. package/dist/integrations/harnesses/pi/index.js +0 -1
  210. package/dist/integrations/harnesses/shared.js +0 -1
  211. package/dist/integrations/harnesses/types.js +1 -3
  212. package/dist/integrations/lockfile.js +82 -79
  213. package/dist/integrations/session-logs/index.js +6 -17
  214. package/dist/integrations/session-logs/provider-base.js +1 -29
  215. package/dist/llm/client.js +10 -5
  216. package/dist/llm/embedder.js +6 -7
  217. package/dist/llm/embedders/local.js +37 -88
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/graph-extract.js +75 -50
  220. package/dist/llm/index-passes.js +43 -5
  221. package/dist/llm/memory-infer.js +8 -6
  222. package/dist/llm/metadata-enhance.js +5 -3
  223. package/dist/llm/structured-call.js +122 -25
  224. package/dist/output/format-exempt.js +1 -1
  225. package/dist/output/render-registry.js +0 -16
  226. package/dist/output/renderers.js +12 -7
  227. package/dist/output/shapes/curate.js +1 -0
  228. package/dist/output/shapes/helpers.js +10 -2
  229. package/dist/output/shapes/passthrough.js +2 -0
  230. package/dist/output/text/command-format.js +31 -33
  231. package/dist/output/text/health-format.js +1 -29
  232. package/dist/output/text/migrate.js +6 -56
  233. package/dist/output/text/proposal-format.js +16 -1
  234. package/dist/output/text/workflow-format.js +16 -0
  235. package/dist/registry/network.js +279 -0
  236. package/dist/registry/pinned-request-helper.js +247 -0
  237. package/dist/registry/pinned-transport.js +717 -0
  238. package/dist/registry/providers/skills-sh.js +18 -6
  239. package/dist/registry/providers/static-index.js +20 -7
  240. package/dist/registry/resolve.js +53 -28
  241. package/dist/scripts/akm-migrate-node.js +19334 -52269
  242. package/dist/scripts/akm-migrate.js +19270 -51612
  243. package/dist/setup/registry-stash-loader.js +64 -20
  244. package/dist/setup/semantic-assets.js +9 -34
  245. package/dist/setup/setup.js +12 -30
  246. package/dist/setup/source-identity.js +17 -0
  247. package/dist/setup/steps/sources.js +36 -15
  248. package/dist/setup/steps/tasks.js +39 -11
  249. package/dist/sources/providers/git-provider.js +3 -3
  250. package/dist/sources/providers/npm.js +2 -2
  251. package/dist/sources/providers/provider-utils.js +4 -3
  252. package/dist/sources/providers/website.js +11 -7
  253. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  254. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  255. package/dist/sources/website-url.js +73 -0
  256. package/dist/storage/engines/sqlite-migrations.js +81 -26
  257. package/dist/storage/managed-db.js +27 -24
  258. package/dist/storage/repositories/events-repository.js +3 -0
  259. package/dist/storage/repositories/index-connection.js +42 -10
  260. package/dist/storage/repositories/index-entries-repository.js +203 -229
  261. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  262. package/dist/storage/repositories/index-entry-schema.js +255 -0
  263. package/dist/storage/repositories/index-fts-repository.js +64 -71
  264. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  265. package/dist/storage/repositories/index-meta-repository.js +0 -11
  266. package/dist/storage/repositories/index-schema.js +74 -350
  267. package/dist/storage/repositories/index-utility-repository.js +12 -17
  268. package/dist/storage/repositories/index-vec-repository.js +56 -7
  269. package/dist/storage/repositories/proposals-repository.js +4 -127
  270. package/dist/storage/repositories/registry-cache.js +2 -1
  271. package/dist/storage/repositories/task-history-repository.js +20 -40
  272. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  273. package/dist/storage/sqlite-read-snapshot.js +148 -0
  274. package/dist/tasks/backends/cron.js +170 -42
  275. package/dist/tasks/backends/index.js +1 -1
  276. package/dist/tasks/backends/launchd.js +787 -202
  277. package/dist/tasks/backends/schtasks.js +282 -83
  278. package/dist/tasks/embedded.js +7 -7
  279. package/dist/tasks/frozen-script.js +50 -0
  280. package/dist/tasks/resolve-akm-bin.js +5 -1
  281. package/dist/tasks/runner.js +239 -251
  282. package/dist/tasks/runtime-v3.js +281 -0
  283. package/dist/tasks/scheduler-binding.js +272 -0
  284. package/dist/tasks/scheduler-invocation.js +57 -43
  285. package/dist/tasks/scheduler-sync.js +654 -0
  286. package/dist/tasks/source-v3.js +752 -0
  287. package/dist/tasks/standalone-script-entry.js +5 -0
  288. package/dist/tasks/task-id.js +29 -0
  289. package/dist/workflows/authoring/authoring.js +15 -32
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  291. package/dist/workflows/exec/exec-unit.js +7 -28
  292. package/dist/workflows/exec/frozen-judge.js +57 -89
  293. package/dist/workflows/exec/lowering-notices.js +23 -0
  294. package/dist/workflows/exec/native-executor.js +301 -458
  295. package/dist/workflows/exec/param-secrets.js +4 -3
  296. package/dist/workflows/exec/run-workflow.js +26 -32
  297. package/dist/workflows/exec/step-work.js +105 -109
  298. package/dist/workflows/exec/unit-dispatch.js +103 -27
  299. package/dist/workflows/exec/unit-writer.js +3 -3
  300. package/dist/workflows/exec/worktree.js +2 -2
  301. package/dist/workflows/ir/compile.js +86 -72
  302. package/dist/workflows/ir/environment-v4.js +328 -0
  303. package/dist/workflows/ir/freeze-v4.js +122 -0
  304. package/dist/workflows/ir/plan-hash.js +13 -7
  305. package/dist/workflows/ir/schema-v4.js +525 -0
  306. package/dist/workflows/ir/schema.js +25 -284
  307. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  308. package/dist/workflows/parser.js +27 -24
  309. package/dist/workflows/program/schema.js +1 -2
  310. package/dist/workflows/renderer.js +42 -29
  311. package/dist/workflows/resource-limits.js +4 -5
  312. package/dist/workflows/runtime/agent-identity.js +11 -13
  313. package/dist/workflows/runtime/plan-classifier.js +8 -8
  314. package/dist/workflows/runtime/runs.js +27 -43
  315. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  316. package/dist/workflows/source-files.js +373 -0
  317. package/dist/workflows/source-ir/compile.js +196 -0
  318. package/dist/workflows/source-ir/github-yaml.js +577 -0
  319. package/dist/workflows/source-ir/ordering.js +38 -0
  320. package/dist/workflows/source-ir/program.js +50 -0
  321. package/dist/workflows/source-ir/result.js +26 -0
  322. package/dist/workflows/source-ir/schema.js +772 -0
  323. package/dist/workflows/source-ir/semantics.js +242 -0
  324. package/dist/workflows/source-ir/uses.js +14 -0
  325. package/docs/README.md +2 -0
  326. package/docs/migration/README.md +3 -1
  327. package/docs/migration/release-notes/0.9.2.md +55 -0
  328. package/docs/migration/release-notes/README.md +5 -0
  329. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  330. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  331. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  332. package/docs/reference/README.md +1 -0
  333. package/docs/reference/cli.md +230 -98
  334. package/docs/reference/configuration.md +159 -36
  335. package/docs/reference/data-and-telemetry.md +19 -1
  336. package/docs/reference/supported-formats.md +23 -3
  337. package/docs/reference/tasks.md +182 -0
  338. package/docs/reference/workflow-schema.md +91 -40
  339. package/docs/reference/workflows.md +33 -6
  340. package/package.json +10 -6
  341. package/schemas/akm-config.json +372 -224
  342. package/schemas/akm-task.json +324 -80
  343. package/schemas/akm-workflow.json +6 -9
  344. package/dist/core/migration-operation.js +0 -75
  345. package/dist/integrations/agent/model-aliases.js +0 -74
  346. package/dist/tasks/parser.js +0 -380
  347. package/dist/tasks/schema.js +0 -123
  348. package/dist/tasks/validator.js +0 -80
  349. package/dist/workflows/ir/freeze.js +0 -320
  350. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -4,8 +4,8 @@
4
4
  /**
5
5
  * `akm workflow` command family. `run` is the canonical start/resume/execute
6
6
  * surface; the former public `start`, `next`, and `complete` lifecycle is gone.
7
- * Workflows are markdown-only; authoring uses `create --print` and validation
8
- * uses `akm lint --type workflows`.
7
+ * `create --print` emits Markdown; execution accepts peer `.md` and
8
+ * GitHub-shaped `.yml` workflow sources. Validate with `akm lint --type workflows`.
9
9
  */
10
10
  import { getStringArg } from "../cli/parse-args.js";
11
11
  import { defineGroupCommand, defineJsonCommand, EXIT_CODES, output } from "../cli/shared.js";
@@ -232,6 +232,9 @@ export const agentSkillsAdapter = {
232
232
  extensions: [".md"],
233
233
  recognize,
234
234
  validate,
235
+ readCandidates(c, conceptId) {
236
+ return [{ path: path.join(c.root, conceptId, SKILL_MANIFEST), conceptId }];
237
+ },
235
238
  /** A skill places to `<name>/SKILL.md`; the conceptId IS the package directory (spec §4.5). */
236
239
  placeNew(c, conceptId) {
237
240
  return path.join(c.root, conceptId, SKILL_MANIFEST);
@@ -39,9 +39,10 @@
39
39
  * conceptId = `<stash-subdir>/<canonical-name>` — the placement stash-subdir
40
40
  * (`stashDirFor(type)`) followed by the winning type's canonical name
41
41
  * (`deriveCanonicalAssetNameFromStashRoot`: skill = its dir, script keeps its
42
- * extension, markdown strips `.md`, env/task strip their ext, secret/session
43
- * keep the natural path). For markdown types this IS the OKF concept ID
44
- * (path − `.md`); it is the same spelling {@link placeNew} consumes, so
42
+ * extension, Markdown strips `.md`, workflow strips its peer `.md`/`.yml`
43
+ * extension, env/task strip their ext, and secret/session keep the natural
44
+ * path). For Markdown types this IS the OKF concept ID (path − `.md`); it is
45
+ * the same spelling {@link placeNew} consumes, so
45
46
  * recognize/place share one identity (D-R2 resolved the earlier split). The
46
47
  * `type` is carried separately on `IndexDocument.type`, per §0.2 (type ≠
47
48
  * identity), and `entry.name` keeps the BARE canonical name for FTS parity.
@@ -81,11 +82,11 @@
81
82
  import fs from "node:fs";
82
83
  import path from "node:path";
83
84
  import { applyPostContributorFields, applyPreContributorFields, extractPackageMetadata, } from "../../../indexer/passes/metadata.js";
84
- import { parseTaskYaml, taskYamlParseDetail } from "../../../tasks/schema.js";
85
85
  import { assetPathForName, deriveCanonicalAssetNameFromStashRoot, placementTypes, stashDirFor, stashDirNames, } from "../../asset/asset-placement.js";
86
86
  import { parseFrontmatter } from "../../asset/frontmatter.js";
87
- import { recognizeMatch } from "../recognize-match.js";
88
- import { perTypeValidateChecks, skillDirectoryDiagnostics } from "./akm-lint.js";
87
+ import { executionDefaultsFromFrontmatter, renderMarkdownExecutionSource } from "../execution-source.js";
88
+ import { recognizeMatch, recognizePathCandidateMatches } from "../recognize-match.js";
89
+ import { perTypeValidateChecks, skillDirectoryDiagnostics, workflowYamlSourceDiagnostics } from "./akm-lint.js";
89
90
  import { applyFoldedMetadata, foldRecognizedMetadata } from "./akm-metadata.js";
90
91
  import { hashContent, runBaseValidateChecks } from "./shared.js";
91
92
  // `recognizeMatch` + the builtin matcher list moved to the cycle-free leaf
@@ -178,7 +179,6 @@ const DOCUMENT_JSON_CARRIED_FIELDS = [
178
179
  "whenToUse",
179
180
  "toc",
180
181
  "parameters",
181
- "bodyOpening",
182
182
  "source",
183
183
  "category",
184
184
  "supersededBy",
@@ -221,6 +221,8 @@ function indexDocumentFromEntry(entry, base, rendererName) {
221
221
  doc.description = entry.description;
222
222
  if (entry.tags !== undefined)
223
223
  doc.tags = entry.tags;
224
+ if (entry.content !== undefined)
225
+ doc.content = entry.content;
224
226
  if (entry.aliases !== undefined)
225
227
  doc.aliases = entry.aliases;
226
228
  if (entry.searchHints !== undefined)
@@ -243,6 +245,21 @@ function indexDocumentFromEntry(entry, base, rendererName) {
243
245
  doc.derivedFrom = entry.derivedFrom;
244
246
  return doc;
245
247
  }
248
+ function conceptIdForRecognizedType(root, filePath, type) {
249
+ const canonicalName = deriveCanonicalAssetNameFromStashRoot(type, root, filePath);
250
+ if (canonicalName === undefined)
251
+ return undefined;
252
+ const stashDir = stashDirFor(type);
253
+ return stashDir !== undefined ? `${stashDir}/${canonicalName}` : canonicalName;
254
+ }
255
+ function recognizePathCandidates(c, file) {
256
+ if (isReservedFileName(file.fileName) || akmStashAbstains(c.root, file.absPath))
257
+ return [];
258
+ return recognizePathCandidateMatches(file).flatMap((match) => {
259
+ const conceptId = conceptIdForRecognizedType(c.root, file.absPath, match.type);
260
+ return conceptId === undefined ? [] : [conceptId];
261
+ });
262
+ }
246
263
  function recognize(c, file) {
247
264
  // D-R6 (spec §5.1): `index.md` / `log.md` are OKF reserved structural files at
248
265
  // every depth — never items. Excluded BEFORE classification so a directory
@@ -259,20 +276,20 @@ function recognize(c, file) {
259
276
  return null;
260
277
  // Canonical name = the winning type's per-type canonical name (§5.1).
261
278
  // Invalid placements are not projected onto a different identity.
262
- const derived = deriveCanonicalAssetNameFromStashRoot(match.type, c.root, file.absPath);
263
- if (derived === undefined)
279
+ const conceptId = conceptIdForRecognizedType(c.root, file.absPath, match.type);
280
+ if (conceptId === undefined)
264
281
  return null;
265
- const canonicalName = derived;
282
+ const stashDir = stashDirFor(match.type);
283
+ const canonicalName = stashDir !== undefined ? conceptId.slice(stashDir.length + 1) : conceptId;
266
284
  // conceptId = the QUALIFIED `<stash-subdir>/<canonical-name>` spelling
267
- // (ref-grammar decision D-R2): the same form `placeNew` consumes, and for
268
- // markdown types the OKF concept ID (path − .md). Both branches now feed a
285
+ // (ref-grammar decision D-R2): the same form `placeNew` consumes; Markdown
286
+ // types use the OKF concept ID (path − .md), while workflows strip either
287
+ // peer source extension. Both branches feed a
269
288
  // BARE canonicalName (the abstain fallback is the basename, above), so the
270
289
  // stash-subdir is prefixed uniformly — a flat `skills/x.md` yields
271
290
  // `skills/x`, never the un-prefixed `x` or the double-prefixed
272
291
  // `skills/skills/x`. `entry.name` below keeps the BARE canonical name —
273
292
  // identity ≠ search text.
274
- const stashDir = stashDirFor(match.type);
275
- const conceptId = stashDir !== undefined ? `${stashDir}/${canonicalName}` : canonicalName;
276
293
  const dirPath = path.dirname(file.absPath);
277
294
  // Chunk 5 M-b: recognize now carries the FULL index-time metadata surface
278
295
  // (spec §2/§3), reproducing `buildEntryFromFile`'s flat-walk metadata
@@ -307,6 +324,30 @@ function recognize(c, file) {
307
324
  type: match.type,
308
325
  }, match.renderer);
309
326
  }
327
+ /** Render AKM-native command/agent Markdown into the shared execution source. */
328
+ function renderExecutionSource(c, file) {
329
+ const document = recognize(c, file);
330
+ if (document?.type !== "command" && document?.type !== "agent")
331
+ return null;
332
+ if (!document.ref)
333
+ return null;
334
+ const raw = file.content();
335
+ return renderMarkdownExecutionSource({
336
+ kind: document.type === "command" ? "command" : "persona",
337
+ raw,
338
+ identity: {
339
+ ref: document.ref,
340
+ bundle: c.id,
341
+ adapter: "akm",
342
+ file: file.relPath,
343
+ },
344
+ defaults: (data) => executionDefaultsFromFrontmatter(data, {
345
+ kind: document.type === "command" ? "command" : "persona",
346
+ allowTopLevelEngine: true,
347
+ toolsKeys: ["tools"],
348
+ }),
349
+ });
350
+ }
310
351
  /**
311
352
  * A read-only {@link FileContext} backed by an OVERLAY string (a pending
312
353
  * change's content) instead of the live filesystem — the `validate` contract
@@ -357,10 +398,9 @@ function buildOverlayContext(root, relPathInput, raw) {
357
398
  *
358
399
  * Per change (non-delete, readable): the `type` is recovered by running the
359
400
  * SAME sync matcher arbitration ({@link recognizeMatch}) over an OVERLAY
360
- * FileContext ({@link buildOverlayContext}) — no live-FS read. `task` files are
361
- * parsed as pure YAML (mirroring `lint/index.ts`'s `subdir === "tasks"` branch)
362
- * so the TaskLinter's field checks see real data and `missing-updated` never
363
- * fires (frontmatter is `null`); everything else parses via `parseFrontmatter`.
401
+ * FileContext ({@link buildOverlayContext}) — no live-FS read. `task` files
402
+ * keep raw YAML for the canonical task-v3 parser and use no frontmatter;
403
+ * everything else parses via `parseFrontmatter`.
364
404
  * Base checks (shared) then the per-type extra checks run; a `skills/` directory
365
405
  * pass reproduces `SkillLinter.lintDirectory` across the change set.
366
406
  */
@@ -376,23 +416,17 @@ async function validate(c, changes, ctx) {
376
416
  const overlay = buildOverlayContext(c.root, change.path, raw);
377
417
  const match = recognizeMatch(overlay);
378
418
  const type = match?.type;
379
- // Parse strategy per `lint/index.ts`: `task` pure YAML (frontmatter null),
380
- // everything else `parseFrontmatter`.
419
+ // A complete GitHub-shaped `on` + `jobs` document is workflow-owned. It
420
+ // never flows through task-v3 parsing or Markdown base/frontmatter checks.
421
+ if (type === "workflow" && overlay.ext === ".yml") {
422
+ diagnostics.push(...workflowYamlSourceDiagnostics(change.path, raw, change.path, c.root).errors);
423
+ continue;
424
+ }
425
+ // Task parsing belongs to perTypeValidateChecks' canonical v3 parser. Base
426
+ // checks need only a frontmatter-free placeholder here.
381
427
  let parsed;
382
428
  if (type === "task") {
383
- const task = parseTaskYaml(raw);
384
- // A parse failure is its OWN finding: every task rule short-circuits on
385
- // an empty mapping, so collapsing "unparseable" onto `{}` made a broken
386
- // task file validate clean (issue #760). Mirrors the CLI sweep.
387
- if (!task.ok) {
388
- diagnostics.push({
389
- file: change.path,
390
- issue: "invalid-task-yaml",
391
- detail: taskYamlParseDetail(task.error),
392
- fixed: false,
393
- });
394
- }
395
- parsed = { data: task.data, content: raw, frontmatter: null };
429
+ parsed = { data: {}, content: raw, frontmatter: null };
396
430
  }
397
431
  else {
398
432
  const p = parseFrontmatter(raw);
@@ -408,6 +442,7 @@ async function validate(c, changes, ctx) {
408
442
  body: parsed.content,
409
443
  ext: overlay.ext,
410
444
  ctx,
445
+ workspaceRoot: c.root,
411
446
  })));
412
447
  diagnostics.push(...(await skillDirectoryDiagnostics(change.path, seenSkillDirs, ctx)));
413
448
  }
@@ -417,8 +452,9 @@ export const akmAdapter = {
417
452
  id: "akm",
418
453
  version: "0.9.0",
419
454
  // Recognized-extension HINT, derived from what the matchers accept (§6):
420
- // `.md` (markdown types + skill), `.yaml`/`.yml` (task YAML), `.env` (env
421
- // files), and the 16 SCRIPT_EXTENSIONS. This is a
455
+ // `.md` (Markdown types + workflow peer), `.yml` (task and workflow YAML),
456
+ // `.yaml` (task near-miss diagnostics), `.env` (env files), and the 16
457
+ // SCRIPT_EXTENSIONS. This is a
422
458
  // NON-EXHAUSTIVE hint for the akm adapter: recognition is directory-driven
423
459
  // via `recognize()` (e.g. a bare `secrets/<anything>` file with no extension
424
460
  // is a `secret`), so `recognize()` — not this list — is the source of truth.
@@ -445,7 +481,21 @@ export const akmAdapter = {
445
481
  ".kts",
446
482
  ],
447
483
  recognize,
484
+ recognizePathCandidates,
485
+ renderExecutionSource,
448
486
  validate,
487
+ readCandidates(c, conceptId) {
488
+ const posix = conceptId.replace(/\\/g, "/");
489
+ const slash = posix.indexOf("/");
490
+ if (slash <= 0)
491
+ return [{ path: path.join(c.root, `${posix}.md`), conceptId: posix }];
492
+ const head = posix.slice(0, slash);
493
+ const rest = posix.slice(slash + 1);
494
+ const type = stashDirToType(head);
495
+ return type === undefined || rest.length === 0
496
+ ? []
497
+ : [{ path: assetPathForName(type, path.join(c.root, head), rest), conceptId: posix }];
498
+ },
449
499
  /**
450
500
  * Type-driven placement (§5.1), reproducing `path-resolver.ts#buildDiskCandidates`:
451
501
  * the primary candidate is
@@ -40,8 +40,8 @@
40
40
  * ── Cycle-safety (chunk-2 ratchet, baseline 18) ──
41
41
  *
42
42
  * Imported ONLY by `akm-adapter.ts` (no inbound `src/` edge) → can never join a
43
- * cycle. It VALUE-imports `parseWorkflow` (already transitively reachable from
44
- * `akm-adapter` via `matchers.ts`) and the pure predicate `isDangerousEnvKey`
43
+ * cycle. It VALUE-imports the one `compileWorkflowSource` frontend and the pure
44
+ * predicate `isDangerousEnvKey`
45
45
  * from `commands/lint/env-key-rules` — the predicate is imported, not copied,
46
46
  * precisely so the 40+ security-sensitive dangerous-key names cannot drift from
47
47
  * the canonical set; importing it is ratchet-neutral (verified: 18). The small
@@ -52,9 +52,9 @@
52
52
  */
53
53
  import path from "node:path";
54
54
  import { isDangerousEnvKey } from "../../../commands/lint/env-key-rules.js";
55
- import { isPresentTarget, taskFieldProblems } from "../../../tasks/schema.js";
55
+ import { parseTaskV3Yaml, taskV3SourceErrorDetail } from "../../../tasks/source-v3.js";
56
56
  import { compileWorkflowPlan } from "../../../workflows/ir/compile.js";
57
- import { parseWorkflow } from "../../../workflows/parser.js";
57
+ import { compileWorkflowSource } from "../../../workflows/source-ir/compile.js";
58
58
  import { conceptIdForStashFile } from "../../asset/resolve-ref.js";
59
59
  /** Recommended `category` values for facts — `commands/lint/fact-linter.ts:9`. */
60
60
  const KNOWN_CATEGORIES = new Set(["personal", "team", "project", "convention", "meta"]);
@@ -197,11 +197,8 @@ const AKM_SKILL_DIRS = new Set(["skills"]);
197
197
  * (matching the per-subdir call). `file`/`detail` mirror the live check exactly
198
198
  * (relDir + `no SKILL.md in <relDir>/`).
199
199
  *
200
- * `skillDirs` defaults to the akm-native `skills/` placement dir. The tool-dir
201
- * adapters pass their OWN accepted spellings, because opencode also accepts the
202
- * singular `skill/` alias on read (`opencode-adapter.ts` LAYOUT) — with the
203
- * gate hardcoded to `"skills"`, an identical manifest-less package went flagged
204
- * under `skills/` and unflagged under `skill/` (issue #774).
200
+ * `skillDirs` defaults to the akm-native `skills/` placement dir. Tool-dir
201
+ * adapters pass their own canonical placement directory.
205
202
  */
206
203
  export async function skillDirectoryDiagnostics(relPath, seen, ctx, skillDirs = AKM_SKILL_DIRS) {
207
204
  const segments = relPath
@@ -225,7 +222,7 @@ export async function skillDirectoryDiagnostics(relPath, seen, ctx, skillDirs =
225
222
  * routes through `ctx`.
226
223
  */
227
224
  export async function perTypeValidateChecks(args) {
228
- const { type, relPath, raw, data, frontmatter, body, ext, ctx } = args;
225
+ const { type, relPath, raw, data, frontmatter, body, ext, ctx, workspaceRoot } = args;
229
226
  switch (type) {
230
227
  case "command":
231
228
  return nameOrTypeDiagnostics(relPath, data, frontmatter, ["command"]);
@@ -234,9 +231,9 @@ export async function perTypeValidateChecks(args) {
234
231
  case "fact":
235
232
  return factDiagnostics(relPath, data);
236
233
  case "task":
237
- return taskDiagnostics(relPath, data);
234
+ return taskDiagnostics(relPath, raw, workspaceRoot);
238
235
  case "workflow":
239
- // Unified workflows are markdown-only; other extensions are not a lint path.
236
+ // Markdown lint handles `.md`; peer GitHub-shaped `.yml` sources enter through the workflow source adapter.
240
237
  return ext === ".md" ? workflowDiagnostics(relPath, raw, body) : [];
241
238
  case "memory":
242
239
  return memoryDiagnostics(relPath, data, body, ctx);
@@ -274,33 +271,29 @@ export function factDiagnostics(relPath, data) {
274
271
  return [];
275
272
  }
276
273
  /**
277
- * TaskLinter extra check (`task-linter.ts:25-58`). `data` is the parsed YAML.
278
- * Field rules come from the shared {@link taskFieldProblems} (see its doc for
279
- * the lint-vs-parser reconciliation story); this sweep additionally requires
280
- * at least one target.
274
+ * Task validation has one semantic owner: the strict task-v3 source parser.
275
+ * Keeping raw YAML at this boundary preserves duplicate-key, alias/tag,
276
+ * source-location, descriptor, resource-bound, and migration-hint behavior.
281
277
  */
282
- export function taskDiagnostics(relPath, data) {
283
- if (data === null || Object.keys(data).length === 0)
278
+ export function taskDiagnostics(relPath, raw, workspaceRoot) {
279
+ try {
280
+ parseTaskV3Yaml({
281
+ filePath: relPath,
282
+ yaml: raw,
283
+ ...(workspaceRoot ? { workspaceRoot } : {}),
284
+ });
284
285
  return [];
285
- const missing = taskFieldProblems(data);
286
- // Presence, matching the runtime parser's rule (src/tasks/parser.ts): an
287
- // empty string is NOT a target there, so a `workflow: ""` that linted clean
288
- // here failed at run time with MISSING_REQUIRED_ARGUMENT — a file the linter
289
- // called valid but that could never run.
290
- const hasTarget = ["prompt", "workflow", "command"].some((key) => isPresentTarget(data[key]));
291
- if (!hasTarget)
292
- missing.push("prompt, workflow, or command");
293
- if (missing.length > 0) {
286
+ }
287
+ catch (cause) {
294
288
  return [
295
289
  {
296
290
  file: relPath,
297
291
  issue: "invalid-task-yaml",
298
- detail: `missing required fields: ${missing.join(", ")}`,
292
+ detail: taskV3SourceErrorDetail(cause),
299
293
  fixed: false,
300
294
  },
301
295
  ];
302
296
  }
303
- return [];
304
297
  }
305
298
  /**
306
299
  * The first placeholder marker present in a workflow body, or `null`
@@ -313,11 +306,10 @@ export function matchWorkflowPlaceholder(body) {
313
306
  }
314
307
  /**
315
308
  * WorkflowLinter's `invalid-workflow-structure` check (`workflow-linter.ts:48-77`):
316
- * the ERROR half of {@link workflowFrontendDiagnostics}, for callers that only
317
- * ever surface fatal findings the read-only adapter `validate` path and
318
- * `akm migrate`'s stale-workflow probe. A caller that ALSO surfaces the
319
- * advisories must call {@link workflowFrontendDiagnostics} once instead of
320
- * pairing this with a second view. NEVER writes.
309
+ * the ERROR half of {@link workflowFrontendDiagnostics}, for the read-only
310
+ * adapter `validate` path. A caller that ALSO surfaces advisories must call
311
+ * {@link workflowFrontendDiagnostics} once instead of pairing this with a
312
+ * second view. NEVER writes.
321
313
  */
322
314
  export function workflowStructureDiagnostics(relPath, raw, parsePath) {
323
315
  return workflowFrontendDiagnostics(relPath, raw, parsePath).errors;
@@ -333,6 +325,30 @@ export function workflowStructureDiagnostics(relPath, raw, parsePath) {
333
325
  function lineOf(err) {
334
326
  return typeof err.line === "number" && Number.isFinite(err.line) && err.line > 0 ? { line: err.line } : {};
335
327
  }
328
+ /**
329
+ * Compile one peer GitHub-shaped `.yml` source through the shared source-IR
330
+ * frontend. YAML has no Markdown frontmatter/base/stub pass and currently
331
+ * emits fatal source diagnostics only; keeping the same two-channel result
332
+ * shape lets the ordinary sweep and both workflow adapters route it exactly
333
+ * like the established Markdown frontend without inventing another parser.
334
+ */
335
+ export function workflowYamlSourceDiagnostics(relPath, raw, parsePath, workspaceRoot) {
336
+ if (parsePath.includes("/.cache/") || parsePath.includes("/registry/"))
337
+ return { errors: [], warnings: [] };
338
+ const compiled = compileWorkflowSource(raw, { path: parsePath, workspaceRoot });
339
+ if (compiled.ok)
340
+ return { errors: [], warnings: [] };
341
+ return {
342
+ errors: compiled.errors.map((error) => ({
343
+ file: relPath,
344
+ issue: "invalid-workflow-structure",
345
+ detail: error.message,
346
+ fixed: false,
347
+ ...lineOf(error),
348
+ })),
349
+ warnings: [],
350
+ };
351
+ }
336
352
  /**
337
353
  * ONE parse+compile of a workflow through the unified frontend, returning both
338
354
  * halves of what it produces: fatal `invalid-workflow-structure` findings, and
@@ -340,9 +356,8 @@ function lineOf(err) {
340
356
  * no `output:` schema, a reference to an undeclared param). The read-only
341
357
  * `/.cache/` + `/registry/` cached copies are skipped, and nothing is written.
342
358
  *
343
- * `parsePath` is the path handed to `parseWorkflow` (the adapter passes the
344
- * change relPath, the CLI passes the absolute filePath — matching each
345
- * caller's legacy behavior).
359
+ * `parsePath` is the source identity handed to `compileWorkflowSource` (the
360
+ * adapter passes the change relPath; the CLI passes the absolute filePath).
346
361
  *
347
362
  * A caller that surfaces BOTH halves must call this once and route the result
348
363
  * itself. The frontend is expensive — instruction bodies reach
@@ -356,7 +371,7 @@ export function workflowFrontendDiagnostics(relPath, raw, parsePath) {
356
371
  const errors = [];
357
372
  const warnings = [];
358
373
  try {
359
- const result = parseWorkflow(raw, { path: parsePath });
374
+ const result = compileWorkflowSource(raw, { path: parsePath });
360
375
  if (!result.ok) {
361
376
  for (const err of result.errors ?? []) {
362
377
  errors.push({
@@ -369,7 +384,7 @@ export function workflowFrontendDiagnostics(relPath, raw, parsePath) {
369
384
  }
370
385
  return { errors, warnings };
371
386
  }
372
- const compiled = compileWorkflowPlan(result.document, path.basename(parsePath, path.extname(parsePath)));
387
+ const compiled = compileWorkflowPlan(result.ir, path.basename(parsePath, path.extname(parsePath)));
373
388
  if (!compiled.ok) {
374
389
  for (const err of compiled.errors) {
375
390
  errors.push({
@@ -14,7 +14,8 @@
14
14
  * lesson-frontmatter-metadata, memory-frontmatter-metadata,
15
15
  * script-comment-metadata, env-file-metadata, secret-file-metadata,
16
16
  * task-yaml-metadata, session-md-metadata, fact-md-metadata;
17
- * - `workflows/renderer.ts` (1): workflow-document-metadata (workflow-md).
17
+ * - `workflows/renderer.ts` (1): workflow-document-metadata (the historical
18
+ * `workflow-md` renderer id now presents both peer workflow sources).
18
19
  *
19
20
  * ── Cycle-safety (chunk-2 ratchet, baseline 18) ──
20
21
  *
@@ -22,9 +23,9 @@
22
23
  * that file's header), so this leaf can never gain an inbound edge from a cycle
23
24
  * participant and therefore can never JOIN a cycle. It VALUE-imports the pure
24
25
  * parsers each contributor already uses so the fold cannot drift from them:
25
- * `parseFrontmatter`, `parseMarkdownToc`, `parseWorkflow`, `parseWorkflowProgram`,
26
- * `projectProgramParameters`/`programStepInstructions`, and `listKeys`
27
- * (env key-names). `output/renderers.ts` / `workflows/renderer.ts` are the
26
+ * `parseFrontmatter`, `parseMarkdownToc`, the peer-source
27
+ * `compileWorkflowSource` frontend/projection, and env key-name scanning.
28
+ * `output/renderers.ts` / `workflows/renderer.ts` are the
28
29
  * cycle-sensitive modules whose contributor bodies live in this port — their
29
30
  * ONE non-importable helper (`metadata.ts#extractDescriptionFromComments`, a
30
31
  * heavy cycle-participant module) is copied verbatim below as
@@ -46,17 +47,17 @@
46
47
  *
47
48
  * Every contributor that reads/parses content is reproduced with the SAME
48
49
  * try/catch tolerance it has today (toc/memory/session/fact/task swallow parse
49
- * errors). The two workflow contributors throw-to-skip in today's pipeline (a
50
- * broken workflow is dropped by the metadata drain); here the parse is wrapped
50
+ * errors). Workflow source compilation fails closed in the drain (a broken
51
+ * workflow is dropped); here the compile/projection is wrapped
51
52
  * so a broken workflow yields no folded metadata instead of throwing out of the
52
53
  * synchronous `recognize` — the drop-the-entry behavior is an index-drain
53
54
  * concern (Chunk 4/5), not recognition's. On the valid Chunk-0b fixture this
54
55
  * distinction never triggers.
55
56
  */
56
- import fs from "node:fs";
57
- import { parse as parseYaml } from "yaml";
58
- import { listKeys } from "../../../commands/env/env.js";
59
- import { parseWorkflow } from "../../../workflows/parser.js";
57
+ import { scanEnvKeyNames } from "../../../commands/env/env.js";
58
+ import { parseTaskV3Yaml } from "../../../tasks/source-v3.js";
59
+ import { compileWorkflowSource } from "../../../workflows/source-ir/compile.js";
60
+ import { sourceStepInstructions } from "../../../workflows/source-ir/program.js";
60
61
  import { parseFrontmatter } from "../../asset/frontmatter.js";
61
62
  import { parseMarkdownToc } from "../../asset/markdown.js";
62
63
  import { nonEmptyString } from "./shared.js";
@@ -67,14 +68,7 @@ import { nonEmptyString } from "./shared.js";
67
68
  * others are imported). Reads a leading JSDoc block or hash-comment run from the
68
69
  * script file; returns `null` on read/parse miss.
69
70
  */
70
- function extractDescriptionFromComments(filePath) {
71
- let content;
72
- try {
73
- content = fs.readFileSync(filePath, "utf8");
74
- }
75
- catch {
76
- return null;
77
- }
71
+ function extractDescriptionFromComments(content) {
78
72
  const lines = content.split(/\r?\n/).slice(0, 50);
79
73
  const blockStart = lines.findIndex((l) => /^\s*\/\*\*/.test(l));
80
74
  if (blockStart >= 0) {
@@ -210,7 +204,7 @@ export function foldRecognizedMetadata(rendererName, file) {
210
204
  case "script-source": {
211
205
  if (file.ext === ".md")
212
206
  return out;
213
- const commentDesc = extractDescriptionFromComments(file.absPath);
207
+ const commentDesc = extractDescriptionFromComments(file.content());
214
208
  if (commentDesc && !out.description) {
215
209
  out.description = commentDesc;
216
210
  out.source = "comments";
@@ -220,7 +214,7 @@ export function foldRecognizedMetadata(rendererName, file) {
220
214
  }
221
215
  // ── env-file-metadata (env-file) ──
222
216
  case "env-file": {
223
- const { keys } = listKeys(file.absPath);
217
+ const keys = scanEnvKeyNames(file.content());
224
218
  if (keys.length > 0)
225
219
  out.searchHints = keys;
226
220
  out.tags = Array.from(new Set([...(out.tags ?? []), "env", "secrets"]));
@@ -235,18 +229,25 @@ export function foldRecognizedMetadata(rendererName, file) {
235
229
  case "task-yaml": {
236
230
  out.tags = Array.from(new Set([...(out.tags ?? []), "task", "scheduled"]));
237
231
  try {
238
- const doc = parseYaml(file.content());
239
- const data = doc && typeof doc === "object" && !Array.isArray(doc) ? doc : {};
232
+ const task = parseTaskV3Yaml({ yaml: file.content(), filePath: file.absPath, workspaceRoot: file.stashRoot });
240
233
  const hints = new Set();
241
- const schedule = nonEmptyString(data.schedule);
242
- if (schedule)
243
- hints.add(`schedule:${schedule}`);
244
- const workflow = nonEmptyString(data.workflow);
245
- if (workflow)
246
- hints.add(`workflow:${workflow}`);
247
- const prompt = nonEmptyString(data.prompt);
248
- if (prompt)
249
- hints.add(`prompt:${prompt}`);
234
+ for (const binding of task.triggers.schedules)
235
+ hints.add(`schedule:${binding.cron}`);
236
+ if (task.target.kind === "uses") {
237
+ if (task.target.uses.kind === "workflow")
238
+ hints.add(`workflow:${task.target.uses.ref}`);
239
+ else if (task.target.uses.kind === "command")
240
+ hints.add(`prompt:${task.target.uses.ref}`);
241
+ else if (task.target.command?.kind === "inline")
242
+ hints.add(`prompt:${task.target.command.content}`);
243
+ else if (task.target.command?.kind === "stored")
244
+ hints.add(`prompt:${task.target.command.ref}`);
245
+ else
246
+ hints.add(`uses:${task.target.uses.ref}`);
247
+ }
248
+ else {
249
+ hints.add(`run:${task.target.run}`);
250
+ }
250
251
  finalizeHints(out, hints);
251
252
  }
252
253
  catch {
@@ -301,26 +302,25 @@ export function foldRecognizedMetadata(rendererName, file) {
301
302
  }
302
303
  return out;
303
304
  }
304
- // ── workflow-document-metadata (workflow-md) ──
305
+ // ── workflow-document-metadata (historical renderer id `workflow-md`, peer sources) ──
305
306
  case "workflow-md": {
306
307
  try {
307
- const result = parseWorkflow(file.content(), { path: file.relPath });
308
+ const result = compileWorkflowSource(file.content(), { path: file.relPath, workspaceRoot: file.stashRoot });
308
309
  if (!result.ok)
309
310
  return out;
310
- const doc = result.document;
311
+ const sourceIr = result.ir;
311
312
  const hints = new Set();
312
- if (doc.preamble)
313
- hints.add(doc.preamble);
314
- for (const step of doc.steps) {
313
+ if (sourceIr.preamble)
314
+ hints.add(sourceIr.preamble);
315
+ for (const step of sourceIr.jobs.flatMap((job) => job.steps)) {
315
316
  hints.add(step.id);
316
- if (step.instructions)
317
- hints.add(step.instructions.text);
318
- if (step.gateRubric)
319
- hints.add(step.gateRubric.text);
317
+ hints.add(sourceStepInstructions(step));
318
+ if (step.gate?.rubric)
319
+ hints.add(step.gate.rubric);
320
320
  }
321
321
  out.searchHints = Array.from(hints).filter(Boolean);
322
- if (doc.params) {
323
- const parameters = Object.entries(doc.params).map(([name, schema]) => {
322
+ if (sourceIr.params) {
323
+ const parameters = Object.entries(sourceIr.params).map(([name, schema]) => {
324
324
  const description = schema.description;
325
325
  return { name, ...(typeof description === "string" && description ? { description } : {}) };
326
326
  });
@@ -348,8 +348,8 @@ export function foldRecognizedMetadata(rendererName, file) {
348
348
  * Precedence, verified against every case in {@link foldRecognizedMetadata}:
349
349
  * - description/source/confidence travel together and only when the entry has
350
350
  * no description yet (contributors gate on `!entry.description`); a
351
- * contributor that sets description WITHOUT source/confidence
352
- * (workflow-program-yaml) leaves those untouched — mirrored by the
351
+ * contributor that sets description WITHOUT source/confidence leaves those
352
+ * untouched — mirrored by the
353
353
  * `folded.source`/`folded.confidence` guards below;
354
354
  * - tags UNION into the existing set (order-preserving Set dedup), matching the
355
355
  * contributors' `Array.from(new Set([...entry.tags, ...added]))`;