stageflow 0.32.0 → 0.35.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 (335) hide show
  1. package/README.md +4 -3
  2. package/dist/agent/adapterSpill.d.ts +14 -0
  3. package/dist/agent/adapterSpill.js +83 -0
  4. package/dist/agent/agentBackend.d.ts +2 -2
  5. package/dist/agent/agentBackend.js +2 -2
  6. package/dist/agent/claudeAdapter.d.ts +13 -2
  7. package/dist/agent/claudeAdapter.js +50 -6
  8. package/dist/agent/cursorProvider.d.ts +3 -0
  9. package/dist/agent/cursorProvider.js +23 -6
  10. package/dist/agent/fakeAgent.d.ts +3 -3
  11. package/dist/agent/fakeAgent.js +0 -6
  12. package/dist/agent/piAdapter.d.ts +2 -2
  13. package/dist/agent/piAdapter.js +116 -15
  14. package/dist/agent/piIsolatedMcp.d.ts +1 -0
  15. package/dist/agent/piIsolatedMcp.js +19 -1
  16. package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
  17. package/dist/agent/piIsolatedMcpProbe.js +20 -0
  18. package/dist/agent/port.d.ts +2 -2
  19. package/dist/agent/providerAuth.d.ts +6 -0
  20. package/dist/agent/providerAuth.js +86 -17
  21. package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -6
  22. package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +13 -9
  23. package/dist/agent/stageTurnDriver.d.ts +33 -0
  24. package/dist/agent/stageTurnDriver.js +64 -0
  25. package/dist/agent/toolOutputBudget.d.ts +46 -0
  26. package/dist/agent/toolOutputBudget.js +97 -0
  27. package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
  28. package/dist/agent/toolOutputBudgetExtension.js +141 -0
  29. package/dist/agent/toolOutputSpill.d.ts +34 -0
  30. package/dist/agent/toolOutputSpill.js +85 -0
  31. package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
  32. package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
  33. package/dist/browser/anchor.d.ts +2 -0
  34. package/dist/browser/anchor.js +2 -1
  35. package/dist/browser/auditSink.d.ts +8 -0
  36. package/dist/browser/browserHost.d.ts +29 -0
  37. package/dist/browser/browserSweep.d.ts +4 -1
  38. package/dist/browser/browserSweep.js +7 -0
  39. package/dist/browser/browserTeardown.d.ts +1 -0
  40. package/dist/browser/browserTeardown.js +32 -2
  41. package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
  42. package/dist/browser/containerBrowserEndpoint.js +57 -0
  43. package/dist/browser/containerBrowserHost.d.ts +24 -0
  44. package/dist/browser/containerBrowserHost.js +147 -0
  45. package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
  46. package/dist/browser/dockerSandboxOrchestrator.js +142 -0
  47. package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
  48. package/dist/browser/fakeLiveViewRelay.js +94 -0
  49. package/dist/browser/fakeLiveViewSource.d.ts +6 -0
  50. package/dist/browser/fakeLiveViewSource.js +16 -0
  51. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
  52. package/dist/browser/fakeRemoteBrowserHost.js +2 -1
  53. package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
  54. package/dist/browser/fakeSandboxOrchestrator.js +44 -0
  55. package/dist/browser/gateHandoff.d.ts +0 -2
  56. package/dist/browser/gateHandoff.js +5 -11
  57. package/dist/browser/hostCapabilities.d.ts +23 -0
  58. package/dist/browser/hostCapabilities.js +23 -0
  59. package/dist/browser/humanLogin.d.ts +2 -2
  60. package/dist/browser/humanLogin.js +11 -2
  61. package/dist/browser/liveViewPersisted.d.ts +11 -0
  62. package/dist/browser/liveViewPersisted.js +22 -0
  63. package/dist/browser/liveViewRelay.d.ts +76 -0
  64. package/dist/browser/liveViewRelay.js +1 -0
  65. package/dist/browser/liveViewSessions.d.ts +25 -0
  66. package/dist/browser/liveViewSessions.js +139 -0
  67. package/dist/browser/liveViewSource.d.ts +25 -0
  68. package/dist/browser/liveViewSource.js +7 -0
  69. package/dist/browser/liveViewTickets.d.ts +46 -0
  70. package/dist/browser/liveViewTickets.js +122 -0
  71. package/dist/browser/localBrowserHost.d.ts +10 -0
  72. package/dist/browser/localBrowserHost.js +53 -7
  73. package/dist/browser/ownerScope.d.ts +10 -0
  74. package/dist/browser/ownerScope.js +6 -0
  75. package/dist/browser/profileStore.d.ts +5 -2
  76. package/dist/browser/profileStore.js +3 -5
  77. package/dist/browser/sandboxOrchestrator.d.ts +60 -0
  78. package/dist/browser/sandboxOrchestrator.js +8 -0
  79. package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
  80. package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
  81. package/dist/browser/stageBrowserEnv.d.ts +7 -0
  82. package/dist/browser/stageBrowserEnv.js +53 -15
  83. package/dist/browser/stageHandoff.d.ts +47 -0
  84. package/dist/browser/stageHandoff.js +101 -0
  85. package/dist/browser/stageProfileLock.d.ts +1 -0
  86. package/dist/browser/stageProfileLock.js +1 -3
  87. package/dist/browser/volumeProfileStore.d.ts +7 -0
  88. package/dist/browser/volumeProfileStore.js +35 -0
  89. package/dist/cli/browserCommand.d.ts +3 -1
  90. package/dist/cli/browserCommand.js +25 -20
  91. package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
  92. package/dist/cli/resolveCliProjectRoot.js +27 -0
  93. package/dist/cli/runCommand.d.ts +1 -1
  94. package/dist/cli/runCommand.js +59 -17
  95. package/dist/cli/skillsCommand.d.ts +20 -0
  96. package/dist/cli/skillsCommand.js +74 -31
  97. package/dist/cli/validateCommand.d.ts +1 -1
  98. package/dist/cli/validateCommand.js +22 -4
  99. package/dist/cli.js +4 -4
  100. package/dist/config/browseCatalog.d.ts +2 -0
  101. package/dist/config/browseCatalog.js +6 -0
  102. package/dist/config/catalogRelativePath.d.ts +4 -0
  103. package/dist/config/catalogRelativePath.js +37 -9
  104. package/dist/config/createStage.d.ts +1 -0
  105. package/dist/config/createStage.js +11 -2
  106. package/dist/config/createTask.d.ts +30 -0
  107. package/dist/config/createTask.js +161 -0
  108. package/dist/config/createTrigger.d.ts +15 -0
  109. package/dist/config/createTrigger.js +32 -25
  110. package/dist/config/draftPackage.d.ts +41 -0
  111. package/dist/config/draftPackage.js +216 -23
  112. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  113. package/dist/config/ensureCatalogScanRoot.js +6 -2
  114. package/dist/config/findTaskInCatalog.d.ts +33 -0
  115. package/dist/config/findTaskInCatalog.js +69 -0
  116. package/dist/config/hostConfig.d.ts +21 -0
  117. package/dist/config/hostConfig.js +113 -1
  118. package/dist/config/importTask.d.ts +19 -0
  119. package/dist/config/importTask.js +133 -0
  120. package/dist/config/installSkillRequest.d.ts +40 -0
  121. package/dist/config/installSkillRequest.js +286 -0
  122. package/dist/config/listConfig.d.ts +0 -16
  123. package/dist/config/listConfig.js +0 -3
  124. package/dist/config/loadOutcome.d.ts +11 -6
  125. package/dist/config/loadPipeline.js +0 -1
  126. package/dist/config/loadStage.js +2 -0
  127. package/dist/config/loadStageflowManifest.js +4 -1
  128. package/dist/config/loadTask.js +2 -0
  129. package/dist/config/loadTrigger.js +2 -0
  130. package/dist/config/mergePipelineIncludes.js +3 -0
  131. package/dist/config/normalizePipelineStageEntry.js +10 -1
  132. package/dist/config/printTargetYaml.js +0 -1
  133. package/dist/config/publishDraftPackage.d.ts +5 -1
  134. package/dist/config/publishDraftPackage.js +4 -1
  135. package/dist/config/resolveCatalogRoots.d.ts +4 -1
  136. package/dist/config/resolveCatalogRoots.js +18 -1
  137. package/dist/config/skillUsageIndex.d.ts +6 -0
  138. package/dist/config/skillUsageIndex.js +39 -0
  139. package/dist/config/stageBrowser.js +13 -0
  140. package/dist/config/triggerAdapterStatus.d.ts +11 -0
  141. package/dist/config/triggerAdapterStatus.js +90 -0
  142. package/dist/config/triggerCatalog.d.ts +41 -2
  143. package/dist/config/triggerCatalog.js +107 -15
  144. package/dist/config/updateTask.d.ts +30 -0
  145. package/dist/config/updateTask.js +119 -0
  146. package/dist/config/updateTrigger.d.ts +38 -0
  147. package/dist/config/updateTrigger.js +220 -0
  148. package/dist/config/validateCatalog.d.ts +6 -2
  149. package/dist/config/validateCatalog.js +48 -7
  150. package/dist/config/workshopCatalogFilenames.d.ts +37 -0
  151. package/dist/config/workshopCatalogFilenames.js +125 -0
  152. package/dist/config/workshopCatalogRepair.d.ts +7 -0
  153. package/dist/config/workshopCatalogRepair.js +144 -0
  154. package/dist/config/yamlParsePosition.d.ts +4 -0
  155. package/dist/config/yamlParsePosition.js +9 -0
  156. package/dist/hosted/containment.d.ts +3 -0
  157. package/dist/hosted/containment.js +36 -0
  158. package/dist/hosted/errors.d.ts +21 -0
  159. package/dist/hosted/errors.js +30 -0
  160. package/dist/hosted/packageSkills.d.ts +3 -0
  161. package/dist/hosted/packageSkills.js +80 -0
  162. package/dist/hosted/runtime.d.ts +12 -0
  163. package/dist/hosted/runtime.js +596 -0
  164. package/dist/hosted/sequencer.d.ts +28 -0
  165. package/dist/hosted/sequencer.js +129 -0
  166. package/dist/hosted/translate.d.ts +3 -0
  167. package/dist/hosted/translate.js +43 -0
  168. package/dist/hosted/types.d.ts +108 -0
  169. package/dist/hosted/types.js +10 -0
  170. package/dist/index.d.ts +9 -1
  171. package/dist/index.js +6 -0
  172. package/dist/mcp/getStartedTool.js +15 -6
  173. package/dist/mcp/triggerTools.js +2 -8
  174. package/dist/operatorAgent/agentPort.d.ts +11 -0
  175. package/dist/operatorAgent/agentPort.js +12 -0
  176. package/dist/operatorAgent/index.d.ts +1 -0
  177. package/dist/operatorAgent/index.js +1 -0
  178. package/dist/operatorAgent/piModel.js +26 -6
  179. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  180. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
  181. package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
  182. package/dist/package-meta.d.ts +1 -1
  183. package/dist/package-meta.js +1 -1
  184. package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
  185. package/dist/project/ensureProjectRootsFromEnv.js +22 -0
  186. package/dist/project/globalHome.d.ts +1 -0
  187. package/dist/project/globalHome.js +18 -0
  188. package/dist/project/workspaceCatalog.d.ts +10 -0
  189. package/dist/project/workspaceCatalog.js +91 -0
  190. package/dist/projection/projectRun.d.ts +0 -1
  191. package/dist/projection/projectRun.js +0 -1
  192. package/dist/runstore/backup.js +14 -4
  193. package/dist/runstore/port.d.ts +11 -1
  194. package/dist/runstore/restore.js +5 -2
  195. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
  196. package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
  197. package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
  198. package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
  199. package/dist/runstore/sqlite/migrations/index.js +2 -0
  200. package/dist/runstore/stageSnapshot.d.ts +3 -1
  201. package/dist/runstore/stageSnapshot.js +19 -0
  202. package/dist/runstore/workspaceLayout.js +27 -0
  203. package/dist/runstore/writeContainedArtifact.d.ts +1 -0
  204. package/dist/runstore/writeContainedArtifact.js +52 -0
  205. package/dist/runtime/containerLimits.d.ts +0 -3
  206. package/dist/runtime/containerLimits.js +0 -5
  207. package/dist/runtime/credentialBinding.d.ts +3 -0
  208. package/dist/runtime/credentialBinding.js +23 -6
  209. package/dist/runtime/emailSource.js +20 -28
  210. package/dist/runtime/githubPollSource.js +20 -28
  211. package/dist/runtime/pipelineRunner.d.ts +4 -4
  212. package/dist/runtime/pipelineScheduler.d.ts +2 -2
  213. package/dist/runtime/pipelineScheduler.js +3 -0
  214. package/dist/runtime/resumeReconstruct.d.ts +2 -2
  215. package/dist/runtime/resumeTimedOut.d.ts +2 -2
  216. package/dist/runtime/runManager.d.ts +18 -2
  217. package/dist/runtime/runManager.js +95 -30
  218. package/dist/runtime/runRetentionSweep.d.ts +0 -2
  219. package/dist/runtime/runRetentionSweep.js +0 -8
  220. package/dist/runtime/runRetryCoordinator.d.ts +2 -2
  221. package/dist/runtime/scheduleSource.js +15 -24
  222. package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
  223. package/dist/runtime/stageAttemptBootstrap.js +10 -1
  224. package/dist/runtime/stageProcessLauncher.js +2 -2
  225. package/dist/runtime/stageRoots.js +12 -1
  226. package/dist/runtime/stageRunner.d.ts +2 -2
  227. package/dist/runtime/stageRunner.js +8 -2
  228. package/dist/runtime/stageWorker.js +2 -2
  229. package/dist/runtime/triggerPort.d.ts +5 -3
  230. package/dist/runtime/triggerRunner.d.ts +1 -0
  231. package/dist/runtime/triggerRunner.js +27 -20
  232. package/dist/runtime/webhookAccept.d.ts +25 -0
  233. package/dist/runtime/webhookAccept.js +56 -0
  234. package/dist/server/bootstrap.d.ts +7 -2
  235. package/dist/server/bootstrap.js +15 -1
  236. package/dist/server/controlToken.d.ts +1 -1
  237. package/dist/server/controlToken.js +18 -2
  238. package/dist/server/healthSurfaces.d.ts +12 -0
  239. package/dist/server/healthSurfaces.js +27 -6
  240. package/dist/server/http.d.ts +19 -10
  241. package/dist/server/http.js +605 -85
  242. package/dist/server/liveViewPath.d.ts +9 -0
  243. package/dist/server/liveViewPath.js +8 -0
  244. package/dist/server/liveViewRoutes.d.ts +58 -0
  245. package/dist/server/liveViewRoutes.js +565 -0
  246. package/dist/server/mcpHost.d.ts +4 -2
  247. package/dist/server/mcpHost.js +2 -0
  248. package/dist/server/readCatalogFile.d.ts +29 -0
  249. package/dist/server/readCatalogFile.js +215 -0
  250. package/dist/server/triggerRoutes.d.ts +1 -0
  251. package/dist/server/triggerRoutes.js +200 -73
  252. package/dist/tools/readEmail.d.ts +3 -3
  253. package/dist/tools/writeStageArtifact.d.ts +9 -0
  254. package/dist/tools/writeStageArtifact.js +2 -47
  255. package/dist/types/pipeline.d.ts +1 -1
  256. package/dist/types/stage.d.ts +1 -1
  257. package/dist/types/stageflowManifest.d.ts +1 -1
  258. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  259. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  260. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  261. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  262. package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  263. package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  264. package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  265. package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  266. package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  267. package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  268. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  269. package/dist/ui/assets/index-BoFPkKFg.js +256 -0
  270. package/dist/ui/assets/{index-Bv4p1aPK.css → index-CIPbbBUi.css} +1 -1
  271. package/dist/ui/index.html +4 -4
  272. package/dist/workshop/autosave.d.ts +2 -2
  273. package/dist/workshop/autosave.js +4 -6
  274. package/dist/workshop/buildStore.d.ts +2 -0
  275. package/dist/workshop/chatAttachments.d.ts +27 -0
  276. package/dist/workshop/chatAttachments.js +98 -0
  277. package/dist/workshop/chatTurn.d.ts +5 -2
  278. package/dist/workshop/chatTurn.js +22 -18
  279. package/dist/workshop/sessionMutations.d.ts +17 -0
  280. package/dist/workshop/sessionMutations.js +107 -0
  281. package/dist/workshop/sessionStore.d.ts +10 -0
  282. package/dist/workshop/sessionStore.js +52 -0
  283. package/dist/workshop/workshopPin.d.ts +4 -3
  284. package/dist/workshop/workshopPin.js +26 -6
  285. package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
  286. package/dist/workshop/workshopTranscriptTurn.js +46 -0
  287. package/examples/README.md +1 -1
  288. package/examples/archify-on-pr/README.md +1 -1
  289. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
  290. package/examples/archify-on-pr/author-diagrams.yaml +1 -0
  291. package/examples/conditional-fork/branch-a.yaml +1 -1
  292. package/examples/conditional-fork/decide.yaml +2 -1
  293. package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
  294. package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
  295. package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
  296. package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
  297. package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
  298. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
  299. package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
  300. package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
  301. package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
  302. package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
  303. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
  304. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
  305. package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
  306. package/examples/hello-world/hello.pipeline.yaml +2 -2
  307. package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
  308. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
  309. package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
  310. package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
  311. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
  312. package/examples/stageflow.yaml +6 -0
  313. package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
  314. package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
  315. package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
  316. package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
  317. package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
  318. package/examples/triggers-readiness/README.md +48 -0
  319. package/examples/triggers-readiness/readiness-emit.yaml +18 -0
  320. package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
  321. package/examples/triggers-readiness/readiness.task.yaml +6 -0
  322. package/examples/triggers-readiness/stageflow.yaml +13 -0
  323. package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
  324. package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
  325. package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
  326. package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
  327. package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
  328. package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
  329. package/package.json +9 -1
  330. package/skills/stageflow/references/control-surface.md +1 -1
  331. package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
  332. package/skills/stageflow-run/SKILL.md +1 -1
  333. package/skills/stageflow-run/references/mcp-call.md +1 -1
  334. package/skills/stageflow-setup/SKILL.md +1 -1
  335. package/dist/ui/assets/index-59cjF0OA.js +0 -133
