akm-cli 0.9.1 → 0.9.2-alpha.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 (341) hide show
  1. package/CHANGELOG.md +88 -0
  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/tasks/core/extract.yml +6 -5
  12. package/dist/assets/tasks/core/improve.yml +6 -5
  13. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  14. package/dist/assets/tasks/core/sync.yml +6 -5
  15. package/dist/assets/tasks/core/version-check.yml +6 -5
  16. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  17. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  21. package/dist/cli/confirm.js +2 -2
  22. package/dist/cli/parse-args.js +3 -24
  23. package/dist/cli/retired-commands.js +1 -1
  24. package/dist/cli/shared.js +2 -2
  25. package/dist/cli.js +11 -9
  26. package/dist/commands/agent/agent-dispatch.js +55 -89
  27. package/dist/commands/agent/contribute-cli.js +12 -45
  28. package/dist/commands/command/builtin-action.js +32 -0
  29. package/dist/commands/command/command-cli.js +99 -0
  30. package/dist/commands/command/command-execution.js +308 -0
  31. package/dist/commands/command/execution-source-loader.js +176 -0
  32. package/dist/commands/command/portable-template.js +60 -0
  33. package/dist/commands/config-cli.js +10 -4
  34. package/dist/commands/env/env.js +4 -2
  35. package/dist/commands/feedback-cli.js +1 -1
  36. package/dist/commands/health/checks.js +241 -29
  37. package/dist/commands/health/html-report.js +0 -14
  38. package/dist/commands/health/report-view-model.js +0 -1
  39. package/dist/commands/health/surfaces.js +6 -7
  40. package/dist/commands/health/types.js +0 -2
  41. package/dist/commands/health.js +63 -18
  42. package/dist/commands/improve/collapse-detector.js +2 -2
  43. package/dist/commands/improve/consolidate.js +251 -214
  44. package/dist/commands/improve/distill/promote-memory.js +71 -34
  45. package/dist/commands/improve/distill/quality-gate.js +17 -5
  46. package/dist/commands/improve/distill.js +232 -155
  47. package/dist/commands/improve/eligibility.js +112 -79
  48. package/dist/commands/improve/execution.js +57 -0
  49. package/dist/commands/improve/extract-cli.js +5 -5
  50. package/dist/commands/improve/extract.js +521 -342
  51. package/dist/commands/improve/improve-strategies.js +43 -14
  52. package/dist/commands/improve/improve.js +249 -29
  53. package/dist/commands/improve/loop-stages.js +11 -17
  54. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  55. package/dist/commands/improve/outcome-loop.js +22 -38
  56. package/dist/commands/improve/planner.js +134 -0
  57. package/dist/commands/improve/preparation.js +730 -409
  58. package/dist/commands/improve/reflect.js +386 -223
  59. package/dist/commands/improve/run-context.js +3 -4
  60. package/dist/commands/improve/salience.js +6 -58
  61. package/dist/commands/improve/session-asset.js +12 -12
  62. package/dist/commands/lint/index.js +101 -29
  63. package/dist/commands/migrate-cli.js +11 -69
  64. package/dist/commands/migration-tool.js +6 -9
  65. package/dist/commands/models-cli.js +27 -0
  66. package/dist/commands/proposal/drain.js +258 -186
  67. package/dist/commands/proposal/proposal-cli.js +32 -10
  68. package/dist/commands/proposal/proposal.js +2 -5
  69. package/dist/commands/proposal/propose.js +192 -172
  70. package/dist/commands/proposal/repository.js +54 -91
  71. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  72. package/dist/commands/read/curate.js +10 -0
  73. package/dist/commands/read/registry-search.js +25 -9
  74. package/dist/commands/read/remember-cli.js +14 -2
  75. package/dist/commands/read/search.js +10 -4
  76. package/dist/commands/read/show.js +139 -153
  77. package/dist/commands/registry-cli.js +16 -7
  78. package/dist/commands/remember.js +33 -18
  79. package/dist/commands/sources/add-cli.js +19 -178
  80. package/dist/commands/sources/bundle-cli.js +15 -3
  81. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  82. package/dist/commands/sources/info.js +2 -1
  83. package/dist/commands/sources/installed-stashes.js +835 -152
  84. package/dist/commands/sources/schema-repair.js +174 -95
  85. package/dist/commands/sources/self-update.js +30 -74
  86. package/dist/commands/sources/source-add.js +3 -5
  87. package/dist/commands/sources/sources-cli.js +2 -15
  88. package/dist/commands/sources/update-transaction.js +220 -0
  89. package/dist/commands/tasks/tasks-cli.js +3 -3
  90. package/dist/commands/tasks/tasks.js +736 -317
  91. package/dist/commands/workflow-cli.js +2 -2
  92. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  93. package/dist/core/adapter/adapters/akm-adapter.js +83 -34
  94. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  95. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  96. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  97. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  98. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  99. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  100. package/dist/core/adapter/adapters/index.js +0 -9
  101. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  102. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  103. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  104. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  105. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  106. package/dist/core/adapter/execution-source.js +308 -0
  107. package/dist/core/adapter/recognize-match.js +36 -13
  108. package/dist/core/adapter/registry.js +0 -9
  109. package/dist/core/asset/stash-meta.js +94 -4
  110. package/dist/core/common.js +6 -11
  111. package/dist/core/config/config-io.js +3 -3
  112. package/dist/core/config/config-schema.js +18 -40
  113. package/dist/core/config/config-sources.js +11 -21
  114. package/dist/core/config/config-walker.js +31 -13
  115. package/dist/core/config/config.js +21 -20
  116. package/dist/core/config/schema/engines.js +8 -7
  117. package/dist/core/config/schema/improve-processes.js +29 -5
  118. package/dist/core/config/schema/primitives.js +1 -23
  119. package/dist/core/config/schema/sources-bundles.js +13 -16
  120. package/dist/core/errors.js +2 -0
  121. package/dist/core/events.js +68 -32
  122. package/dist/core/extra-params.js +1 -0
  123. package/dist/core/improve-result.js +315 -0
  124. package/dist/core/lesson-lint.js +0 -6
  125. package/dist/core/maintenance-barrier.js +4 -4
  126. package/dist/core/network-policy.js +152 -0
  127. package/dist/core/paths.js +1 -1
  128. package/dist/core/recognition-util.js +4 -4
  129. package/dist/core/registry-url.js +456 -0
  130. package/dist/core/state/migrations.js +161 -47
  131. package/dist/core/state-db.js +453 -80
  132. package/dist/core/system-error.js +32 -0
  133. package/dist/core/time.js +2 -12
  134. package/dist/core/write-source.js +0 -18
  135. package/dist/execution/directory-identity.js +52 -0
  136. package/dist/execution/executable-identity.js +107 -0
  137. package/dist/execution/guarded-source.js +398 -0
  138. package/dist/execution/json.js +95 -0
  139. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  140. package/dist/execution/record.js +55 -0
  141. package/dist/execution/resolved-request.js +730 -0
  142. package/dist/execution/source.js +320 -0
  143. package/dist/indexer/bundle-identity-guard.js +5 -4
  144. package/dist/indexer/db/graph-db.js +33 -0
  145. package/dist/indexer/graph/graph-boost.js +3 -4
  146. package/dist/indexer/graph/graph-extraction.js +562 -373
  147. package/dist/indexer/index-written-assets.js +65 -34
  148. package/dist/indexer/indexer.js +443 -222
  149. package/dist/indexer/installations.js +6 -0
  150. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  151. package/dist/indexer/passes/memory-inference.js +227 -174
  152. package/dist/indexer/scan/doc-to-entry.js +7 -9
  153. package/dist/indexer/scan/drain-dir.js +51 -23
  154. package/dist/indexer/search/db-search.js +98 -22
  155. package/dist/indexer/search/search-fields.js +1 -1
  156. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  157. package/dist/indexer/search/search-source.js +7 -16
  158. package/dist/indexer/search/semantic-status.js +10 -1
  159. package/dist/indexer/usage/show-usage.js +105 -0
  160. package/dist/indexer/usage/usage-events.js +7 -2
  161. package/dist/indexer/walk/matchers.js +40 -10
  162. package/dist/indexer/walk/path-resolver.js +5 -2
  163. package/dist/indexer/walk/walker.js +20 -2
  164. package/dist/integrations/agent/builder-shared.js +3 -6
  165. package/dist/integrations/agent/conversation-fallback.js +16 -0
  166. package/dist/integrations/agent/engine-resolution.js +87 -87
  167. package/dist/integrations/agent/execution-cascade.js +566 -0
  168. package/dist/integrations/agent/execution-definitions.js +211 -0
  169. package/dist/integrations/agent/execution-lowering.js +811 -0
  170. package/dist/integrations/agent/execution-preparation.js +67 -0
  171. package/dist/integrations/agent/index.js +0 -2
  172. package/dist/integrations/agent/inline-execution.js +74 -0
  173. package/dist/integrations/agent/model-map.js +515 -0
  174. package/dist/integrations/agent/persona-fallback.js +30 -0
  175. package/dist/integrations/agent/request-lowering.js +186 -0
  176. package/dist/integrations/agent/runner-dispatch.js +230 -37
  177. package/dist/integrations/agent/runner.js +12 -83
  178. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  179. package/dist/integrations/harnesses/aider/index.js +0 -1
  180. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  181. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  182. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  183. package/dist/integrations/harnesses/claude/index.js +1 -5
  184. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  185. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  186. package/dist/integrations/harnesses/codex/index.js +0 -1
  187. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/copilot/index.js +0 -1
  189. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/gemini/index.js +0 -1
  191. package/dist/integrations/harnesses/index.js +4 -44
  192. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  193. package/dist/integrations/harnesses/opencode/index.js +0 -2
  194. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  195. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  196. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  197. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  198. package/dist/integrations/harnesses/openhands/index.js +0 -1
  199. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  200. package/dist/integrations/harnesses/pi/index.js +0 -1
  201. package/dist/integrations/harnesses/shared.js +0 -1
  202. package/dist/integrations/harnesses/types.js +1 -3
  203. package/dist/integrations/lockfile.js +82 -79
  204. package/dist/integrations/session-logs/index.js +6 -17
  205. package/dist/integrations/session-logs/provider-base.js +1 -29
  206. package/dist/llm/client.js +10 -5
  207. package/dist/llm/embedder.js +6 -7
  208. package/dist/llm/embedders/local.js +37 -88
  209. package/dist/llm/embedders/types.js +1 -1
  210. package/dist/llm/graph-extract.js +75 -50
  211. package/dist/llm/index-passes.js +43 -5
  212. package/dist/llm/memory-infer.js +8 -6
  213. package/dist/llm/metadata-enhance.js +5 -3
  214. package/dist/llm/structured-call.js +122 -25
  215. package/dist/output/format-exempt.js +1 -1
  216. package/dist/output/render-registry.js +0 -16
  217. package/dist/output/renderers.js +12 -7
  218. package/dist/output/shapes/curate.js +1 -0
  219. package/dist/output/shapes/helpers.js +10 -2
  220. package/dist/output/shapes/passthrough.js +2 -0
  221. package/dist/output/text/command-format.js +28 -32
  222. package/dist/output/text/health-format.js +1 -29
  223. package/dist/output/text/migrate.js +6 -56
  224. package/dist/output/text/proposal-format.js +16 -1
  225. package/dist/output/text/workflow-format.js +16 -0
  226. package/dist/registry/network.js +279 -0
  227. package/dist/registry/pinned-request-helper.js +247 -0
  228. package/dist/registry/pinned-transport.js +717 -0
  229. package/dist/registry/providers/skills-sh.js +18 -6
  230. package/dist/registry/providers/static-index.js +20 -7
  231. package/dist/registry/resolve.js +53 -28
  232. package/dist/scripts/akm-migrate-node.js +19122 -52218
  233. package/dist/scripts/akm-migrate.js +19064 -51567
  234. package/dist/setup/registry-stash-loader.js +64 -20
  235. package/dist/setup/semantic-assets.js +9 -34
  236. package/dist/setup/setup.js +12 -30
  237. package/dist/setup/source-identity.js +17 -0
  238. package/dist/setup/steps/sources.js +36 -15
  239. package/dist/setup/steps/tasks.js +39 -11
  240. package/dist/sources/providers/git-provider.js +3 -3
  241. package/dist/sources/providers/npm.js +2 -2
  242. package/dist/sources/providers/provider-utils.js +4 -3
  243. package/dist/sources/providers/website.js +11 -7
  244. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  245. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  246. package/dist/sources/website-url.js +73 -0
  247. package/dist/storage/engines/sqlite-migrations.js +81 -26
  248. package/dist/storage/managed-db.js +27 -24
  249. package/dist/storage/repositories/events-repository.js +3 -0
  250. package/dist/storage/repositories/index-connection.js +42 -10
  251. package/dist/storage/repositories/index-entries-repository.js +167 -207
  252. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  253. package/dist/storage/repositories/index-entry-schema.js +255 -0
  254. package/dist/storage/repositories/index-fts-repository.js +10 -10
  255. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  256. package/dist/storage/repositories/index-meta-repository.js +0 -11
  257. package/dist/storage/repositories/index-schema.js +71 -342
  258. package/dist/storage/repositories/index-utility-repository.js +12 -17
  259. package/dist/storage/repositories/index-vec-repository.js +1 -1
  260. package/dist/storage/repositories/proposals-repository.js +4 -127
  261. package/dist/storage/repositories/registry-cache.js +2 -1
  262. package/dist/storage/repositories/task-history-repository.js +20 -40
  263. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  264. package/dist/storage/sqlite-read-snapshot.js +148 -0
  265. package/dist/tasks/backends/cron.js +170 -42
  266. package/dist/tasks/backends/index.js +1 -1
  267. package/dist/tasks/backends/launchd.js +787 -202
  268. package/dist/tasks/backends/schtasks.js +282 -83
  269. package/dist/tasks/embedded.js +7 -7
  270. package/dist/tasks/frozen-script.js +50 -0
  271. package/dist/tasks/resolve-akm-bin.js +5 -1
  272. package/dist/tasks/runner.js +239 -251
  273. package/dist/tasks/runtime-v3.js +281 -0
  274. package/dist/tasks/scheduler-binding.js +272 -0
  275. package/dist/tasks/scheduler-invocation.js +57 -43
  276. package/dist/tasks/scheduler-sync.js +654 -0
  277. package/dist/tasks/source-v3.js +752 -0
  278. package/dist/tasks/standalone-script-entry.js +5 -0
  279. package/dist/tasks/task-id.js +29 -0
  280. package/dist/workflows/authoring/authoring.js +15 -32
  281. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  282. package/dist/workflows/exec/exec-unit.js +7 -28
  283. package/dist/workflows/exec/frozen-judge.js +57 -89
  284. package/dist/workflows/exec/lowering-notices.js +23 -0
  285. package/dist/workflows/exec/native-executor.js +301 -458
  286. package/dist/workflows/exec/param-secrets.js +4 -3
  287. package/dist/workflows/exec/run-workflow.js +26 -32
  288. package/dist/workflows/exec/step-work.js +105 -109
  289. package/dist/workflows/exec/unit-dispatch.js +103 -27
  290. package/dist/workflows/exec/unit-writer.js +3 -3
  291. package/dist/workflows/exec/worktree.js +2 -2
  292. package/dist/workflows/ir/compile.js +86 -72
  293. package/dist/workflows/ir/environment-v4.js +328 -0
  294. package/dist/workflows/ir/freeze-v4.js +122 -0
  295. package/dist/workflows/ir/plan-hash.js +13 -7
  296. package/dist/workflows/ir/schema-v4.js +525 -0
  297. package/dist/workflows/ir/schema.js +25 -284
  298. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  299. package/dist/workflows/parser.js +27 -24
  300. package/dist/workflows/program/schema.js +1 -2
  301. package/dist/workflows/renderer.js +42 -29
  302. package/dist/workflows/resource-limits.js +4 -5
  303. package/dist/workflows/runtime/agent-identity.js +11 -13
  304. package/dist/workflows/runtime/plan-classifier.js +8 -8
  305. package/dist/workflows/runtime/runs.js +27 -43
  306. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  307. package/dist/workflows/source-files.js +373 -0
  308. package/dist/workflows/source-ir/compile.js +196 -0
  309. package/dist/workflows/source-ir/github-yaml.js +577 -0
  310. package/dist/workflows/source-ir/ordering.js +38 -0
  311. package/dist/workflows/source-ir/program.js +50 -0
  312. package/dist/workflows/source-ir/result.js +26 -0
  313. package/dist/workflows/source-ir/schema.js +772 -0
  314. package/dist/workflows/source-ir/semantics.js +242 -0
  315. package/dist/workflows/source-ir/uses.js +14 -0
  316. package/docs/README.md +2 -0
  317. package/docs/migration/README.md +3 -1
  318. package/docs/migration/release-notes/0.9.2.md +55 -0
  319. package/docs/migration/release-notes/README.md +5 -0
  320. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  321. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  322. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  323. package/docs/reference/README.md +1 -0
  324. package/docs/reference/cli.md +230 -98
  325. package/docs/reference/configuration.md +153 -27
  326. package/docs/reference/data-and-telemetry.md +19 -1
  327. package/docs/reference/supported-formats.md +23 -3
  328. package/docs/reference/tasks.md +182 -0
  329. package/docs/reference/workflow-schema.md +91 -40
  330. package/docs/reference/workflows.md +33 -6
  331. package/package.json +10 -6
  332. package/schemas/akm-config.json +372 -216
  333. package/schemas/akm-task.json +324 -80
  334. package/schemas/akm-workflow.json +6 -9
  335. package/dist/core/migration-operation.js +0 -75
  336. package/dist/integrations/agent/model-aliases.js +0 -74
  337. package/dist/tasks/parser.js +0 -380
  338. package/dist/tasks/schema.js +0 -123
  339. package/dist/tasks/validator.js +0 -80
  340. package/dist/workflows/ir/freeze.js +0 -320
  341. 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
