stageflow 0.23.0 → 0.27.0

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 (511) hide show
  1. package/README.md +24 -5
  2. package/dist/a2a/configDiscovery.d.ts +9 -0
  3. package/dist/a2a/configDiscovery.js +18 -0
  4. package/dist/a2a/contracts.d.ts +94 -0
  5. package/dist/a2a/contracts.js +164 -0
  6. package/dist/a2a/limits.d.ts +14 -0
  7. package/dist/a2a/limits.js +29 -0
  8. package/dist/a2a/registry.d.ts +46 -0
  9. package/dist/a2a/registry.js +178 -0
  10. package/dist/a2a/server.d.ts +36 -0
  11. package/dist/a2a/server.js +344 -0
  12. package/dist/a2a/service.d.ts +97 -0
  13. package/dist/a2a/service.js +545 -0
  14. package/dist/a2a/store.d.ts +109 -0
  15. package/dist/a2a/store.js +202 -0
  16. package/dist/agent/activity.d.ts +3 -0
  17. package/dist/agent/bootProviderConfig.d.ts +21 -0
  18. package/dist/agent/bootProviderConfig.js +74 -0
  19. package/dist/agent/piAdapter.d.ts +9 -1
  20. package/dist/agent/piAdapter.js +58 -3
  21. package/dist/agent/piIsolatedMcp.d.ts +2 -0
  22. package/dist/agent/piIsolatedMcp.js +10 -2
  23. package/dist/agent/port.d.ts +6 -0
  24. package/dist/agent/streamLogRedact.d.ts +1 -1
  25. package/dist/agent/streamLogRedact.js +4 -11
  26. package/dist/cli/a2aCommand.d.ts +8 -0
  27. package/dist/cli/a2aCommand.js +131 -0
  28. package/dist/cli/artifactCommand.js +9 -16
  29. package/dist/cli/backupCommand.d.ts +9 -0
  30. package/dist/cli/backupCommand.js +111 -0
  31. package/dist/cli/debugRunCommand.d.ts +10 -0
  32. package/dist/cli/debugRunCommand.js +109 -0
  33. package/dist/cli/doctorCommand.d.ts +32 -0
  34. package/dist/cli/doctorCommand.js +453 -0
  35. package/dist/cli/envelopeCommand.js +9 -2
  36. package/dist/cli/exportAllCommand.d.ts +55 -0
  37. package/dist/cli/exportAllCommand.js +218 -0
  38. package/dist/cli/exportRunCommand.js +23 -21
  39. package/dist/cli/hostClient.d.ts +21 -3
  40. package/dist/cli/hostClient.js +93 -12
  41. package/dist/cli/initTemplates.d.ts +1 -1
  42. package/dist/cli/initTemplates.js +8 -1
  43. package/dist/cli/resolveSafeOutPath.d.ts +2 -0
  44. package/dist/cli/resolveSafeOutPath.js +40 -0
  45. package/dist/cli/restoreCommand.d.ts +10 -0
  46. package/dist/cli/restoreCommand.js +90 -0
  47. package/dist/cli/runCommand.d.ts +3 -2
  48. package/dist/cli/runCommand.js +93 -11
  49. package/dist/cli/runOutput.d.ts +4 -0
  50. package/dist/cli/runOutput.js +33 -7
  51. package/dist/cli/runStageCommand.d.ts +34 -0
  52. package/dist/cli/runStageCommand.js +426 -0
  53. package/dist/cli/runsCommand.d.ts +1 -1
  54. package/dist/cli/runsCommand.js +162 -8
  55. package/dist/cli.js +190 -30
  56. package/dist/config/catalogRelativePath.d.ts +62 -0
  57. package/dist/config/catalogRelativePath.js +118 -0
  58. package/dist/config/configOrigin.d.ts +27 -0
  59. package/dist/config/configOrigin.js +59 -0
  60. package/dist/config/hostConfig.d.ts +55 -0
  61. package/dist/config/hostConfig.js +333 -0
  62. package/dist/config/listSkills.d.ts +26 -0
  63. package/dist/config/listSkills.js +190 -3
  64. package/dist/config/loadOutcome.d.ts +1 -1
  65. package/dist/config/loadPipeline.js +23 -2
  66. package/dist/config/loadStage.js +18 -0
  67. package/dist/config/loadTask.d.ts +2 -0
  68. package/dist/config/loadTask.js +57 -18
  69. package/dist/config/materializeStageModels.js +5 -1
  70. package/dist/config/mergePipelineIncludes.d.ts +3 -0
  71. package/dist/config/mergePipelineIncludes.js +19 -0
  72. package/dist/config/migrateYaml.js +6 -11
  73. package/dist/config/multiProjectCatalog.d.ts +30 -0
  74. package/dist/config/multiProjectCatalog.js +142 -0
  75. package/dist/config/normalizePipelineStageEntry.js +20 -1
  76. package/dist/config/pipelineStageKeys.js +2 -0
  77. package/dist/config/resolveCatalogRoots.d.ts +27 -0
  78. package/dist/config/resolveCatalogRoots.js +76 -0
  79. package/dist/config/resolveModel.d.ts +7 -1
  80. package/dist/config/resolveModel.js +11 -1
  81. package/dist/config/resolveStageMcpServers.d.ts +9 -1
  82. package/dist/config/resolveStageMcpServers.js +36 -9
  83. package/dist/config/secretFromEnvOrFile.d.ts +9 -0
  84. package/dist/config/secretFromEnvOrFile.js +34 -0
  85. package/dist/config/seededCatalog.d.ts +11 -0
  86. package/dist/config/seededCatalog.js +29 -0
  87. package/dist/config/toolRequires.d.ts +24 -0
  88. package/dist/config/toolRequires.js +115 -0
  89. package/dist/config/validateCatalog.d.ts +5 -1
  90. package/dist/config/validateCatalog.js +140 -1
  91. package/dist/diagnostics/checks.d.ts +39 -0
  92. package/dist/diagnostics/checks.js +135 -0
  93. package/dist/errors/codes.d.ts +14 -0
  94. package/dist/errors/codes.js +59 -0
  95. package/dist/git/cache.d.ts +14 -0
  96. package/dist/git/cache.js +150 -0
  97. package/dist/git/credentials.d.ts +9 -0
  98. package/dist/git/credentials.js +88 -0
  99. package/dist/git/errors.d.ts +20 -0
  100. package/dist/git/errors.js +94 -0
  101. package/dist/git/exec.d.ts +16 -0
  102. package/dist/git/exec.js +135 -0
  103. package/dist/git/operations.d.ts +29 -0
  104. package/dist/git/operations.js +153 -0
  105. package/dist/git/version.d.ts +1 -0
  106. package/dist/git/version.js +10 -0
  107. package/dist/logging/audit.d.ts +16 -0
  108. package/dist/logging/audit.js +19 -0
  109. package/dist/logging/commonWords.d.ts +2 -0
  110. package/dist/logging/commonWords.js +13 -0
  111. package/dist/logging/logger.d.ts +49 -0
  112. package/dist/logging/logger.js +193 -0
  113. package/dist/logging/namedSecrets.d.ts +6 -0
  114. package/dist/logging/namedSecrets.js +58 -0
  115. package/dist/logging/redact.d.ts +13 -0
  116. package/dist/logging/redact.js +88 -0
  117. package/dist/mcp/catalogTools.js +591 -76
  118. package/dist/mcp/checkoutTools.d.ts +52 -0
  119. package/dist/mcp/checkoutTools.js +332 -0
  120. package/dist/mcp/controlTools.js +179 -5
  121. package/dist/mcp/deps.d.ts +1 -0
  122. package/dist/mcp/getStartedTool.d.ts +3 -0
  123. package/dist/mcp/getStartedTool.js +62 -0
  124. package/dist/mcp/readArtifact.d.ts +11 -0
  125. package/dist/mcp/readArtifact.js +46 -30
  126. package/dist/mcp/server.js +1 -0
  127. package/dist/mcp/tools.js +4 -0
  128. package/dist/net/proxy.d.ts +14 -0
  129. package/dist/net/proxy.js +78 -0
  130. package/dist/package-meta.d.ts +2 -1
  131. package/dist/package-meta.js +2 -1
  132. package/dist/preflight/bash.d.ts +8 -0
  133. package/dist/preflight/bash.js +34 -0
  134. package/dist/preflight/claudeRoot.d.ts +11 -0
  135. package/dist/preflight/claudeRoot.js +23 -0
  136. package/dist/preflight/mcpCommands.d.ts +8 -0
  137. package/dist/preflight/mcpCommands.js +36 -0
  138. package/dist/preflight/pipelinePreflight.d.ts +40 -0
  139. package/dist/preflight/pipelinePreflight.js +192 -0
  140. package/dist/preflight/tls.d.ts +6 -0
  141. package/dist/preflight/tls.js +18 -0
  142. package/dist/preflight/toolchain.d.ts +45 -0
  143. package/dist/preflight/toolchain.js +219 -0
  144. package/dist/project/findProjectRoot.js +3 -4
  145. package/dist/project/globalHome.d.ts +7 -0
  146. package/dist/project/globalHome.js +63 -2
  147. package/dist/project/resolveProjectContext.js +2 -2
  148. package/dist/project/resolveStageflowContext.js +2 -2
  149. package/dist/projection/projectRun.d.ts +2 -1
  150. package/dist/projection/projectRun.js +1 -0
  151. package/dist/runstore/assertTmpdir.d.ts +1 -0
  152. package/dist/runstore/assertTmpdir.js +21 -0
  153. package/dist/runstore/backup.d.ts +52 -0
  154. package/dist/runstore/backup.js +292 -0
  155. package/dist/runstore/createStore.d.ts +27 -0
  156. package/dist/runstore/createStore.js +75 -7
  157. package/dist/runstore/debugBundle.d.ts +60 -0
  158. package/dist/runstore/debugBundle.js +143 -0
  159. package/dist/runstore/diskUsage.d.ts +50 -0
  160. package/dist/runstore/diskUsage.js +214 -0
  161. package/dist/runstore/exportRunPayload.d.ts +8 -0
  162. package/dist/runstore/exportRunPayload.js +23 -0
  163. package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
  164. package/dist/runstore/normalizeCatalogPath.js +10 -0
  165. package/dist/runstore/paths.d.ts +5 -0
  166. package/dist/runstore/paths.js +145 -1
  167. package/dist/runstore/port.d.ts +119 -8
  168. package/dist/runstore/port.js +8 -1
  169. package/dist/runstore/restore.d.ts +81 -0
  170. package/dist/runstore/restore.js +355 -0
  171. package/dist/runstore/retention.d.ts +27 -0
  172. package/dist/runstore/retention.js +74 -0
  173. package/dist/runstore/runManifest.d.ts +146 -0
  174. package/dist/runstore/runManifest.js +288 -0
  175. package/dist/runstore/runProjection.d.ts +3 -1
  176. package/dist/runstore/runProjection.js +63 -1
  177. package/dist/runstore/sqlite/SqliteRunStore.d.ts +37 -3
  178. package/dist/runstore/sqlite/SqliteRunStore.js +401 -227
  179. package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
  180. package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
  181. package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
  182. package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
  183. package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
  184. package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
  185. package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
  186. package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
  187. package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
  188. package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
  189. package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
  190. package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
  191. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
  192. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
  193. package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
  194. package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
  195. package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
  196. package/dist/runstore/sqlite/migrations/index.js +72 -0
  197. package/dist/runstore/sqlite/schema.d.ts +20 -1
  198. package/dist/runstore/sqlite/schema.js +283 -1
  199. package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
  200. package/dist/runstore/sqlite/storeOpenError.js +11 -0
  201. package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
  202. package/dist/runstore/sqlite/storeSchemaError.js +8 -0
  203. package/dist/runstore/stageExecution.js +3 -0
  204. package/dist/runstore/storeFilesystem.d.ts +32 -0
  205. package/dist/runstore/storeFilesystem.js +148 -0
  206. package/dist/runstore/submission.d.ts +11 -0
  207. package/dist/runstore/submission.js +7 -0
  208. package/dist/runstore/trackProjection.js +2 -0
  209. package/dist/runstore/workspaceLayout.d.ts +13 -0
  210. package/dist/runstore/workspaceLayout.js +76 -0
  211. package/dist/runtime/completionCheckRunner.d.ts +3 -0
  212. package/dist/runtime/completionCheckRunner.js +45 -7
  213. package/dist/runtime/containerLimits.d.ts +16 -0
  214. package/dist/runtime/containerLimits.js +84 -0
  215. package/dist/runtime/credentialMaterialisation.d.ts +28 -0
  216. package/dist/runtime/credentialMaterialisation.js +128 -0
  217. package/dist/runtime/gitCheckoutCapability.js +7 -13
  218. package/dist/runtime/pipelineRunner.d.ts +38 -1
  219. package/dist/runtime/pipelineRunner.js +179 -15
  220. package/dist/runtime/pipelineScheduler.d.ts +15 -0
  221. package/dist/runtime/pipelineScheduler.js +167 -42
  222. package/dist/runtime/reloadRunCatalog.js +12 -1
  223. package/dist/runtime/repositoryMaterialize.d.ts +47 -0
  224. package/dist/runtime/repositoryMaterialize.js +235 -0
  225. package/dist/runtime/resolveEnvelopeRef.d.ts +30 -0
  226. package/dist/runtime/resolveEnvelopeRef.js +79 -0
  227. package/dist/runtime/resumeTimedOut.d.ts +1 -1
  228. package/dist/runtime/resumeTimedOut.js +5 -2
  229. package/dist/runtime/runChangeBus.js +9 -0
  230. package/dist/runtime/runDeletion.d.ts +7 -0
  231. package/dist/runtime/runDeletion.js +37 -0
  232. package/dist/runtime/runManager.d.ts +179 -4
  233. package/dist/runtime/runManager.js +1644 -83
  234. package/dist/runtime/runRetentionSweep.d.ts +31 -0
  235. package/dist/runtime/runRetentionSweep.js +328 -0
  236. package/dist/runtime/runRetryCoordinator.d.ts +2 -0
  237. package/dist/runtime/runRetryCoordinator.js +19 -2
  238. package/dist/runtime/runSkills.d.ts +40 -0
  239. package/dist/runtime/runSkills.js +173 -0
  240. package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
  241. package/dist/runtime/stageAttemptBootstrap.js +195 -24
  242. package/dist/runtime/stageCacheEnv.d.ts +6 -0
  243. package/dist/runtime/stageCacheEnv.js +34 -0
  244. package/dist/runtime/stageConcurrency.d.ts +2 -1
  245. package/dist/runtime/stageConcurrency.js +4 -2
  246. package/dist/runtime/stageEnvironment.d.ts +38 -0
  247. package/dist/runtime/stageEnvironment.js +193 -0
  248. package/dist/runtime/stageProcessLauncher.d.ts +18 -0
  249. package/dist/runtime/stageProcessLauncher.js +194 -38
  250. package/dist/runtime/stageRecovery.d.ts +3 -1
  251. package/dist/runtime/stageRecovery.js +19 -4
  252. package/dist/runtime/stageRoots.d.ts +38 -1
  253. package/dist/runtime/stageRoots.js +118 -0
  254. package/dist/runtime/stageRunner.d.ts +1 -0
  255. package/dist/runtime/stageRunner.js +3 -1
  256. package/dist/runtime/stageSecretDecl.d.ts +9 -0
  257. package/dist/runtime/stageSecretDecl.js +102 -0
  258. package/dist/runtime/stageSecrets.d.ts +40 -0
  259. package/dist/runtime/stageSecrets.js +181 -0
  260. package/dist/runtime/stageWorker.d.ts +3 -2
  261. package/dist/runtime/stageWorker.js +58 -10
  262. package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
  263. package/dist/runtime/stageWorkerProtocol.js +8 -0
  264. package/dist/runtime/startPayload.d.ts +32 -0
  265. package/dist/runtime/startPayload.js +59 -0
  266. package/dist/runtime/taskInput.js +14 -5
  267. package/dist/runtime/verifiedStageExecution.d.ts +1 -0
  268. package/dist/runtime/verifiedStageExecution.js +2 -1
  269. package/dist/runtime/workspaceBinding.d.ts +20 -0
  270. package/dist/runtime/workspaceBinding.js +60 -0
  271. package/dist/server/allowedHosts.d.ts +19 -0
  272. package/dist/server/allowedHosts.js +179 -0
  273. package/dist/server/bootstrap.d.ts +38 -2
  274. package/dist/server/bootstrap.js +219 -4
  275. package/dist/server/controlToken.d.ts +47 -0
  276. package/dist/server/controlToken.js +241 -0
  277. package/dist/server/createHttpHost.d.ts +11 -2
  278. package/dist/server/createHttpHost.js +109 -10
  279. package/dist/server/ensureGlobalService.d.ts +5 -1
  280. package/dist/server/ensureGlobalService.js +35 -1
  281. package/dist/server/healthSurfaces.d.ts +6 -0
  282. package/dist/server/healthSurfaces.js +98 -0
  283. package/dist/server/http.d.ts +12 -1
  284. package/dist/server/http.js +1085 -485
  285. package/dist/server/listenHost.d.ts +5 -0
  286. package/dist/server/listenHost.js +56 -0
  287. package/dist/server/mcpHost.d.ts +10 -1
  288. package/dist/server/mcpHost.js +36 -6
  289. package/dist/server/operatorResults.d.ts +2 -2
  290. package/dist/server/operatorResults.js +40 -29
  291. package/dist/server/requestAuthContext.d.ts +12 -0
  292. package/dist/server/requestAuthContext.js +21 -0
  293. package/dist/server/shutdown.d.ts +69 -0
  294. package/dist/server/shutdown.js +219 -0
  295. package/dist/types/pipeline.d.ts +7 -0
  296. package/dist/types/stage.d.ts +8 -0
  297. package/dist/types/task.d.ts +8 -0
  298. package/dist/ui/assets/index-B6hlSXXn.js +120 -0
  299. package/dist/ui/index.html +1 -1
  300. package/examples/README.md +98 -0
  301. package/examples/archify-on-pr/README.md +213 -0
  302. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
  303. package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
  304. package/examples/archify-on-pr/author-diagrams.yaml +106 -0
  305. package/examples/archify-on-pr/detect-changes.yaml +89 -0
  306. package/examples/ci-validate/README.md +35 -0
  307. package/examples/ci-validate/check.yaml +13 -0
  308. package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
  309. package/examples/ci-validate/validate.sh +5 -0
  310. package/examples/compliance-review-demo/README.md +125 -0
  311. package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
  312. package/examples/compliance-review-demo/connect-account.yaml +47 -0
  313. package/examples/compliance-review-demo/map-to-control.yaml +123 -0
  314. package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
  315. package/examples/compliance-review-demo/publish-report.yaml +79 -0
  316. package/examples/compliance-review-demo/review-verdict.yaml +122 -0
  317. package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
  318. package/examples/compliance-review-demo/store-evidence.yaml +143 -0
  319. package/examples/conditional-fork/README.md +89 -0
  320. package/examples/conditional-fork/branch-a.yaml +13 -0
  321. package/examples/conditional-fork/branch-b.yaml +13 -0
  322. package/examples/conditional-fork/decide.yaml +35 -0
  323. package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
  324. package/examples/conditional-fork/fork-demo.task.yaml +8 -0
  325. package/examples/context7-mcp/README.md +42 -0
  326. package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
  327. package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
  328. package/examples/context7-mcp/fetch-docs.yaml +52 -0
  329. package/examples/context7-mcp/resolve-library.yaml +31 -0
  330. package/examples/context7-mcp/write-brief.yaml +52 -0
  331. package/examples/feature-loop/README.md +104 -0
  332. package/examples/feature-loop/address-feedback.yaml +148 -0
  333. package/examples/feature-loop/align.yaml +116 -0
  334. package/examples/feature-loop/decompose.yaml +86 -0
  335. package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
  336. package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
  337. package/examples/feature-loop/feature-loop.task.yaml +25 -0
  338. package/examples/feature-loop/implement.yaml +128 -0
  339. package/examples/feature-loop/plan.yaml +111 -0
  340. package/examples/feature-loop/publish.yaml +167 -0
  341. package/examples/feature-loop/review.yaml +146 -0
  342. package/examples/feature-loop/verify.yaml +106 -0
  343. package/examples/feedback-loop/README.md +72 -0
  344. package/examples/feedback-loop/VERIFY.md +208 -0
  345. package/examples/feedback-loop/continue-only.task.yaml +4 -0
  346. package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
  347. package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
  348. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
  349. package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
  350. package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
  351. package/examples/feedback-loop/implement.yaml +14 -0
  352. package/examples/feedback-loop/plan.yaml +14 -0
  353. package/examples/feedback-loop/review.yaml +15 -0
  354. package/examples/feedback-loop/send-back-once.task.yaml +6 -0
  355. package/examples/feedback-loop/submit.yaml +12 -0
  356. package/examples/generic-fan-in/README.md +151 -0
  357. package/examples/generic-fan-in/clarify.yaml +18 -0
  358. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
  359. package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
  360. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
  361. package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
  362. package/examples/generic-fan-in/research.yaml +45 -0
  363. package/examples/generic-fan-in/synthesize.yaml +66 -0
  364. package/examples/generic-fan-in/validation.yaml +45 -0
  365. package/examples/github-release/README.md +78 -0
  366. package/examples/github-release/draft-release-notes.yaml +64 -0
  367. package/examples/github-release/github-release.pipeline.yaml +11 -0
  368. package/examples/github-release/github-release.task.yaml +13 -0
  369. package/examples/github-release/publish-github-release.yaml +64 -0
  370. package/examples/hello-world/README.md +28 -0
  371. package/examples/hello-world/hello.pipeline.yaml +4 -0
  372. package/examples/hello-world/my-task.task.yaml +6 -0
  373. package/examples/hello-world/research.yaml +18 -0
  374. package/examples/mcp-hitl-tour/README.md +113 -0
  375. package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
  376. package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
  377. package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
  378. package/examples/mcp-hitl-tour/gather.yaml +31 -0
  379. package/examples/mcp-hitl-tour/intake.yaml +62 -0
  380. package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
  381. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
  382. package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
  383. package/examples/mcp-hitl-tour/polish.yaml +33 -0
  384. package/examples/mcp-hitl-tour/review.yaml +17 -0
  385. package/examples/mcp-hitl-tour/ship.yaml +25 -0
  386. package/examples/oss-issue-contribution/README.md +218 -0
  387. package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
  388. package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
  389. package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
  390. package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
  391. package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
  392. package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
  393. package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
  394. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
  395. package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
  396. package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
  397. package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
  398. package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
  399. package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
  400. package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
  401. package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
  402. package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
  403. package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
  404. package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
  405. package/examples/oss-quick-fix/README.md +65 -0
  406. package/examples/oss-quick-fix/implement-fix.yaml +84 -0
  407. package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
  408. package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
  409. package/examples/oss-quick-fix/ship-fix.yaml +81 -0
  410. package/examples/oss-quick-fix/understand-issue.yaml +67 -0
  411. package/examples/oss-quick-fix/verify-fix.yaml +73 -0
  412. package/examples/plan-review/README.md +37 -0
  413. package/examples/plan-review/plan-review-followup.yaml +13 -0
  414. package/examples/plan-review/plan-review.pipeline.yaml +9 -0
  415. package/examples/plan-review/plan-review.task.yaml +9 -0
  416. package/examples/plan-review/plan-review.yaml +25 -0
  417. package/examples/playwright-mcp/README.md +39 -0
  418. package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
  419. package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
  420. package/examples/playwright-mcp/screenshot.yaml +40 -0
  421. package/examples/retry-tour/README.md +75 -0
  422. package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
  423. package/examples/retry-tour/retry-tour.task.yaml +9 -0
  424. package/examples/route-if-tour/README.md +202 -0
  425. package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
  426. package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
  427. package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
  428. package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
  429. package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
  430. package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
  431. package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
  432. package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
  433. package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
  434. package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
  435. package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
  436. package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
  437. package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
  438. package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
  439. package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
  440. package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
  441. package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
  442. package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
  443. package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
  444. package/examples/route-wiring-smoke-test/README.md +377 -0
  445. package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
  446. package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
  447. package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
  448. package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
  449. package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
  450. package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
  451. package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
  452. package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
  453. package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
  454. package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
  455. package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
  456. package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
  457. package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
  458. package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
  459. package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
  460. package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
  461. package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
  462. package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
  463. package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
  464. package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
  465. package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
  466. package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
  467. package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
  468. package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
  469. package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
  470. package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
  471. package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
  472. package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
  473. package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
  474. package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
  475. package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
  476. package/examples/ship-feature/README.md +190 -0
  477. package/examples/ship-feature/feat-address-feedback.yaml +119 -0
  478. package/examples/ship-feature/feat-implement.yaml +180 -0
  479. package/examples/ship-feature/feat-plan.yaml +120 -0
  480. package/examples/ship-feature/feat-review.yaml +109 -0
  481. package/examples/ship-feature/feat-ship.yaml +203 -0
  482. package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
  483. package/examples/ship-feature/ship-feature.task.yaml +78 -0
  484. package/examples/stage-mcp/README.md +144 -0
  485. package/examples/stage-mcp/echo-mcp.mjs +20 -0
  486. package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
  487. package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
  488. package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
  489. package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
  490. package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
  491. package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
  492. package/examples/stage-mcp/use-echo.yaml +20 -0
  493. package/examples/verify-tour/README.md +80 -0
  494. package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
  495. package/examples/verify-tour/verify-tour.task.yaml +11 -0
  496. package/package.json +6 -1
  497. package/skills/stageflow/SKILL.md +6 -6
  498. package/skills/stageflow/references/control-surface.md +11 -5
  499. package/skills/stageflow/scripts/detect-host.mjs +14 -1
  500. package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
  501. package/skills/stageflow-author/SKILL.md +4 -3
  502. package/skills/stageflow-author/references/catalog-mapping.md +16 -0
  503. package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
  504. package/skills/stageflow-run/SKILL.md +20 -12
  505. package/skills/stageflow-run/references/mcp-call.md +9 -1
  506. package/skills/stageflow-run/references/task-and-pipeline-selection.md +16 -5
  507. package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
  508. package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
  509. package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
  510. package/skills/stageflow-setup/SKILL.md +40 -8
  511. package/dist/ui/assets/index-Da5EHU6M.js +0 -119