@@ -7,13 +7,17 @@ export function normalizeCatalogDir(directory) {
7
7
  return directory.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
8
8
  }
9
9
  /** Untitled workshop saves land here so Run can see them without a new catalog edit. */
10
- export function defaultWorkshopPackageDirectory(pipelineId) {
10
+ export function defaultWorkshopPackageDirectory(pipelineId, catalogRootKind) {
11
11
  const slug = (pipelineId ?? "")
12
12
  .toLowerCase()
13
13
  .replace(/[^a-z0-9]+/g, "-")
14
14
  .replace(/^-+|-+$/g, "")
15
15
  .slice(0, 40);
16
- return `${WORKSHOP_PACKAGE_ROOT}/${slug || "untitled"}`;
16
+ const id = slug || "untitled";
17
+ if (catalogRootKind === "workspace") {
18
+ return id;
19
+ }
20
+ return `${WORKSHOP_PACKAGE_ROOT}/${id}`;
17
21
  }
18
22
  /**
19
23
  * Catalog root that covers this package directory.
@@ -0,0 +1,33 @@
1
+ import type { TaskFile } from "../types/task.js";
2
+ import { type ResolveCatalogRootsOptions } from "./resolveCatalogRoots.js";
3
+ export type TaskDetail = TaskFile & {
4
+ path: string;
5
+ };
6
+ export type TaskDetailWithRoot = TaskDetail & {
7
+ project_root: string;
8
+ };
9
+ export type FindTaskInCatalogMultiResult = {
10
+ ok: true;
11
+ task: TaskDetailWithRoot;
12
+ } | {
13
+ ok: false;
14
+ status: 404;
15
+ error: string;
16
+ } | {
17
+ ok: false;
18
+ status: 400;
19
+ error: string;
20
+ code: "unknown_project_root";
21
+ } | {
22
+ ok: false;
23
+ status: 409;
24
+ error: string;
25
+ code: "ambiguous_task";
26
+ project_roots: string[];
27
+ };
28
+ export declare function findTaskInProjectRoot(projectRoot: string, taskId: string): Promise<TaskDetail | null>;
29
+ export declare function findTaskInCatalogMulti(options: ResolveCatalogRootsOptions & {
30
+ taskId: string;
31
+ projectRootFilter?: string;
32
+ }): Promise<FindTaskInCatalogMultiResult>;
33
+ export declare function findTaskInCatalog(cwd: string, taskId: string): Promise<TaskDetail | null>;
@@ -0,0 +1,69 @@
1
+ import path from "node:path";
2
+ import { getCatalogScanPaths } from "./browseCatalog.js";
3
+ import { loadTaskOutcome } from "./loadTask.js";
4
+ import { listTasksMultiProject } from "./multiProjectCatalog.js";
5
+ import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
6
+ import { findCatalogRoot, } from "./resolveCatalogRoots.js";
7
+ import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
8
+ export async function findTaskInProjectRoot(projectRoot, taskId) {
9
+ const ctx = catalogContextFromStageflow(await resolveStageflowContext(projectRoot));
10
+ const scanPaths = await getCatalogScanPaths(ctx);
11
+ if (!scanPaths || ctx.projectRoot === null) {
12
+ return null;
13
+ }
14
+ const root = ctx.projectRoot;
15
+ for (const filePath of scanPaths.taskPaths) {
16
+ const outcome = await loadTaskOutcome(filePath);
17
+ if (!outcome.ok || outcome.value.id !== taskId) {
18
+ continue;
19
+ }
20
+ return {
21
+ ...outcome.value,
22
+ path: path.relative(root, filePath).replace(/\\/g, "/"),
23
+ };
24
+ }
25
+ return null;
26
+ }
27
+ export async function findTaskInCatalogMulti(options) {
28
+ const { taskId, projectRootFilter, ...rootOptions } = options;
29
+ const listed = await listTasksMultiProject({
30
+ ...rootOptions,
31
+ projectRootFilter,
32
+ });
33
+ if (projectRootFilter !== undefined &&
34
+ listed.root_errors.some((e) => e.code === "unknown_project_root")) {
35
+ return {
36
+ ok: false,
37
+ status: 400,
38
+ error: listed.root_errors[0].message,
39
+ code: "unknown_project_root",
40
+ };
41
+ }
42
+ const matches = listed.items.filter((t) => t.id === taskId);
43
+ if (matches.length === 0) {
44
+ return { ok: false, status: 404, error: `Task not found: ${taskId}` };
45
+ }
46
+ const projectRoots = [...new Set(matches.map((m) => m.project_root))];
47
+ if (projectRoots.length > 1) {
48
+ return {
49
+ ok: false,
50
+ status: 409,
51
+ error: `Task id is ambiguous across catalog roots: ${taskId}`,
52
+ code: "ambiguous_task",
53
+ project_roots: projectRoots.sort((a, b) => a.localeCompare(b)),
54
+ };
55
+ }
56
+ const wireRoot = projectRoots[0];
57
+ const catalogRoot = findCatalogRoot(listed.roots, wireRoot);
58
+ if (catalogRoot === undefined) {
59
+ return { ok: false, status: 404, error: `Task not found: ${taskId}` };
60
+ }
61
+ const detail = await findTaskInProjectRoot(catalogRoot.path, taskId);
62
+ if (!detail) {
63
+ return { ok: false, status: 404, error: `Task not found: ${taskId}` };
64
+ }
65
+ return { ok: true, task: { ...detail, project_root: wireRoot } };
66
+ }
67
+ export async function findTaskInCatalog(cwd, taskId) {
68
+ return findTaskInProjectRoot(cwd, taskId);
69
+ }
@@ -9,6 +9,14 @@ export declare const KNOWN_STAGEFLOW_ENV_KEYS: Set<string>;
9
9
  export type CallerQuotaConfig = {
10
10
  maxConcurrent: number;
11
11
  };
12
+ export declare const DEFAULT_BROWSER_DIALOG_TIMEOUT_SECONDS = 60;
13
+ export type BrowserContainerConfig = {
14
+ image: string;
15
+ shmSize: string;
16
+ memory: string;
17
+ pidsLimit: number;
18
+ };
19
+ export declare const DEFAULT_BROWSER_CONTAINER_IMAGE = "stageflow-browser-sandbox:local";
12
20
  export type HostConfig = {
13
21
  maxConcurrentRuns: number;
14
22
  maxConcurrentRunsPerProject: number | undefined;
@@ -22,6 +30,18 @@ export type HostConfig = {
22
30
  callers: Record<string, CallerQuotaConfig>;
23
31
  /** Domains no browser stage may use (`browser.blocked_sites` in config.yaml). */
24
32
  browserBlockedSites: string[];
33
+ /** Chrome launch arguments (`browser.launch_args`); empty when unset. */
34
+ browserLaunchArgs: string[];
35
+ /** Explicit Chrome path (`browser.executable_path`); undefined when unset. */
36
+ browserExecutablePath: string | undefined;
37
+ /** Seconds an unanswered live view page dialog waits before the Host dismisses it (`browser.dialog_timeout_seconds`). */
38
+ browserDialogTimeoutSeconds: number;
39
+ /** Where shared profile browsers run (`browser.host`): this machine, or one container per run. */
40
+ browserHost: "local" | "container";
41
+ /** Per-owner adapter override (`browser.owner_hosts`, owner scope to host); scopes absent here use `browserHost`. */
42
+ browserOwnerHosts: Record<string, "local" | "container">;
43
+ /** Container host settings (`browser.container`); defaults apply when unset. */
44
+ browserContainer: BrowserContainerConfig;
25
45
  /** Absolute path of config.yaml when loaded; undefined if absent. */
26
46
  configFilePath: string | undefined;
27
47
  warnings: string[];
@@ -45,6 +65,7 @@ export type HostConfigPublicEcho = {
45
65
  callers: Record<string, CallerQuotaConfig>;
46
66
  secrets: HostConfigSecretsEcho;
47
67
  };
68
+ export declare function browserHostForOwner(config: Pick<HostConfig, "browserHost" | "browserOwnerHosts">, scope: string): "local" | "container";
48
69
  /**
49
70
  * Assemble HostConfig once at boot. Precedence: overrides (CLI flags) > env > file > default.
50
71
  */
@@ -2,6 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import { globalStageflowHome } from "../project/globalHome.js";
5
+ import { validateProfileScope } from "../browser/profileStore.js";
5
6
  import { readSecretFromEnvOrFile } from "./secretFromEnvOrFile.js";
6
7
  export class HostConfigError extends Error {
7
8
  code;
@@ -16,7 +17,9 @@ export class HostConfigError extends Error {
16
17
  /** Env keys that are known STAGEFLOW_* (or dynamic families) so unknown-key check accepts them. */
17
18
  export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
18
19
  "STAGEFLOW_HOME",
20
+ "STAGEFLOW_CREDENTIAL_HOME",
19
21
  "STAGEFLOW_BIND",
22
+ "STAGEFLOW_ALLOW_UNAUTHENTICATED_BIND",
20
23
  "STAGEFLOW_ALLOWED_HOSTS",
21
24
  "STAGEFLOW_CONTROL_TOKEN",
22
25
  "STAGEFLOW_CONTROL_TOKEN_FILE",
@@ -40,6 +43,7 @@ export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
40
43
  "STAGEFLOW_LOG_MAX_LINE_BYTES",
41
44
  "STAGEFLOW_MCP_STATELESS",
42
45
  "STAGEFLOW_DISK_WARN_BYTES",
46
+ "STAGEFLOW_ENSURE_PROJECT_ROOTS",
43
47
  "STAGEFLOW_MIN_FREE_DISK_BYTES",
44
48
  "STAGEFLOW_BARE_CACHE_TTL_MS",
45
49
  "STAGEFLOW_SLIM_ARTIFACT_MAX_BYTES",
@@ -82,6 +86,11 @@ const FILE_KEY_TO_FIELD = {
82
86
  callers: "callers",
83
87
  browser: "browser",
84
88
  };
89
+ export const DEFAULT_BROWSER_DIALOG_TIMEOUT_SECONDS = 60;
90
+ export const DEFAULT_BROWSER_CONTAINER_IMAGE = "stageflow-browser-sandbox:local";
91
+ export function browserHostForOwner(config, scope) {
92
+ return config.browserOwnerHosts[scope] ?? config.browserHost;
93
+ }
85
94
  const DEFAULT_MAX_CONCURRENT = 3;
86
95
  const DEFAULT_MAX_QUEUED = 32;
87
96
  function isKnownStageflowEnvKey(key) {
@@ -303,16 +312,113 @@ export function loadHostConfig(options) {
303
312
  callers = coerceCallers(file.values.callers, "callers");
304
313
  }
305
314
  let browserBlockedSites = [];
315
+ let browserLaunchArgs = [];
316
+ let browserExecutablePath;
317
+ let browserDialogTimeoutSeconds = DEFAULT_BROWSER_DIALOG_TIMEOUT_SECONDS;
318
+ let browserHost = "local";
319
+ const browserOwnerHosts = {};
320
+ const browserContainer = {
321
+ image: DEFAULT_BROWSER_CONTAINER_IMAGE,
322
+ shmSize: "1g",
323
+ memory: "1g",
324
+ pidsLimit: 512,
325
+ };
306
326
  if (file.values.browser !== undefined) {
307
327
  const raw = file.values.browser;
308
328
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
309
329
  throw new HostConfigError(`Invalid value for browser`, "config_invalid", "browser");
310
330
  }
311
331
  for (const sub of Object.keys(raw)) {
312
- if (sub !== "blocked_sites") {
332
+ if (sub !== "blocked_sites" && sub !== "launch_args" && sub !== "executable_path" && sub !== "dialog_timeout_seconds" && sub !== "host" && sub !== "container" && sub !== "owner_hosts") {
313
333
  throw new HostConfigError(`Unknown key "${sub}" under browser`, "config_unknown_key", `browser.${sub}`);
314
334
  }
315
335
  }
336
+ const args = raw.launch_args;
337
+ if (args !== undefined && args !== null) {
338
+ if (!Array.isArray(args) || !args.every((a) => typeof a === "string")) {
339
+ throw new HostConfigError("Invalid value for browser.launch_args; expected a list of strings", "config_invalid", "browser.launch_args");
340
+ }
341
+ for (const arg of args) {
342
+ if (arg.trim() === "" || /[,\r\n]/.test(arg)) {
343
+ throw new HostConfigError(`Invalid item ${JSON.stringify(arg)} in browser.launch_args; each item must be non-empty with no comma or newline`, "config_invalid", "browser.launch_args");
344
+ }
345
+ }
346
+ browserLaunchArgs = args.map((a) => a.trim());
347
+ }
348
+ const exe = raw.executable_path;
349
+ if (exe !== undefined && exe !== null) {
350
+ if (typeof exe !== "string" ||
351
+ exe.trim() === "" ||
352
+ !(path.posix.isAbsolute(exe.trim()) || path.win32.isAbsolute(exe.trim()))) {
353
+ throw new HostConfigError("Invalid value for browser.executable_path; expected a non-empty absolute path", "config_invalid", "browser.executable_path");
354
+ }
355
+ browserExecutablePath = exe.trim();
356
+ }
357
+ const dialogTimeout = raw.dialog_timeout_seconds;
358
+ if (dialogTimeout !== undefined && dialogTimeout !== null) {
359
+ if (typeof dialogTimeout !== "number" ||
360
+ !Number.isInteger(dialogTimeout) ||
361
+ dialogTimeout < 1) {
362
+ throw new HostConfigError("Invalid value for browser.dialog_timeout_seconds; expected a positive integer", "config_invalid", "browser.dialog_timeout_seconds");
363
+ }
364
+ browserDialogTimeoutSeconds = dialogTimeout;
365
+ }
366
+ const host = raw.host;
367
+ if (host !== undefined && host !== null) {
368
+ if (host !== "local" && host !== "container") {
369
+ throw new HostConfigError('Invalid value for browser.host; expected "local" or "container"', "config_invalid", "browser.host");
370
+ }
371
+ browserHost = host;
372
+ }
373
+ const ownerHosts = raw.owner_hosts;
374
+ if (ownerHosts !== undefined && ownerHosts !== null) {
375
+ if (typeof ownerHosts !== "object" || Array.isArray(ownerHosts)) {
376
+ throw new HostConfigError("Invalid value for browser.owner_hosts", "config_invalid", "browser.owner_hosts");
377
+ }
378
+ for (const [scope, value] of Object.entries(ownerHosts)) {
379
+ try {
380
+ validateProfileScope(scope);
381
+ }
382
+ catch {
383
+ throw new HostConfigError(`Invalid owner scope "${scope}" in browser.owner_hosts`, "config_invalid", "browser.owner_hosts");
384
+ }
385
+ if (value !== "local" && value !== "container") {
386
+ throw new HostConfigError(`Invalid value for browser.owner_hosts.${scope}; expected "local" or "container"`, "config_invalid", `browser.owner_hosts.${scope}`);
387
+ }
388
+ browserOwnerHosts[scope] = value;
389
+ }
390
+ }
391
+ const container = raw.container;
392
+ if (container !== undefined && container !== null) {
393
+ if (typeof container !== "object" || Array.isArray(container)) {
394
+ throw new HostConfigError("Invalid value for browser.container", "config_invalid", "browser.container");
395
+ }
396
+ const fields = container;
397
+ for (const sub of Object.keys(fields)) {
398
+ if (!["image", "shm_size", "memory", "pids_limit"].includes(sub)) {
399
+ throw new HostConfigError(`Unknown key "${sub}" under browser.container`, "config_unknown_key", `browser.container.${sub}`);
400
+ }
401
+ }
402
+ const text = (key, pattern, target) => {
403
+ const value = fields[key];
404
+ if (value === undefined || value === null)
405
+ return;
406
+ if (typeof value !== "string" || !pattern.test(value.trim())) {
407
+ throw new HostConfigError(`Invalid value for browser.container.${key}`, "config_invalid", `browser.container.${key}`);
408
+ }
409
+ browserContainer[target] = value.trim();
410
+ };
411
+ text("image", /^[A-Za-z0-9][A-Za-z0-9._\-/:@]*$/, "image");
412
+ text("shm_size", /^[1-9][0-9]*[bkmg]?$/i, "shmSize");
413
+ text("memory", /^[1-9][0-9]*[bkmg]?$/i, "memory");
414
+ const pids = fields.pids_limit;
415
+ if (pids !== undefined && pids !== null) {
416
+ if (typeof pids !== "number" || !Number.isInteger(pids) || pids < 1) {
417
+ throw new HostConfigError("Invalid value for browser.container.pids_limit; expected a positive integer", "config_invalid", "browser.container.pids_limit");
418
+ }
419
+ browserContainer.pidsLimit = pids;
420
+ }
421
+ }
316
422
  const sites = raw.blocked_sites;
317
423
  if (sites !== undefined) {
318
424
  browserBlockedSites = coerceFileStringList(sites, "browser.blocked_sites")
@@ -336,6 +442,12 @@ export function loadHostConfig(options) {
336
442
  readToken,
337
443
  callers,
338
444
  browserBlockedSites,
445
+ browserLaunchArgs,
446
+ browserExecutablePath,
447
+ browserDialogTimeoutSeconds,
448
+ browserHost,
449
+ browserOwnerHosts,
450
+ browserContainer,
339
451
  configFilePath: configFilePath !== undefined && existsSync(configFilePath)
340
452
  ? configFilePath
341
453
  : undefined,
@@ -0,0 +1,19 @@
1
+ import { type CreateTaskResult } from "./createTask.js";
2
+ export declare const GITHUB_REPO_PATTERN: RegExp;
3
+ export type ImportTaskInput = {
4
+ repo: string;
5
+ number: number;
6
+ directory: string;
7
+ };
8
+ export type ImportTaskParseError = {
9
+ ok: false;
10
+ status: 400;
11
+ error: string;
12
+ };
13
+ export type IssueFetch = (url: string, init: {
14
+ headers: Record<string, string>;
15
+ }) => Promise<Response>;
16
+ export declare function parseImportTaskBody(body: unknown): ImportTaskInput | ImportTaskParseError;
17
+ export declare function slugIssueTitle(title: string, issueNumber: number): string;
18
+ export declare function taskIdWithSuffix(baseId: string, suffixNumber: number): string;
19
+ export declare function importTaskFromIssue(rawProjectRoot: string, input: ImportTaskInput, fetchImpl?: IssueFetch): Promise<CreateTaskResult>;
@@ -0,0 +1,133 @@
1
+ import path from "node:path";
2
+ import { realpath } from "node:fs/promises";
3
+ import { STAGE_ID_PATTERN } from "./createStage.js";
4
+ import { createTask, findTaskIdCollision, resolveTaskDirectory, validateTaskId, } from "./createTask.js";
5
+ export const GITHUB_REPO_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
6
+ function isPlainObject(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value);
8
+ }
9
+ export function parseImportTaskBody(body) {
10
+ if (!isPlainObject(body)) {
11
+ return { ok: false, status: 400, error: "Request body must be an object" };
12
+ }
13
+ if (typeof body.repo !== "string" || !GITHUB_REPO_PATTERN.test(body.repo)) {
14
+ return { ok: false, status: 400, error: "repo must be a GitHub owner/repo" };
15
+ }
16
+ if (typeof body.number !== "number" || !Number.isInteger(body.number) || body.number <= 0) {
17
+ return { ok: false, status: 400, error: "number must be a positive integer" };
18
+ }
19
+ if (typeof body.directory !== "string" || body.directory.trim().length === 0) {
20
+ return { ok: false, status: 400, error: "directory is required" };
21
+ }
22
+ const directory = body.directory.trim().replace(/\\/g, "/").replace(/\/+$/, "");
23
+ if (directory.length === 0) {
24
+ return { ok: false, status: 400, error: "directory is required" };
25
+ }
26
+ return { repo: body.repo, number: body.number, directory };
27
+ }
28
+ export function slugIssueTitle(title, issueNumber) {
29
+ const fallback = `issue-${issueNumber}`;
30
+ let slug = title
31
+ .toLowerCase()
32
+ .replace(/[^a-z0-9]+/g, "-")
33
+ .replace(/^-+|-+$/g, "")
34
+ .replace(/^[^a-z]+/, "")
35
+ .replace(/^-+|-+$/g, "");
36
+ if (slug.length > 64) {
37
+ slug = slug.slice(0, 64).replace(/-+$/g, "");
38
+ }
39
+ if (!slug || !STAGE_ID_PATTERN.test(slug))
40
+ return fallback;
41
+ return slug;
42
+ }
43
+ export function taskIdWithSuffix(baseId, suffixNumber) {
44
+ const suffix = `-${suffixNumber}`;
45
+ let stem = baseId;
46
+ if (stem.length + suffix.length > 64) {
47
+ stem = stem.slice(0, 64 - suffix.length).replace(/-+$/g, "");
48
+ }
49
+ if (!stem || !STAGE_ID_PATTERN.test(stem))
50
+ stem = "issue";
51
+ const candidate = `${stem}${suffix}`;
52
+ if (candidate.length <= 64 && STAGE_ID_PATTERN.test(candidate))
53
+ return candidate;
54
+ return `issue${suffix}`;
55
+ }
56
+ async function allocateTaskId(projectRoot, directory, baseId) {
57
+ for (let n = 1; n <= 100; n += 1) {
58
+ const id = n === 1 ? baseId : taskIdWithSuffix(baseId, n);
59
+ const idError = validateTaskId(id);
60
+ if (idError)
61
+ continue;
62
+ const targetPath = path.join(directory, `${id}.task.yaml`);
63
+ const collision = await findTaskIdCollision(projectRoot, id, targetPath);
64
+ if (!collision)
65
+ return id;
66
+ }
67
+ return null;
68
+ }
69
+ function githubHeaders() {
70
+ const headers = {
71
+ Accept: "application/vnd.github+json",
72
+ "User-Agent": "stageflow",
73
+ };
74
+ const token = process.env.GITHUB_TOKEN;
75
+ if (typeof token === "string" && token.length > 0) {
76
+ headers.Authorization = `Bearer ${token}`;
77
+ }
78
+ return headers;
79
+ }
80
+ export async function importTaskFromIssue(rawProjectRoot, input, fetchImpl = (url, init) => fetch(url, init)) {
81
+ if (!GITHUB_REPO_PATTERN.test(input.repo)) {
82
+ return { ok: false, status: 400, error: "repo must be a GitHub owner/repo" };
83
+ }
84
+ if (!Number.isInteger(input.number) || input.number <= 0) {
85
+ return { ok: false, status: 400, error: "number must be a positive integer" };
86
+ }
87
+ const projectRoot = await realpath(rawProjectRoot).catch(() => rawProjectRoot);
88
+ const directory = resolveTaskDirectory(projectRoot, input.directory);
89
+ if (!directory) {
90
+ return { ok: false, status: 400, error: "directory must be inside the project root" };
91
+ }
92
+ const url = `https://api.github.com/repos/${input.repo}/issues/${input.number}`;
93
+ let response;
94
+ try {
95
+ response = await fetchImpl(url, { headers: githubHeaders() });
96
+ }
97
+ catch (err) {
98
+ const message = err instanceof Error ? err.message : String(err);
99
+ return { ok: false, status: 502, error: message };
100
+ }
101
+ if (response.status === 404) {
102
+ return {
103
+ ok: false,
104
+ status: 404,
105
+ error: `GitHub issue not found: ${input.repo}#${input.number}`,
106
+ };
107
+ }
108
+ if (!response.ok) {
109
+ return { ok: false, status: 502, error: `GitHub request failed (${response.status})` };
110
+ }
111
+ let payload;
112
+ try {
113
+ payload = await response.json();
114
+ }
115
+ catch {
116
+ return { ok: false, status: 502, error: "GitHub response was not JSON" };
117
+ }
118
+ if (!isPlainObject(payload) || typeof payload.title !== "string" || payload.title.length === 0) {
119
+ return { ok: false, status: 502, error: "GitHub issue is missing a title" };
120
+ }
121
+ const context = typeof payload.body === "string" ? payload.body : "";
122
+ const baseId = slugIssueTitle(payload.title, input.number);
123
+ const id = await allocateTaskId(projectRoot, directory, baseId);
124
+ if (!id) {
125
+ return { ok: false, status: 409, error: "Unable to allocate a task id" };
126
+ }
127
+ return createTask(projectRoot, {
128
+ directory: input.directory,
129
+ id,
130
+ goal: payload.title,
131
+ ...(context.length > 0 ? { context } : {}),
132
+ });
133
+ }
@@ -0,0 +1,40 @@
1
+ export type InstallSkillParseError = {
2
+ ok: false;
3
+ status: 400;
4
+ error: string;
5
+ };
6
+ export type ParsedInstallSkillRequest = {
7
+ command: string;
8
+ };
9
+ export type ParsedSkillsAddCommand = {
10
+ source: string;
11
+ skillName?: string;
12
+ };
13
+ export type SkillCloneFn = (cloneUrl: string, destDir: string) => Promise<void>;
14
+ export type InstallSkillFromCommandResult = {
15
+ ok: true;
16
+ skill_name: string;
17
+ dest_path: string;
18
+ project_root: string;
19
+ } | {
20
+ ok: false;
21
+ error: string;
22
+ doctorFailed?: boolean;
23
+ status?: 400 | 422;
24
+ };
25
+ export declare function parseSkillsAddCommand(command: string): ParsedSkillsAddCommand | InstallSkillParseError;
26
+ export declare function parseInstallSkillBody(body: unknown): ParsedInstallSkillRequest | InstallSkillParseError;
27
+ export type LocatedSkill = {
28
+ name: string;
29
+ root: string;
30
+ };
31
+ export declare function locateSkillDirectory(checkoutRoot: string, skillName?: string): Promise<LocatedSkill>;
32
+ export declare function resolveGitCloneUrl(source: string): string;
33
+ export declare function defaultCloneSkillRepo(cloneUrl: string, destDir: string): Promise<void>;
34
+ export declare function installSkillFromCommand(input: {
35
+ projectRoot: string;
36
+ command: string;
37
+ cloneRepo?: SkillCloneFn;
38
+ }): Promise<InstallSkillFromCommandResult>;
39
+ /** Test helper: build a file: command for a local skill tree. */
40
+ export declare function skillsAddFileCommand(fromPath: string, skillName?: string): string;