@@ -243,6 +244,21 @@ function indexDocumentFromEntry(entry, base, rendererName) {
243
244
  doc.derivedFrom = entry.derivedFrom;
244
245
  return doc;
245
246
  }
247
+ function conceptIdForRecognizedType(root, filePath, type) {
248
+ const canonicalName = deriveCanonicalAssetNameFromStashRoot(type, root, filePath);
249
+ if (canonicalName === undefined)
250
+ return undefined;
251
+ const stashDir = stashDirFor(type);
252
+ return stashDir !== undefined ? `${stashDir}/${canonicalName}` : canonicalName;
253
+ }
254
+ function recognizePathCandidates(c, file) {
255
+ if (isReservedFileName(file.fileName) || akmStashAbstains(c.root, file.absPath))
256
+ return [];
257
+ return recognizePathCandidateMatches(file).flatMap((match) => {
258
+ const conceptId = conceptIdForRecognizedType(c.root, file.absPath, match.type);
259
+ return conceptId === undefined ? [] : [conceptId];
260
+ });
261
+ }
246
262
  function recognize(c, file) {
247
263
  // D-R6 (spec §5.1): `index.md` / `log.md` are OKF reserved structural files at
248
264
  // every depth — never items. Excluded BEFORE classification so a directory
@@ -259,20 +275,20 @@ function recognize(c, file) {
259
275
  return null;
260
276
  // Canonical name = the winning type's per-type canonical name (§5.1).
261
277
  // Invalid placements are not projected onto a different identity.
262
- const derived = deriveCanonicalAssetNameFromStashRoot(match.type, c.root, file.absPath);
263
- if (derived === undefined)
278
+ const conceptId = conceptIdForRecognizedType(c.root, file.absPath, match.type);
279
+ if (conceptId === undefined)
264
280
  return null;
265
- const canonicalName = derived;
281
+ const stashDir = stashDirFor(match.type);
282
+ const canonicalName = stashDir !== undefined ? conceptId.slice(stashDir.length + 1) : conceptId;
266
283
  // 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
284
+ // (ref-grammar decision D-R2): the same form `placeNew` consumes; Markdown
285
+ // types use the OKF concept ID (path − .md), while workflows strip either
286
+ // peer source extension. Both branches feed a
269
287
  // BARE canonicalName (the abstain fallback is the basename, above), so the
270
288
  // stash-subdir is prefixed uniformly — a flat `skills/x.md` yields
271
289
  // `skills/x`, never the un-prefixed `x` or the double-prefixed
272
290
  // `skills/skills/x`. `entry.name` below keeps the BARE canonical name —
273
291
  // identity ≠ search text.
274
- const stashDir = stashDirFor(match.type);
275
- const conceptId = stashDir !== undefined ? `${stashDir}/${canonicalName}` : canonicalName;
276
292
  const dirPath = path.dirname(file.absPath);
277
293
  // Chunk 5 M-b: recognize now carries the FULL index-time metadata surface
278
294
  // (spec §2/§3), reproducing `buildEntryFromFile`'s flat-walk metadata
@@ -307,6 +323,30 @@ function recognize(c, file) {
307
323
  type: match.type,
308
324
  }, match.renderer);
309
325
  }