@@ -1,6 +1,7 @@
1
- import { access } from "node:fs/promises";
1
+ import { access, readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
2
3
  import { DefaultResourceLoader, SettingsManager, } from "@earendil-works/pi-coding-agent";
3
- function toListing(skill) {
4
+ function toListing(skill, origin) {
4
5
  return {
5
6
  name: skill.name,
6
7
  description: skill.description,
@@ -9,8 +10,39 @@ function toListing(skill) {
9
10
  scope: skill.sourceInfo.scope,
10
11
  source: skill.sourceInfo.source,
11
12
  disableModelInvocation: skill.disableModelInvocation,
13
+ ...(origin !== undefined ? { origin } : {}),
12
14
  };
13
15
  }
16
+ function isUnderRoot(candidate, root) {
17
+ const resolvedRoot = path.resolve(root);
18
+ const resolved = path.resolve(candidate);
19
+ const rel = path.relative(resolvedRoot, resolved);
20
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
21
+ }
22
+ async function pathExists(filePath) {
23
+ try {
24
+ await access(filePath);
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ function descriptionFromSkillMd(content) {
32
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
33
+ if (!match)
34
+ return undefined;
35
+ const block = match[1];
36
+ const descLine = /^description:\s*(.*)$/m.exec(block);
37
+ if (!descLine)
38
+ return undefined;
39
+ let value = descLine[1].trim();
40
+ if ((value.startsWith('"') && value.endsWith('"')) ||
41
+ (value.startsWith("'") && value.endsWith("'"))) {
42
+ value = value.slice(1, -1);
43
+ }
44
+ return value.length > 0 ? value : undefined;
45
+ }
14
46
  async function openSkillCatalogLoader(options) {
15
47
  const settingsManager = SettingsManager.create(options.cwd, options.agentDir, {
16
48
  projectTrusted: true,
@@ -23,7 +55,67 @@ async function openSkillCatalogLoader(options) {
23
55
  await loader.reload();
24
56
  return loader;
25
57
  }
58
+ async function resolveRunSkill(name, runSkillsDir) {
59
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
60
+ return undefined;
61
+ }
62
+ const baseDir = path.join(runSkillsDir, name);
63
+ const filePath = path.join(baseDir, "SKILL.md");
64
+ if (!(await pathExists(filePath)))
65
+ return undefined;
66
+ return {
67
+ name,
68
+ filePath,
69
+ baseDir,
70
+ scope: "temporary",
71
+ source: "run",
72
+ origin: "run",
73
+ };
74
+ }
75
+ async function resolveCheckoutSkill(name, checkoutRoot) {
76
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
77
+ return undefined;
78
+ }
79
+ const baseDir = path.join(checkoutRoot, ".pi", "skills", name);
80
+ const filePath = path.join(baseDir, "SKILL.md");
81
+ if (!(await pathExists(filePath)))
82
+ return undefined;
83
+ return {
84
+ name,
85
+ filePath,
86
+ baseDir,
87
+ scope: "project",
88
+ source: "checkout",
89
+ origin: "checkout",
90
+ };
91
+ }
92
+ function originForLoaderSkill(skill, options) {
93
+ if (options.checkoutRoot !== undefined &&
94
+ isUnderRoot(skill.filePath, path.join(options.checkoutRoot, ".pi", "skills"))) {
95
+ return "checkout";
96
+ }
97
+ if (isUnderRoot(skill.filePath, path.join(options.agentDir, "skills"))) {
98
+ return "host";
99
+ }
100
+ if (skill.sourceInfo.scope === "project") {
101
+ return "checkout";
102
+ }
103
+ return "host";
104
+ }
105
+ /**
106
+ * Resolve a skill by name. Search order: run skills → checkout `.pi/skills` → host agentDir.
107
+ */
26
108
  export async function resolveSkillByName(name, options) {
109
+ if (options.runSkillsDir !== undefined) {
110
+ const run = await resolveRunSkill(name, options.runSkillsDir);
111
+ if (run)
112
+ return run;
113
+ }
114
+ if (options.checkoutRoot !== undefined) {
115
+ const checkout = await resolveCheckoutSkill(name, options.checkoutRoot);
116
+ if (checkout)
117
+ return checkout;
118
+ }
27
119
  const loader = await openSkillCatalogLoader(options);
28
120
  const match = loader.getSkills().skills.find((skill) => skill.name === name);
29
121
  if (!match)
@@ -34,10 +126,24 @@ export async function resolveSkillByName(name, options) {
34
126
  catch {
35
127
  return undefined;
36
128
  }
129
+ if (options.checkoutRoot !== undefined &&
130
+ isUnderRoot(match.filePath, path.join(options.checkoutRoot, ".pi", "skills"))) {
131
+ return {
132
+ name: match.name,
133
+ filePath: match.filePath,
134
+ baseDir: match.baseDir,
135
+ scope: match.sourceInfo.scope,
136
+ source: match.sourceInfo.source,
137
+ origin: "checkout",
138
+ };
139
+ }
37
140
  return {
38
141
  name: match.name,
39
142
  filePath: match.filePath,
40
143
  baseDir: match.baseDir,
144
+ scope: match.sourceInfo.scope,
145
+ source: match.sourceInfo.source,
146
+ origin: originForLoaderSkill(match, options),
41
147
  };
42
148
  }
43
149
  export async function listSkills(options) {
@@ -45,7 +151,7 @@ export async function listSkills(options) {
45
151
  const { skills, diagnostics } = loader.getSkills();
46
152
  return {
47
153
  skills: skills
48
- .map(toListing)
154
+ .map((skill) => toListing(skill, originForLoaderSkill(skill, options)))
49
155
  .sort((a, b) => a.name.localeCompare(b.name)),
50
156
  diagnostics: diagnostics.map((d) => ({
51
157
  message: d.message,
@@ -53,3 +159,84 @@ export async function listSkills(options) {
53
159
  })),
54
160
  };
55
161
  }
162
+ async function listRunSkillOrigins(runSkillsDir) {
163
+ let names;
164
+ try {
165
+ names = await readdir(runSkillsDir);
166
+ }
167
+ catch {
168
+ return [];
169
+ }
170
+ const out = [];
171
+ for (const name of names) {
172
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
173
+ continue;
174
+ }
175
+ const filePath = path.join(runSkillsDir, name, "SKILL.md");
176
+ try {
177
+ const content = await readFile(filePath, "utf8");
178
+ const description = descriptionFromSkillMd(content);
179
+ if (description === undefined)
180
+ continue;
181
+ out.push({ name, description, origin: "run" });
182
+ }
183
+ catch {
184
+ // skip unreadable / incomplete packs
185
+ }
186
+ }
187
+ return out;
188
+ }
189
+ /**
190
+ * List skills with SkillOrigin. When runSkillsDir is set, run-tier skills win on name.
191
+ * Precedence for the merged view: run → checkout → host.
192
+ */
193
+ export async function listSkillsWithOrigin(options) {
194
+ const byName = new Map();
195
+ const hostCatalog = await listSkills({
196
+ cwd: options.cwd,
197
+ agentDir: options.agentDir,
198
+ ...(options.checkoutRoot !== undefined
199
+ ? { checkoutRoot: options.checkoutRoot }
200
+ : {}),
201
+ });
202
+ for (const skill of hostCatalog.skills) {
203
+ const origin = skill.origin ??
204
+ (skill.scope === "project" ? "checkout" : "host");
205
+ byName.set(skill.name, {
206
+ name: skill.name,
207
+ description: skill.description,
208
+ origin,
209
+ });
210
+ }
211
+ if (options.checkoutRoot !== undefined) {
212
+ let checkoutNames = [];
213
+ try {
214
+ checkoutNames = await readdir(path.join(options.checkoutRoot, ".pi", "skills"));
215
+ }
216
+ catch {
217
+ checkoutNames = [];
218
+ }
219
+ for (const name of checkoutNames) {
220
+ const filePath = path.join(options.checkoutRoot, ".pi", "skills", name, "SKILL.md");
221
+ try {
222
+ const content = await readFile(filePath, "utf8");
223
+ const description = descriptionFromSkillMd(content);
224
+ if (description === undefined)
225
+ continue;
226
+ byName.set(name, { name, description, origin: "checkout" });
227
+ }
228
+ catch {
229
+ // skip
230
+ }
231
+ }
232
+ }
233
+ if (options.runSkillsDir !== undefined) {
234
+ for (const skill of await listRunSkillOrigins(options.runSkillsDir)) {
235
+ byName.set(skill.name, skill);
236
+ }
237
+ }
238
+ return {
239
+ skills: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)),
240
+ diagnostics: hostCatalog.diagnostics,
241
+ };
242
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ValidationFindingCode } from "./validateCatalog.js";
2
- export type TaskLoadCode = "task.invalid_shape" | "task.load_error";
2
+ export type TaskLoadCode = "task.invalid_shape" | "task.load_error" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid";
3
3
  export type LoadIssue = {
4
4
  code: ValidationFindingCode;
5
5
  message: string;
@@ -12,6 +12,7 @@ import { recoveryRequiresCompletionIssue } from "./parseCompletionContract.js";
12
12
  import { validateCompletionContractForStage } from "./validateCompletionContract.js";
13
13
  import { applyCloneChains } from "./cloneChain.js";
14
14
  import { collectRouteIfSchemaIssues, collectRouteIfIllegalCombos, collectRouteAllGatedWarnings, } from "./routeIf.js";
15
+ import { mergeToolRequires } from "./toolRequires.js";
15
16
  function schemaCompileIssue(stageId, field, err, pipelineId) {
16
17
  const message = err instanceof Error ? err.message : String(err);
17
18
  if (err instanceof UnresolvedSchemaRefError) {
@@ -128,7 +129,7 @@ export async function loadPipelineFromObject(raw, options = {}) {
128
129
  }
129
130
  async function buildLoadedPipelineFromMerge(merge, options) {
130
131
  const { projectRoot, requireIo, pipelinePath } = options;
131
- const { entries: rawEntries, pipelineId, agent: pipelineAgent, model: pipelineModel, schemas: pipelineSchemas, warnings: mergeWarnings, } = merge;
132
+ const { entries: rawEntries, pipelineId, agent: pipelineAgent, model: pipelineModel, schemas: pipelineSchemas, requires: pipelineRequires, warnings: mergeWarnings, } = merge;
132
133
  const ctx = { pipelineId, path: pipelinePath };
133
134
  const warnings = [...mergeWarnings];
134
135
  const normalizeOutcome = normalizePipelineStageEntries(rawEntries, ctx);
@@ -181,7 +182,14 @@ async function buildLoadedPipelineFromMerge(merge, options) {
181
182
  if (!inlineOutcome.ok) {
182
183
  return loadFailure(inlineOutcome.issues);
183
184
  }
184
- stages.push(inlineOutcome.value);
185
+ let stage = inlineOutcome.value;
186
+ if (entry.requires !== undefined) {
187
+ const merged = mergeToolRequires([stage.requires, entry.requires], { pipelineId, label: `stage "${entry.id}"` });
188
+ if (!merged.ok)
189
+ return loadFailure(merged.issues);
190
+ stage = { ...stage, requires: merged.value };
191
+ }
192
+ stages.push(stage);
185
193
  stageSources[stageId] = { kind: "inline" };
186
194
  continue;
187
195
  }
@@ -229,10 +237,19 @@ async function buildLoadedPipelineFromMerge(merge, options) {
229
237
  },
230
238
  ]);
231
239
  }
240
+ let stageRequires = stageOutcome.value.requires;
241
+ if (entry.requires !== undefined) {
242
+ const merged = mergeToolRequires([stageRequires, entry.requires], { pipelineId, label: `stage "${entry.id}"` });
243
+ if (!merged.ok)
244
+ return loadFailure(merged.issues);
245
+ stageRequires = merged.value;
246
+ }
232
247
  const stage = {
233
248
  ...stageOutcome.value,
234
249
  ...(entry.skill !== undefined ? { skill: entry.skill } : {}),
235
250
  ...(entry.mcp !== undefined ? { mcp: entry.mcp } : {}),
251
+ ...(entry.secrets !== undefined ? { secrets: entry.secrets } : {}),
252
+ ...(stageRequires !== undefined ? { requires: stageRequires } : {}),
236
253
  };
237
254
  stages.push(stage);
238
255
  stageSources[stageId] = { kind: "file", path: entry.body.absolutePath };
@@ -282,7 +299,11 @@ async function buildLoadedPipelineFromMerge(merge, options) {
282
299
  ...(pipelineAgent !== undefined ? { agent: pipelineAgent } : {}),
283
300
  ...(pipelineModel !== undefined ? { model: pipelineModel } : {}),
284
301
  ...(pipelineSchemas !== undefined ? { schemas: pipelineSchemas } : {}),
302
+ ...(pipelineRequires !== undefined ? { requires: pipelineRequires } : {}),
285
303
  };
304
+ const effectiveRequires = mergeToolRequires([pipelineRequires, ...loadedStages.map((s) => s.requires)], { pipelineId });
305
+ if (!effectiveRequires.ok)
306
+ return loadFailure(effectiveRequires.issues);
286
307
  const nodeById = new Map(dag.nodes.map((node) => [node.id, node]));
287
308
  for (const [stageId, after] of fileAfterById) {
288
309
  const node = nodeById.get(stageId);
@@ -7,6 +7,8 @@ import { parseModelField } from "./modelField.js";
7
7
  import { allowLegacyYamlAuthoring, dialectWarningForDocument, legacyAuthoringRejected, presentLegacyKeys, } from "./legacyYaml.js";
8
8
  import { parsePreEmitChecks } from "./parsePreEmitChecks.js";
9
9
  import { readYamlObject } from "./readYamlObject.js";
10
+ import { parseStageSecrets } from "../runtime/stageSecretDecl.js";
11
+ import { parseToolRequires } from "./toolRequires.js";
10
12
  import { classifyYamlDocument, compileTargetContract, dialectFromKeys, mixedDialectIssue, STAGE_FILE_WIRING_KEYS, } from "./yamlDialect.js";
11
13
  const afterCompletionByStage = new WeakMap();
12
14
  /** After-phase IR from target `verify` (`when` includes after). Stamped onto DAG `completion`. */
@@ -348,6 +350,22 @@ function parseStageFields(raw, label, entryId, deferSchemaRefs, requireIo, compi
348
350
  if (mcpOutcome.value !== undefined) {
349
351
  stage.mcp = mcpOutcome.value;
350
352
  }
353
+ const secretsOutcome = parseStageSecrets(raw.secrets, label, entryId);
354
+ if (!secretsOutcome.ok)
355
+ return secretsOutcome;
356
+ if (secretsOutcome.value !== undefined) {
357
+ stage.secrets = secretsOutcome.value;
358
+ }
359
+ const requiresOutcome = parseToolRequires(raw.requires, `stage ${label}`, {
360
+ code: "stage.invalid_requires",
361
+ category: "stage",
362
+ stageId: entryId,
363
+ });
364
+ if (!requiresOutcome.ok)
365
+ return requiresOutcome;
366
+ if (requiresOutcome.value !== undefined) {
367
+ stage.requires = requiresOutcome.value;
368
+ }
351
369
  const agentField = parseAgentField(raw.agent);
352
370
  if (!agentField.ok) {
353
371
  return loadFailure([
@@ -1,5 +1,7 @@
1
1
  import type { TaskFile } from "../types/task.js";
2
2
  import { type LoadOutcome } from "./loadOutcome.js";
3
+ /** Structural TaskFile shape only — does not enforce workspace binding XOR. */
4
+ export declare function coerceTaskFile(raw: unknown): TaskFile | undefined;
3
5
  export declare function parseTaskFile(raw: unknown, source?: string): LoadOutcome<TaskFile>;
4
6
  export declare function loadTaskFromYamlOutcome(yamlText: string, source?: string): LoadOutcome<TaskFile>;
5
7
  export declare function loadTaskOutcome(filePath: string): Promise<LoadOutcome<TaskFile>>;
@@ -1,12 +1,60 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { parse as parseYaml } from "yaml";
3
+ import { resolveWorkspaceBinding } from "../runtime/workspaceBinding.js";
3
4
  import { loadFailure, loadSuccess } from "./loadOutcome.js";
4
5
  function isPlainObject(value) {
5
6
  return typeof value === "object" && value !== null && !Array.isArray(value);
6
7
  }
7
- export function parseTaskFile(raw, source = "task") {
8
+ function parseGitIdentity(raw) {
9
+ if (!isPlainObject(raw))
10
+ return undefined;
11
+ const identity = {};
12
+ if (typeof raw.name === "string")
13
+ identity.name = raw.name;
14
+ if (typeof raw.email === "string")
15
+ identity.email = raw.email;
16
+ return identity;
17
+ }
18
+ /** Structural TaskFile shape only — does not enforce workspace binding XOR. */
19
+ export function coerceTaskFile(raw) {
8
20
  const record = raw;
9
21
  if (typeof record?.id !== "string" || typeof record?.goal !== "string") {
22
+ return undefined;
23
+ }
24
+ if (record.input !== undefined && !isPlainObject(record.input)) {
25
+ return undefined;
26
+ }
27
+ return {
28
+ id: record.id,
29
+ goal: record.goal,
30
+ context: typeof record.context === "string" ? record.context : undefined,
31
+ constraints: typeof record.constraints === "string" ? record.constraints : undefined,
32
+ checkout: typeof record.checkout === "string" ? record.checkout : undefined,
33
+ ...(typeof record.repository === "string" ? { repository: record.repository } : {}),
34
+ ...(typeof record.ref === "string" ? { ref: record.ref } : {}),
35
+ ...(typeof record.run_branch_template === "string"
36
+ ? { run_branch_template: record.run_branch_template }
37
+ : {}),
38
+ ...(isPlainObject(record.git_identity)
39
+ ? { git_identity: parseGitIdentity(record.git_identity) }
40
+ : {}),
41
+ ...(record.input !== undefined ? { input: record.input } : {}),
42
+ };
43
+ }
44
+ export function parseTaskFile(raw, source = "task") {
45
+ const record = raw;
46
+ const task = coerceTaskFile(raw);
47
+ if (task === undefined) {
48
+ if (typeof record?.id === "string" && typeof record?.goal === "string") {
49
+ return loadFailure([
50
+ {
51
+ code: "task.invalid_shape",
52
+ message: `Invalid ${source}: input must be an object`,
53
+ category: "task",
54
+ taskId: record.id,
55
+ },
56
+ ]);
57
+ }
10
58
  return loadFailure([
11
59
  {
12
60
  code: "task.invalid_shape",
@@ -16,24 +64,15 @@ export function parseTaskFile(raw, source = "task") {
16
64
  },
17
65
  ]);
18
66
  }
19
- if (record.input !== undefined && !isPlainObject(record.input)) {
20
- return loadFailure([
21
- {
22
- code: "task.invalid_shape",
23
- message: `Invalid ${source}: input must be an object`,
24
- category: "task",
25
- taskId: record.id,
26
- },
27
- ]);
67
+ const binding = resolveWorkspaceBinding(task);
68
+ if (!binding.ok) {
69
+ return loadFailure(binding.issues.map((issue) => {
70
+ if (issue.category !== "task")
71
+ return issue;
72
+ return { ...issue, taskId: task.id };
73
+ }));
28
74
  }
29
- return loadSuccess({
30
- id: record.id,
31
- goal: record.goal,
32
- context: typeof record.context === "string" ? record.context : undefined,
33
- constraints: typeof record.constraints === "string" ? record.constraints : undefined,
34
- checkout: typeof record.checkout === "string" ? record.checkout : undefined,
35
- ...(record.input !== undefined ? { input: record.input } : {}),
36
- });
75
+ return loadSuccess(task);
37
76
  }
38
77
  export function loadTaskFromYamlOutcome(yamlText, source = "task yaml") {
39
78
  let raw;
@@ -29,7 +29,11 @@ export async function materializeStageModels(stages, options) {
29
29
  if (!modelOutcome.ok) {
30
30
  return loadFailure(modelOutcome.issues);
31
31
  }
32
- materialized.push({ ...stage, model: modelOutcome.value });
32
+ materialized.push({
33
+ ...stage,
34
+ model: modelOutcome.value.model,
35
+ model_tier: modelOutcome.value.tier,
36
+ });
33
37
  }
34
38
  return loadSuccess(materialized);
35
39
  }
@@ -1,6 +1,7 @@
1
1
  import type { PayloadSchemaMap } from "../envelope/payloadSchema.js";
2
2
  import type { InlinePipelineDefinition } from "../types/pipeline.js";
3
3
  import { type LoadIssue, type LoadOutcome } from "./loadOutcome.js";
4
+ import { type ToolRequirement } from "./toolRequires.js";
4
5
  export type RawMergedEntry = {
5
6
  raw: unknown;
6
7
  declaringPath: string;
@@ -11,6 +12,7 @@ export declare function mergePipelineStages(rootPath: string): Promise<LoadOutco
11
12
  agent?: string;
12
13
  model?: string;
13
14
  schemas?: PayloadSchemaMap;
15
+ requires?: ToolRequirement[];
14
16
  warnings: LoadIssue[];
15
17
  }>>;
16
18
  /** Synthetic `declaringPath` for stage entries with no backing file. */
@@ -21,5 +23,6 @@ export declare function mergeInlinePipelineStages(raw: InlinePipelineDefinition)
21
23
  agent?: string;
22
24
  model?: string;
23
25
  schemas?: PayloadSchemaMap;
26
+ requires?: ToolRequirement[];
24
27
  warnings: LoadIssue[];
25
28
  }>>;
@@ -4,6 +4,7 @@ import { loadFailure, loadSuccess } from "./loadOutcome.js";
4
4
  import { allowLegacyYamlAuthoring, dialectWarningForDocument, legacyAuthoringRejected, presentLegacyKeys, } from "./legacyYaml.js";
5
5
  import { parseModelField } from "./modelField.js";
6
6
  import { readYamlObject } from "./readYamlObject.js";
7
+ import { parseToolRequires } from "./toolRequires.js";
7
8
  import { classifyYamlDocument, collectDocumentKeys, mixedDialectIssue, } from "./yamlDialect.js";
8
9
  function isPlainObject(value) {
9
10
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -177,6 +178,14 @@ export async function mergePipelineStages(rootPath) {
177
178
  if (!schemasOutcome.ok)
178
179
  return schemasOutcome;
179
180
  const schemas = schemasOutcome.value;
181
+ const requiresOutcome = parseToolRequires(raw.requires, `pipeline ${absRoot}`, {
182
+ code: "pipeline.invalid_requires",
183
+ category: "pipeline",
184
+ pipelineId,
185
+ });
186
+ if (!requiresOutcome.ok)
187
+ return requiresOutcome;
188
+ const requires = requiresOutcome.value;
180
189
  const idLocations = new Map();
181
190
  const entries = [];
182
191
  const warnings = [];
@@ -198,6 +207,7 @@ export async function mergePipelineStages(rootPath) {
198
207
  ...(agent !== undefined ? { agent } : {}),
199
208
  ...(model !== undefined ? { model } : {}),
200
209
  ...(schemas !== undefined ? { schemas } : {}),
210
+ ...(requires !== undefined ? { requires } : {}),
201
211
  warnings,
202
212
  });
203
213
  }
@@ -252,6 +262,14 @@ export async function mergeInlinePipelineStages(raw) {
252
262
  if (!schemasOutcome.ok)
253
263
  return schemasOutcome;
254
264
  const schemas = schemasOutcome.value;
265
+ const requiresOutcome = parseToolRequires(raw.requires, `inline pipeline ${pipelineId}`, {
266
+ code: "pipeline.invalid_requires",
267
+ category: "pipeline",
268
+ pipelineId,
269
+ });
270
+ if (!requiresOutcome.ok)
271
+ return requiresOutcome;
272
+ const requires = requiresOutcome.value;
255
273
  const entries = [];
256
274
  for (let index = 0; index < raw.stages.length; index++) {
257
275
  const entry = raw.stages[index];
@@ -273,6 +291,7 @@ export async function mergeInlinePipelineStages(raw) {
273
291
  ...(agent !== undefined ? { agent } : {}),
274
292
  ...(model !== undefined ? { model } : {}),
275
293
  ...(schemas !== undefined ? { schemas } : {}),
294
+ ...(requires !== undefined ? { requires } : {}),
276
295
  warnings: [],
277
296
  });
278
297
  }
@@ -1,7 +1,8 @@
1
- import { execFileSync } from "node:child_process";
2
1
  import { randomBytes } from "node:crypto";
3
2
  import { readdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
4
3
  import path from "node:path";
4
+ import { runGitSync } from "../git/exec.js";
5
+ import { statusPorcelain } from "../git/operations.js";
5
6
  import { loadPipelineOutcome } from "./loadPipeline.js";
6
7
  import { afterCompletionForStage, loadStageOutcome } from "./loadStage.js";
7
8
  import { withLegacyYamlAllowedAsync } from "./legacyYaml.js";
@@ -104,11 +105,10 @@ function verifyFingerprint(items) {
104
105
  }
105
106
  function gitToplevel(cwd) {
106
107
  try {
107
- return execFileSync("git", ["rev-parse", "--show-toplevel"], {
108
+ return runGitSync({
108
109
  cwd,
109
- encoding: "utf8",
110
- stdio: ["ignore", "pipe", "ignore"],
111
- }).trim();
110
+ args: ["rev-parse", "--show-toplevel"],
111
+ }).stdout.trim();
112
112
  }
113
113
  catch {
114
114
  return null;
@@ -119,12 +119,7 @@ export function fileDirtyVsHead(absPath) {
119
119
  if (top === null)
120
120
  return true;
121
121
  try {
122
- const status = execFileSync("git", ["status", "--porcelain", "--", absPath], {
123
- cwd: top,
124
- encoding: "utf8",
125
- stdio: ["ignore", "pipe", "ignore"],
126
- });
127
- return status.trim().length > 0;
122
+ return statusPorcelain(top, absPath).trim().length > 0;
128
123
  }
129
124
  catch {
130
125
  return true;
@@ -0,0 +1,30 @@
1
+ import { type PipelineListing, type TaskListing } from "./browseCatalog.js";
2
+ import { type CatalogRoot, type ResolveCatalogRootsOptions } from "./resolveCatalogRoots.js";
3
+ export type CatalogRootError = {
4
+ project_root: string;
5
+ code: "catalog_root_unreadable" | "unknown_project_root";
6
+ message: string;
7
+ };
8
+ export type MultiProjectListResult<T> = {
9
+ items: T[];
10
+ root_errors: CatalogRootError[];
11
+ roots: CatalogRoot[];
12
+ };
13
+ export declare function listPipelinesMultiProject(options: ResolveCatalogRootsOptions & {
14
+ projectRootFilter?: string;
15
+ }): Promise<MultiProjectListResult<PipelineListing & {
16
+ project_root: string;
17
+ }>>;
18
+ export declare function listTasksMultiProject(options: ResolveCatalogRootsOptions & {
19
+ projectRootFilter?: string;
20
+ }): Promise<MultiProjectListResult<TaskListing & {
21
+ project_root: string;
22
+ }>>;
23
+ export declare function listModelsMultiProject(options: ResolveCatalogRootsOptions & {
24
+ projectRootFilter?: string;
25
+ }): Promise<MultiProjectListResult<{
26
+ id: string;
27
+ project_root: string;
28
+ }> & {
29
+ models: string[];
30
+ }>;