326
+ /** Render AKM-native command/agent Markdown into the shared execution source. */
327
+ function renderExecutionSource(c, file) {
328
+ const document = recognize(c, file);
329
+ if (document?.type !== "command" && document?.type !== "agent")
330
+ return null;
331
+ if (!document.ref)
332
+ return null;
333
+ const raw = file.content();
334
+ return renderMarkdownExecutionSource({
335
+ kind: document.type === "command" ? "command" : "persona",
336
+ raw,
337
+ identity: {
338
+ ref: document.ref,
339
+ bundle: c.id,
340
+ adapter: "akm",
341
+ file: file.relPath,
342
+ },
343
+ defaults: (data) => executionDefaultsFromFrontmatter(data, {
344
+ kind: document.type === "command" ? "command" : "persona",
345
+ allowTopLevelEngine: true,
346
+ toolsKeys: ["tools"],
347
+ }),
348
+ });
349
+ }
310
350
  /**
311
351
  * A read-only {@link FileContext} backed by an OVERLAY string (a pending
312
352
  * change's content) instead of the live filesystem — the `validate` contract
@@ -357,10 +397,9 @@ function buildOverlayContext(root, relPathInput, raw) {
357
397
  *
358
398
  * Per change (non-delete, readable): the `type` is recovered by running the
359
399
  * 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`.
400
+ * FileContext ({@link buildOverlayContext}) — no live-FS read. `task` files
401
+ * keep raw YAML for the canonical task-v3 parser and use no frontmatter;
402
+ * everything else parses via `parseFrontmatter`.
364
403
  * Base checks (shared) then the per-type extra checks run; a `skills/` directory
365
404
  * pass reproduces `SkillLinter.lintDirectory` across the change set.
366
405
  */
@@ -376,23 +415,17 @@ async function validate(c, changes, ctx) {
376
415
  const overlay = buildOverlayContext(c.root, change.path, raw);
377
416
  const match = recognizeMatch(overlay);
378
417
  const type = match?.type;
379
- // Parse strategy per `lint/index.ts`: `task` pure YAML (frontmatter null),
380
- // everything else `parseFrontmatter`.
418
+ // A complete GitHub-shaped `on` + `jobs` document is workflow-owned. It
419
+ // never flows through task-v3 parsing or Markdown base/frontmatter checks.
420
+ if (type === "workflow" && overlay.ext === ".yml") {
421
+ diagnostics.push(...workflowYamlSourceDiagnostics(change.path, raw, change.path, c.root).errors);
422
+ continue;
423
+ }
424
+ // Task parsing belongs to perTypeValidateChecks' canonical v3 parser. Base
425
+ // checks need only a frontmatter-free placeholder here.
381
426
  let parsed;
382
427
  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 };
428
+ parsed = { data: {}, content: raw, frontmatter: null };
396
429
  }
397
430
  else {
398
431
  const p = parseFrontmatter(raw);
@@ -408,6 +441,7 @@ async function validate(c, changes, ctx) {
408
441
  body: parsed.content,
409
442
  ext: overlay.ext,
410
443
  ctx,
444
+ workspaceRoot: c.root,
411
445
  })));
412
446
  diagnostics.push(...(await skillDirectoryDiagnostics(change.path, seenSkillDirs, ctx)));
413
447
  }
@@ -417,8 +451,9 @@ export const akmAdapter = {
417
451
  id: "akm",
418
452
  version: "0.9.0",
419
453
  // 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
454
+ // `.md` (Markdown types + workflow peer), `.yml` (task and workflow YAML),
455
+ // `.yaml` (task near-miss diagnostics), `.env` (env files), and the 16
456
+ // SCRIPT_EXTENSIONS. This is a
422
457
  // NON-EXHAUSTIVE hint for the akm adapter: recognition is directory-driven
423
458
  // via `recognize()` (e.g. a bare `secrets/<anything>` file with no extension
424
459
  // is a `secret`), so `recognize()` — not this list — is the source of truth.
@@ -445,7 +480,21 @@ export const akmAdapter = {
445
480
  ".kts",
446
481
  ],
447
482
  recognize,
483
+ recognizePathCandidates,
484
+ renderExecutionSource,
448
485
  validate,
486
+ readCandidates(c, conceptId) {
487
+ const posix = conceptId.replace(/\\/g, "/");
488
+ const slash = posix.indexOf("/");
489
+ if (slash <= 0)
490
+ return [{ path: path.join(c.root, `${posix}.md`), conceptId: posix }];
491
+ const head = posix.slice(0, slash);
492
+ const rest = posix.slice(slash + 1);
493
+ const type = stashDirToType(head);
494
+ return type === undefined || rest.length === 0
495
+ ? []
496
+ : [{ path: assetPathForName(type, path.join(c.root, head), rest), conceptId: posix }];
497
+ },
449
498
  /**
450
499
  * Type-driven placement (§5.1), reproducing `path-resolver.ts#buildDiskCandidates`:
451
500
  * 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]))`;