tubeless 0.2.2 → 0.2.4

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 (199) hide show
  1. package/README.md +52 -52
  2. package/dist/cli/cli-types.d.ts +1 -1
  3. package/dist/cli/cli-types.js.map +1 -1
  4. package/dist/cli/cli.d.ts +2 -4
  5. package/dist/cli/cli.js +1 -3
  6. package/dist/cli/cli.js.map +1 -1
  7. package/dist/core/child-execution.js +9 -4
  8. package/dist/core/child-execution.js.map +1 -1
  9. package/dist/core/lifecycle.d.ts +1 -1
  10. package/dist/core/lifecycle.js +2 -0
  11. package/dist/core/lifecycle.js.map +1 -1
  12. package/dist/core/pipeline-compiler.d.ts +17 -0
  13. package/dist/core/pipeline-compiler.js +44 -0
  14. package/dist/core/pipeline-compiler.js.map +1 -0
  15. package/dist/core/pipeline-definition-validation.d.ts +4 -0
  16. package/dist/core/pipeline-definition-validation.js +101 -0
  17. package/dist/core/pipeline-definition-validation.js.map +1 -0
  18. package/dist/core/pipeline-definition.d.ts +28 -0
  19. package/dist/core/pipeline-definition.js +2 -0
  20. package/dist/core/pipeline-definition.js.map +1 -0
  21. package/dist/core/pipeline-disposition.d.ts +26 -0
  22. package/dist/core/pipeline-disposition.js +44 -0
  23. package/dist/core/pipeline-disposition.js.map +1 -0
  24. package/dist/core/pipeline-errors.d.ts +8 -0
  25. package/dist/core/pipeline-errors.js +20 -0
  26. package/dist/core/pipeline-errors.js.map +1 -0
  27. package/dist/core/pipeline-execute.d.ts +3 -1
  28. package/dist/core/pipeline-execute.js +136 -396
  29. package/dist/core/pipeline-execute.js.map +1 -1
  30. package/dist/core/pipeline-finalizer.d.ts +18 -0
  31. package/dist/core/pipeline-finalizer.js +37 -0
  32. package/dist/core/pipeline-finalizer.js.map +1 -0
  33. package/dist/core/pipeline-graph.d.ts +23 -0
  34. package/dist/core/pipeline-graph.js +135 -0
  35. package/dist/core/pipeline-graph.js.map +1 -0
  36. package/dist/core/pipeline-identity.d.ts +5 -0
  37. package/dist/core/pipeline-identity.js +11 -0
  38. package/dist/core/pipeline-identity.js.map +1 -0
  39. package/dist/core/pipeline-mermaid.d.ts +4 -0
  40. package/dist/core/pipeline-mermaid.js +56 -0
  41. package/dist/core/pipeline-mermaid.js.map +1 -0
  42. package/dist/core/pipeline-plan.d.ts +5 -94
  43. package/dist/core/pipeline-plan.js +5 -392
  44. package/dist/core/pipeline-plan.js.map +1 -1
  45. package/dist/core/pipeline-run-state.d.ts +49 -0
  46. package/dist/core/pipeline-run-state.js +284 -0
  47. package/dist/core/pipeline-run-state.js.map +1 -0
  48. package/dist/core/pipeline-step-executor.d.ts +20 -0
  49. package/dist/core/pipeline-step-executor.js +52 -0
  50. package/dist/core/pipeline-step-executor.js.map +1 -0
  51. package/dist/core/pipeline-step-metadata.d.ts +4 -0
  52. package/dist/core/pipeline-step-metadata.js +5 -0
  53. package/dist/core/pipeline-step-metadata.js.map +1 -0
  54. package/dist/core/pipeline-steps.d.ts +1 -1
  55. package/dist/core/pipeline-steps.js +1 -1
  56. package/dist/core/pipeline-steps.js.map +1 -1
  57. package/dist/core/pipeline-types.d.ts +17 -8
  58. package/dist/core/pipeline-types.js +8 -0
  59. package/dist/core/pipeline-types.js.map +1 -1
  60. package/dist/core/pipeline-validation.d.ts +6 -0
  61. package/dist/core/pipeline-validation.js +41 -0
  62. package/dist/core/pipeline-validation.js.map +1 -0
  63. package/dist/core/pipeline.d.ts +8 -9
  64. package/dist/core/pipeline.js +9 -6
  65. package/dist/core/pipeline.js.map +1 -1
  66. package/dist/node/node.d.ts +0 -3
  67. package/dist/node/node.js +0 -3
  68. package/dist/node/node.js.map +1 -1
  69. package/dist/reporter/reporter-entry.d.ts +1 -2
  70. package/dist/reporter/reporter-entry.js +1 -2
  71. package/dist/reporter/reporter-entry.js.map +1 -1
  72. package/dist/run-store/run-store-codec.d.ts +4 -0
  73. package/dist/run-store/run-store-codec.js +31 -0
  74. package/dist/run-store/run-store-codec.js.map +1 -0
  75. package/dist/run-store/run-store-ndjson.js +2 -295
  76. package/dist/run-store/run-store-ndjson.js.map +1 -1
  77. package/dist/run-store/run-store-sqlite.js +41 -38
  78. package/dist/run-store/run-store-sqlite.js.map +1 -1
  79. package/dist/run-store/run-store.d.ts +4 -3
  80. package/dist/run-store/run-store.js +38 -195
  81. package/dist/run-store/run-store.js.map +1 -1
  82. package/dist/studio/run-store-ui-client-app.d.ts +12 -0
  83. package/dist/studio/run-store-ui-client-app.js +224 -0
  84. package/dist/studio/run-store-ui-client-app.js.map +1 -0
  85. package/dist/studio/run-store-ui-client-browser.d.ts +1 -0
  86. package/dist/studio/run-store-ui-client-browser.js +2 -0
  87. package/dist/studio/run-store-ui-client-commands.d.ts +18 -0
  88. package/dist/studio/run-store-ui-client-commands.js +98 -0
  89. package/dist/studio/run-store-ui-client-commands.js.map +1 -0
  90. package/dist/studio/run-store-ui-client-form.d.ts +11 -0
  91. package/dist/studio/run-store-ui-client-form.js +54 -0
  92. package/dist/studio/run-store-ui-client-form.js.map +1 -0
  93. package/dist/studio/run-store-ui-client-modals.d.ts +20 -0
  94. package/dist/studio/run-store-ui-client-modals.js +154 -0
  95. package/dist/studio/run-store-ui-client-modals.js.map +1 -0
  96. package/dist/studio/run-store-ui-client-model.d.ts +19 -0
  97. package/dist/studio/run-store-ui-client-model.js +189 -0
  98. package/dist/studio/run-store-ui-client-model.js.map +1 -0
  99. package/dist/studio/run-store-ui-client-runs.d.ts +24 -0
  100. package/dist/studio/run-store-ui-client-runs.js +78 -0
  101. package/dist/studio/run-store-ui-client-runs.js.map +1 -0
  102. package/dist/studio/run-store-ui-client-shared.d.ts +22 -0
  103. package/dist/studio/run-store-ui-client-shared.js +63 -0
  104. package/dist/studio/run-store-ui-client-shared.js.map +1 -0
  105. package/dist/studio/run-store-ui-client-transport.d.ts +25 -0
  106. package/dist/studio/run-store-ui-client-transport.js +317 -0
  107. package/dist/studio/run-store-ui-client-transport.js.map +1 -0
  108. package/dist/studio/run-store-ui-client.d.ts +2 -14
  109. package/dist/studio/run-store-ui-client.js +9 -1360
  110. package/dist/studio/run-store-ui-client.js.map +1 -1
  111. package/dist/studio/run-store-ui-page.d.ts +2 -2
  112. package/dist/studio/run-store-ui-page.js +7 -71
  113. package/dist/studio/run-store-ui-page.js.map +1 -1
  114. package/dist/studio/run-store-ui.d.ts +1 -1
  115. package/dist/studio/run-store-ui.js +43 -25
  116. package/dist/studio/run-store-ui.js.map +1 -1
  117. package/dist/tracing/tracing-codec.d.ts +5 -0
  118. package/dist/tracing/tracing-codec.js +13 -0
  119. package/dist/tracing/tracing-codec.js.map +1 -0
  120. package/dist/tracing/tracing-constants.d.ts +4 -0
  121. package/dist/tracing/tracing-constants.js +5 -0
  122. package/dist/tracing/tracing-constants.js.map +1 -0
  123. package/dist/tracing/tracing-contracts.d.ts +31 -0
  124. package/dist/tracing/tracing-contracts.js +2 -0
  125. package/dist/tracing/tracing-contracts.js.map +1 -0
  126. package/dist/tracing/tracing-internal.d.ts +1 -1
  127. package/dist/tracing/tracing-internal.js +116 -81
  128. package/dist/tracing/tracing-internal.js.map +1 -1
  129. package/dist/tracing/tracing-schema.d.ts +809 -0
  130. package/dist/tracing/tracing-schema.js +429 -0
  131. package/dist/tracing/tracing-schema.js.map +1 -0
  132. package/dist/tracing/tracing.d.ts +2 -54
  133. package/dist/tracing/tracing.js.map +1 -1
  134. package/dist/tracing/wire-schema.d.ts +50 -0
  135. package/dist/tracing/wire-schema.js +193 -0
  136. package/dist/tracing/wire-schema.js.map +1 -0
  137. package/dist/workbench/pipeline-module.js +4 -4
  138. package/dist/workbench/pipeline-module.js.map +1 -1
  139. package/dist/workbench/workbench-entry.d.ts +2 -0
  140. package/dist/workbench/workbench-entry.js +3 -0
  141. package/dist/workbench/workbench-entry.js.map +1 -0
  142. package/dist/workbench/workbench-history.js +8 -1
  143. package/dist/workbench/workbench-history.js.map +1 -1
  144. package/dist/workbench/workbench-ui.js +39 -43
  145. package/dist/workbench/workbench-ui.js.map +1 -1
  146. package/docs/README.md +22 -15
  147. package/docs/agent-guide.md +62 -68
  148. package/docs/agent-skills.md +59 -0
  149. package/docs/api-reference.md +14 -178
  150. package/docs/api-report.json +13 -215
  151. package/docs/child-pipeline-composition.md +139 -193
  152. package/docs/cli.md +86 -64
  153. package/docs/comparison.md +64 -62
  154. package/docs/concepts.md +210 -281
  155. package/docs/getting-started.md +61 -49
  156. package/docs/llms.txt +8 -8
  157. package/docs/recipes.md +42 -45
  158. package/docs/remote-step-composition.md +69 -74
  159. package/docs/studio.md +110 -73
  160. package/examples/catalog/pipelines/enrich.ts +2 -2
  161. package/examples/catalog/scripts/enrich.ts +1 -1
  162. package/examples/catalog/scripts/import.ts +1 -1
  163. package/examples/catalog/scripts/publish.ts +1 -1
  164. package/examples/catalog/tubeless.project.ts +6 -3
  165. package/examples/cli-job.ts +1 -1
  166. package/examples/host-embedding.ts +4 -5
  167. package/examples/live-tui.ts +1 -1
  168. package/examples/peloton.ts +1 -1
  169. package/examples/remote-steps.ts +8 -5
  170. package/examples/tracing.ts +96 -5
  171. package/package.json +17 -64
  172. package/skills/tubeless/SKILL.md +85 -0
  173. package/skills/tubeless-make-pipeline/SKILL.md +126 -0
  174. package/dist/node/env.d.ts +0 -1
  175. package/dist/node/env.js +0 -8
  176. package/dist/node/env.js.map +0 -1
  177. package/dist/node/file-utils.d.ts +0 -3
  178. package/dist/node/file-utils.js +0 -17
  179. package/dist/node/file-utils.js.map +0 -1
  180. package/dist/node/paths.d.ts +0 -8
  181. package/dist/node/paths.js +0 -19
  182. package/dist/node/paths.js.map +0 -1
  183. package/dist/reporter/prompt-select.d.ts +0 -80
  184. package/dist/reporter/prompt-select.js +0 -197
  185. package/dist/reporter/prompt-select.js.map +0 -1
  186. package/dist/studio/run-store-ui-client-source.d.ts +0 -1
  187. package/dist/studio/run-store-ui-client-source.js +0 -1
  188. package/dist/tracing/tracing-json.d.ts +0 -11
  189. package/dist/tracing/tracing-json.js +0 -8
  190. package/dist/tracing/tracing-json.js.map +0 -1
  191. package/dist/tracing/tracing-otel.d.ts +0 -35
  192. package/dist/tracing/tracing-otel.js +0 -60
  193. package/dist/tracing/tracing-otel.js.map +0 -1
  194. package/dist/workbench/workbench-studio.d.ts +0 -29
  195. package/dist/workbench/workbench-studio.js +0 -61
  196. package/dist/workbench/workbench-studio.js.map +0 -1
  197. package/examples/catalog/tubeless.studio.ts +0 -20
  198. package/examples/local-observability.ts +0 -105
  199. package/examples/rendering.ts +0 -34
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: tubeless
3
+ description: Author, modify, or review typed Tubeless pipelines, pipeline-backed CLI commands, and project catalogs in TypeScript projects. Use for dependency modeling, failure and skip policies, dry runs, child pipelines, and pipeline tests.
4
+ ---
5
+
6
+ # Author Tubeless pipelines
7
+
8
+ Use the project's installed Tubeless version and public package imports. Keep
9
+ domain logic in ordinary functions and make orchestration explicit in the graph.
10
+
11
+ ## Find the matching documentation
12
+
13
+ Read the consumer's package manifest, installed Tubeless version, and existing
14
+ pipeline conventions first. The installed `tubeless` package includes `docs/`
15
+ and `examples/`; locate it through the project's package manager. Within the
16
+ Tubeless source repository, use those directories at the repository root.
17
+
18
+ Read `docs/agent-guide.md`, then the smallest matching example in
19
+ `docs/recipes.md`. These paths are relative to the **Tubeless package**, not
20
+ this installed skill or the consumer's repository. If package docs are absent,
21
+ start with the [public documentation index](https://tubeless.io/llms.txt),
22
+ [agent guide](https://tubeless.io/docs/agent-guide.md), and
23
+ [recipes](https://tubeless.io/docs/recipes.md). Confirm newer examples against
24
+ the installed declarations before using them; do not silently upgrade Tubeless.
25
+
26
+ ## Authoring decisions
27
+
28
+ - Use `createSteps<TDomainOptions>()` per pipeline. Domain options contain
29
+ business inputs; pass built-in controls separately to `run(options, controls?)`.
30
+ - Give steps stable kebab-case IDs and descriptions of their domain work.
31
+ `name` is an optional display label. Return values from steps and consume
32
+ inferred dependency outputs instead of sharing mutable state.
33
+ - Use `dependsOn` for required outputs, `optionalDependsOn` for expected
34
+ absence, and `skipAfterFailureOf` for a failure gate without a required value.
35
+ Keep publication dependent on successful validation.
36
+ - Use `step.skippable` for an intentional successful omission. Handle its
37
+ `T | undefined` output explicitly; do not turn exceptions into skips.
38
+ - Set `dryRun: "skip"` on writes and other external side effects, or supply a
39
+ side-effect-free typed preview handler. Unmarked steps still run in dry runs.
40
+ - Use `requireOutputs` when the final result requires specific outputs. A plain
41
+ finalizer is appropriate when partial results are valid domain results.
42
+ - Use `runOrThrow` for callers expecting a successful value, `run` for callers
43
+ inspecting structured failures or partial results, and `plan(controls)` when
44
+ no work should execute. Plans do not validate domain input.
45
+ - Use `context.log`, forward `context.signal`, use `context.sleep` for waits,
46
+ and report progress for long loops. Resolve relative paths from `context.cwd`.
47
+ - Branch on structured error `code`, `phase`, and `kind`, not message text.
48
+
49
+ ## Add only the capabilities the workflow needs
50
+
51
+ Read the corresponding package recipe before using these features:
52
+
53
+ - `fromPipeline` for an independently useful child workflow;
54
+ `forEachPipeline` for runtime fan-out with stable keys and bounded concurrency.
55
+ Use ordinary helpers or `runConcurrent` for lightweight work without child
56
+ lifecycle reporting.
57
+ - `createSteps(optionsSchema)`, `outputSchema`, or `resultSchema` for runtime
58
+ validation at untrusted boundaries. Reuse the project's Standard Schema
59
+ implementation; core needs no schema dependency.
60
+ - `definePipelineCommand` from `tubeless/workbench` for pipeline-backed scripts.
61
+ Built-in `--step` / `--target` flags map to `stepIds` / `targets`. Do not
62
+ redeclare built-in flags. Read `docs/cli.md` for option mapping.
63
+ - Use `pipelines/<name>.ts` for definitions and `scripts/<name>.ts` for command
64
+ wrappers when introducing a layout. Register commands explicitly in
65
+ `tubeless.project.ts`; adapt the package's `examples/catalog/` without copying
66
+ unrelated example IDs. Preserve existing consumer conventions.
67
+ - Keep storage and Studio optional. Read `docs/studio.md` before adding them;
68
+ read the composition guides before adding child or remote execution.
69
+ - Implement concrete JSON or telemetry adapters in the application against
70
+ `PipelineTraceExporter`; adapt the package's `examples/tracing.ts`. Use
71
+ `composeTraceExporters` for multiple trace destinations. Finished traces
72
+ can be read with `tubeless history --trace` or `tubeless ui --trace`; recorded
73
+ contents are not redacted.
74
+
75
+ ## Verify
76
+
77
+ Typecheck the consumer and run focused tests. Use `createPipelineTestRuntime`
78
+ from `tubeless/testing` for deterministic observation and cancellation, with
79
+ fake I/O for side effects. Check failure gates, skipped outputs, and dry-run
80
+ behavior. Generate diagrams with `pipeline.toMermaid()` when useful.
81
+
82
+ Within the Tubeless source repository, also follow its `AGENTS.md` and run
83
+ `make check`. For repository evaluations, write `solution.ts` in a disposable
84
+ directory and compile with `bun run eval:agent --`; do not execute model-written
85
+ submissions in the repository. Follow `docs/agent-evaluations.md` for assessment.
@@ -0,0 +1,126 @@
1
+ ---
2
+ name: tubeless-make-pipeline
3
+ description: Convert existing TypeScript or JavaScript scripts, jobs, service workflows, or ETL code into thoughtfully structured, typed Tubeless pipelines. Use when asked to extract a pipeline from existing code, replace ad hoc orchestration, or organize a large workflow into typed steps while preserving behavior.
4
+ ---
5
+
6
+ # Make a Tubeless pipeline from existing code
7
+
8
+ Implement the conversion in the user's project. Preserve observable behavior and
9
+ reuse domain functions while making data flow, ordering, and failure policy
10
+ visible in a typed graph.
11
+
12
+ ## Ground the design in the existing code
13
+
14
+ Read the entrypoint, its callers, relevant helpers, tests, and package manifest.
15
+ Trace inputs, outputs, external effects, error handling, retries, cancellation,
16
+ and any shared mutable state. Identify ordering requirements that are currently
17
+ implicit, such as validation before publication or writes inside a transaction.
18
+ Preserve transaction boundaries, cleanup, and resource ownership when splitting
19
+ work. A transaction or critical section can remain inside one step.
20
+
21
+ Identify the installed Tubeless version. Read `docs/agent-guide.md` and the
22
+ smallest matching `docs/recipes.md` example from that installed package, or from
23
+ the package root when working in Tubeless itself. These are package paths, not
24
+ paths relative to this skill. If unavailable, use the
25
+ [agent guide](https://tubeless.io/docs/agent-guide.md) and
26
+ [recipe index](https://tubeless.io/docs/recipes.md), checking APIs against the
27
+ installed declarations. If Tubeless is missing, add it using the project's
28
+ package manager and dependency conventions. Avoid unrelated upgrades.
29
+
30
+ Before editing, briefly explain the proposed steps, their inputs and outputs,
31
+ and which steps can write externally. Resolve uncertainties from code and tests;
32
+ ask only when a missing domain decision affects correctness.
33
+
34
+ ## Choose meaningful boundaries
35
+
36
+ Create steps around domain outcomes an operator would recognize: load records,
37
+ validate a batch, enrich records, build an artifact, publish it. A useful step
38
+ has a coherent output and a reason to appear in a plan or failure report.
39
+
40
+ - Keep small parsing, formatting, and mapping functions as helpers inside a
41
+ step. Do not make every function call a step or hide the entire old script in
42
+ one step when it contains distinct stages.
43
+ - Build dependencies from consumed values and required ordering. Do not rely
44
+ on declaration order. Keep independent work independent, but preserve the
45
+ original concurrency, rate limits, and ordering unless a change is intended.
46
+ - Use `dependsOn` when a value or successful prerequisite is required. Use
47
+ `optionalDependsOn` only when absence is a valid case. Use
48
+ `skipAfterFailureOf` when a failed or cancelled prerequisite must block work
49
+ without requiring its output. Optional input alone is not a failure gate.
50
+ - Replace shared mutable accumulators with returned step outputs. Reuse
51
+ domain types and helpers; avoid a generic bag of optional fields passed
52
+ through every step.
53
+ - Extract a child pipeline only when that workflow is independently useful.
54
+ Choose `forEachPipeline` for per-item lifecycle reporting; use bounded helper
55
+ concurrency when that reporting is unnecessary. Read the matching recipe
56
+ before introducing either.
57
+
58
+ ## Keep the graph typed
59
+
60
+ Use one `createSteps<TDomainOptions>()` builder and one `definePipeline` per
61
+ pipeline. Import from `tubeless` and its public subpaths. Keep business inputs
62
+ in domain options; built-in run controls belong in the separate controls argument.
63
+
64
+ Give each step a stable literal kebab-case ID and an operational description.
65
+ Let `dependsOn: [step]` infer input types from returned outputs. Preserve
66
+ inference in step collections; do not widen IDs to `string` or use `any`, broad
67
+ records, or type assertions to bypass graph errors. Add explicit output types
68
+ where they define a domain contract, rather than annotating every intermediate.
69
+
70
+ Parse untrusted inputs as `unknown` and validate at the boundary with existing
71
+ project validators. Use Standard Schema support when it fits existing schemas;
72
+ TypeScript alone does not validate external data.
73
+
74
+ Use `requireOutputs` for a result that requires completed steps. If dry runs,
75
+ intentional skips, or best-effort execution legitimately produce partial
76
+ results, model that result explicitly and narrow missing values. Do not default
77
+ a missing required output to an empty array, `false`, or a fake success value.
78
+
79
+ ## Preserve operational behavior
80
+
81
+ - Mark filesystem writes, database mutations, publication, and other external
82
+ side effects with `dryRun: "skip"` or a typed, side-effect-free `dryRun`
83
+ handler. Unmarked steps still execute in a dry run. Read-only fetches may
84
+ still contact real systems; use fake I/O during verification.
85
+ - Follow skipped values through downstream steps and the finalizer. A skipped
86
+ write may prevent `requireOutputs` from producing a result. Either preserve
87
+ that contract or define an honest preview result; never fabricate a completed
88
+ write to make dry runs pass.
89
+ - Keep validation required for publication. Test this even with
90
+ `continueOnError` if the caller enables best-effort execution.
91
+ - Use `step.skippable` only for a successful policy decision. Preserve thrown
92
+ failures and their causes; branch on structured Tubeless error fields.
93
+ Map pipeline errors at the caller boundary if existing callers require a
94
+ specific error contract.
95
+ - Use `context.log` inside steps. Pass `context.signal` to I/O and retry helpers,
96
+ use `context.sleep` for waits, and report progress for long loops. Preserve
97
+ existing retry scope and idempotency; do not add retries to unsafe mutations.
98
+ - Keep pipeline imports free of execution and external effects. Leave invocation
99
+ in the existing caller or a thin command wrapper. Retain externally used
100
+ function signatures and return shapes unless the user requests changing them.
101
+ - Use `runOrThrow(options, controls?)` for value-or-error callers and `run` for
102
+ explicit report handling. `continueOnError` does not make `runOrThrow` succeed
103
+ after failures. Keep cleanup in a reliable `finally` or resource scope.
104
+
105
+ Respect the project's layout. If it has no convention, use
106
+ `pipelines/<name>.ts` and export `<Name>Pipeline`. Add a
107
+ `definePipelineCommand` wrapper only for a CLI workflow, following the CLI
108
+ recipe; register it in an existing project catalog when appropriate. Keep
109
+ storage, Studio, telemetry exporters, and remote engines optional.
110
+
111
+ ## Prove the conversion
112
+
113
+ Typecheck and run the project's focused tests. Adapt existing tests to compare
114
+ observable behavior, not just graph shape. Cover relevant success, validation
115
+ failure, dependency failure, intentional skip, cancellation, and dry-run paths.
116
+ Use `createPipelineTestRuntime` from `tubeless/testing` with fake I/O, and assert
117
+ that forbidden writes never happen. Avoid executing production work as a test.
118
+
119
+ Inspect `pipeline.plan()` and, for declared goals, target selection. Planning
120
+ does not execute steps or validate domain inputs; it cannot prove runtime
121
+ correctness. Generate a graph with `pipeline.toMermaid()` when it helps explain
122
+ the design. Within the Tubeless source repository, also run `make check`.
123
+
124
+ Finish with the implemented step structure, preserved or intentionally changed
125
+ behavior, validation results, and any unresolved contract decisions. Remove
126
+ superseded orchestration once callers use the pipeline; keep reusable helpers.
@@ -1 +0,0 @@
1
- export declare function requireEnv(varName: string, usedBy: string): string;
package/dist/node/env.js DELETED
@@ -1,8 +0,0 @@
1
- export function requireEnv(varName, usedBy) {
2
- const value = process.env[varName];
3
- if (!value) {
4
- throw new Error(`${varName} is required for ${usedBy}`);
5
- }
6
- return value;
7
- }
8
- //# sourceMappingURL=env.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/node/env.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,MAAc;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,oBAAoB,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["export function requireEnv(varName: string, usedBy: string): string {\n const value = process.env[varName];\n if (!value) {\n throw new Error(`${varName} is required for ${usedBy}`);\n }\n return value;\n}\n"]}
@@ -1,3 +0,0 @@
1
- export declare function writeJson(filePath: string, value: unknown): void;
2
- export declare function readJson<T>(filePath: string): T;
3
- export declare function resetDir(dirPath: string): void;
@@ -1,17 +0,0 @@
1
- import * as fs from "fs";
2
- import { writeAtomicText } from "./atomic-text.js";
3
- export function writeJson(filePath, value) {
4
- writeAtomicText(filePath, `${JSON.stringify(value, null, 2)}\n`);
5
- }
6
- export function readJson(filePath) {
7
- // SAFETY: the caller's type parameter T is the contract for the file's
8
- // contents; the file is expected to hold JSON matching T (files written by
9
- // writeJson round-trip). JSON.parse returns `any`, so the assertion only
10
- // documents the caller's guarantee — it performs no runtime narrowing.
11
- return JSON.parse(fs.readFileSync(filePath, "utf-8"));
12
- }
13
- export function resetDir(dirPath) {
14
- fs.rmSync(dirPath, { recursive: true, force: true });
15
- fs.mkdirSync(dirPath, { recursive: true });
16
- }
17
- //# sourceMappingURL=file-utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../src/node/file-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,KAAc;IACxD,eAAe,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAI,QAAgB;IAC1C,uEAAuE;IACvE,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAM,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import * as fs from \"fs\";\nimport { writeAtomicText } from \"./atomic-text.js\";\n\nexport function writeJson(filePath: string, value: unknown): void {\n writeAtomicText(filePath, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport function readJson<T>(filePath: string): T {\n // SAFETY: the caller's type parameter T is the contract for the file's\n // contents; the file is expected to hold JSON matching T (files written by\n // writeJson round-trip). JSON.parse returns `any`, so the assertion only\n // documents the caller's guarantee — it performs no runtime narrowing.\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\")) as T;\n}\n\nexport function resetDir(dirPath: string): void {\n fs.rmSync(dirPath, { recursive: true, force: true });\n fs.mkdirSync(dirPath, { recursive: true });\n}\n"]}
@@ -1,8 +0,0 @@
1
- /**
2
- * Declares a set of workspace-relative default paths as one factory instead of one
3
- * `function defaultXDir(cwd) { return path.join(cwd, "...") }` per path. The returned
4
- * function re-resolves against `cwd` on every call — nothing is computed or cached at
5
- * module-load time, so a frozen-`process.cwd()` bug cannot be reintroduced by
6
- * construction.
7
- */
8
- export declare function definePaths<T extends Record<string, string>>(relativePaths: T): (cwd: string) => T;
@@ -1,19 +0,0 @@
1
- import * as path from "path";
2
- /**
3
- * Declares a set of workspace-relative default paths as one factory instead of one
4
- * `function defaultXDir(cwd) { return path.join(cwd, "...") }` per path. The returned
5
- * function re-resolves against `cwd` on every call — nothing is computed or cached at
6
- * module-load time, so a frozen-`process.cwd()` bug cannot be reintroduced by
7
- * construction.
8
- */
9
- export function definePaths(relativePaths) {
10
- return (cwd) =>
11
- // SAFETY: Object.entries(relativePaths) yields exactly T's keys, and every
12
- // value is path.join(cwd, relativePath), a string — so the result satisfies
13
- // T's Record<string, string> constraint.
14
- Object.fromEntries(Object.entries(relativePaths).map(([key, relativePath]) => [
15
- key,
16
- path.join(cwd, relativePath),
17
- ]));
18
- }
19
- //# sourceMappingURL=paths.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/node/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,aAAgB;IAEhB,OAAO,CAAC,GAAW,EAAE,EAAE;IACrB,2EAA2E;IAC3E,4EAA4E;IAC5E,yCAAyC;IACzC,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC;QACzD,GAAG;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC;KAC7B,CAAC,CACE,CAAC;AACX,CAAC","sourcesContent":["import * as path from \"path\";\n\n/**\n * Declares a set of workspace-relative default paths as one factory instead of one\n * `function defaultXDir(cwd) { return path.join(cwd, \"...\") }` per path. The returned\n * function re-resolves against `cwd` on every call — nothing is computed or cached at\n * module-load time, so a frozen-`process.cwd()` bug cannot be reintroduced by\n * construction.\n */\nexport function definePaths<T extends Record<string, string>>(\n relativePaths: T\n): (cwd: string) => T {\n return (cwd: string) =>\n // SAFETY: Object.entries(relativePaths) yields exactly T's keys, and every\n // value is path.join(cwd, relativePath), a string — so the result satisfies\n // T's Record<string, string> constraint.\n Object.fromEntries(\n Object.entries(relativePaths).map(([key, relativePath]) => [\n key,\n path.join(cwd, relativePath),\n ])\n ) as T;\n}\n"]}
@@ -1,80 +0,0 @@
1
- /**
2
- * One option in an interactive multi-select list.
3
- * Domain packages supply their own values and labels; this module stays generic.
4
- */
5
- export interface MultiSelectChoice<T extends string = string> {
6
- value: T;
7
- /** Shown in fzf/readline. Defaults to `value`. */
8
- label?: string;
9
- }
10
- /**
11
- * Result of a multi-select prompt.
12
- * - `all`: user chose the explicit all-option (meaning is domain-defined)
13
- * - `values`: one or more concrete choice values, in choice-list order
14
- */
15
- export type MultiSelectResult<T extends string = string> = {
16
- kind: "all";
17
- } | {
18
- kind: "values";
19
- values: readonly T[];
20
- };
21
- export interface PromptMultiSelectOptions<T extends string = string> {
22
- /** Items the user may pick. */
23
- choices: readonly (MultiSelectChoice<T> | T)[];
24
- /**
25
- * When true, show an "all" row that returns `{ kind: "all" }`.
26
- * Domains decide what "all" expands to after the prompt returns.
27
- */
28
- allowAll?: boolean;
29
- /** Label for the all-option row. Defaults to `"All"`. */
30
- allLabel?: string;
31
- /** Value token recognized as all (input parse + fzf). Defaults to `"all"`. */
32
- allValue?: string;
33
- /** Heading printed above the numbered fallback menu. */
34
- title?: string;
35
- /** fzf `--header` text. */
36
- header?: string;
37
- /** fzf `--prompt` and readline question prefix. Defaults to `"> "`. */
38
- prompt?: string;
39
- /** Prefer fzf multi-select when the binary is available. Defaults to true. */
40
- preferFzf?: boolean;
41
- stdin?: NodeJS.ReadableStream;
42
- stdout?: NodeJS.WritableStream;
43
- }
44
- export interface ParseMultiSelectInputOptions {
45
- allowAll?: boolean;
46
- allValue?: string;
47
- }
48
- /** Normalize string or `{ value, label }` choices into a stable list. */
49
- export declare function normalizeMultiSelectChoices<T extends string>(choices: readonly (MultiSelectChoice<T> | T)[]): MultiSelectChoice<T>[];
50
- /**
51
- * Parse a space/comma-separated selection of 1-based indices, choice values,
52
- * or the all-token. Returns null when the input is empty or unparseable.
53
- */
54
- export declare function parseMultiSelectInput<T extends string>(input: string, choices: readonly (MultiSelectChoice<T> | T)[], options?: ParseMultiSelectInputOptions): MultiSelectResult<T> | null;
55
- /**
56
- * Interactively multi-select from a list of choices.
57
- *
58
- * Prefers fzf multi-select when available; falls back to a numbered prompt.
59
- * Returns `null` when the user cancels or the session is non-interactive.
60
- *
61
- * @example
62
- * ```ts
63
- * import { promptMultiSelect } from "tubeless/cli";
64
- *
65
- * const picked = await promptMultiSelect({
66
- * choices: [
67
- * { value: "shard-a", label: "Shard A (east)" },
68
- * { value: "shard-b", label: "Shard B (west)" },
69
- * ],
70
- * allowAll: true,
71
- * allLabel: "All shards",
72
- * title: "Select shards to process",
73
- * prompt: "shards> ",
74
- * });
75
- * if (!picked) throw new Error("Selection cancelled");
76
- * if (picked.kind === "all") runAll();
77
- * else runSelected(picked.values);
78
- * ```
79
- */
80
- export declare function promptMultiSelect<T extends string>(options: PromptMultiSelectOptions<T>): Promise<MultiSelectResult<T> | null>;
@@ -1,197 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import * as readline from "node:readline/promises";
3
- function canUseInteractivePrompt(stdin, stdout) {
4
- const inTty = "isTTY" in stdin && stdin.isTTY === true;
5
- const outTty = "isTTY" in stdout && stdout.isTTY === true;
6
- return Boolean(inTty && outTty);
7
- }
8
- function fzfAvailable() {
9
- const result = spawnSync("fzf", ["--version"], {
10
- encoding: "utf8",
11
- stdio: ["ignore", "pipe", "ignore"],
12
- });
13
- return result.status === 0;
14
- }
15
- function isStringChoice(choice) {
16
- return typeof choice === "string";
17
- }
18
- /** Normalize string or `{ value, label }` choices into a stable list. */
19
- export function normalizeMultiSelectChoices(choices) {
20
- return choices.map((choice) => isStringChoice(choice)
21
- ? { value: choice, label: choice }
22
- : { ...choice, label: choice.label ?? choice.value });
23
- }
24
- function displayLabel(choice) {
25
- return choice.label ?? choice.value;
26
- }
27
- /**
28
- * Parse a space/comma-separated selection of 1-based indices, choice values,
29
- * or the all-token. Returns null when the input is empty or unparseable.
30
- */
31
- export function parseMultiSelectInput(input, choices, options = {}) {
32
- const normalized = normalizeMultiSelectChoices(choices);
33
- const allowAll = options.allowAll ?? false;
34
- const allValue = (options.allValue ?? "all").toLowerCase();
35
- const trimmed = input.trim().toLowerCase();
36
- if (!trimmed)
37
- return null;
38
- if (allowAll && (trimmed === allValue || trimmed === "*")) {
39
- return { kind: "all" };
40
- }
41
- const tokens = trimmed.split(/[\s,]+/).filter(Boolean);
42
- if (tokens.length === 0)
43
- return null;
44
- const valueByLower = new Map(normalized.map((choice) => [choice.value.toLowerCase(), choice.value]));
45
- const selected = new Set();
46
- for (const token of tokens) {
47
- if (allowAll && (token === allValue || token === "*")) {
48
- return { kind: "all" };
49
- }
50
- const asIndex = Number(token);
51
- if (Number.isInteger(asIndex) && asIndex >= 1 && asIndex <= normalized.length) {
52
- selected.add(normalized[asIndex - 1].value);
53
- continue;
54
- }
55
- const matched = valueByLower.get(token);
56
- if (matched !== undefined) {
57
- selected.add(matched);
58
- continue;
59
- }
60
- return null;
61
- }
62
- if (selected.size === 0)
63
- return null;
64
- return {
65
- kind: "values",
66
- values: normalized.map((choice) => choice.value).filter((value) => selected.has(value)),
67
- };
68
- }
69
- function promptWithFzf(choices, options) {
70
- const lines = [
71
- ...(options.allowAll ? [`${options.allValue}\t${options.allLabel}`] : []),
72
- ...choices.map((choice) => `${choice.value}\t${displayLabel(choice)}`),
73
- ];
74
- // Candidate list is piped on stdin; fzf opens /dev/tty for the interactive UI.
75
- const result = spawnSync("fzf", [
76
- "--multi",
77
- "--delimiter=\t",
78
- "--with-nth=2..",
79
- `--header=${options.header}`,
80
- `--prompt=${options.prompt}`,
81
- "--height=40%",
82
- "--reverse",
83
- ], {
84
- input: `${lines.join("\n")}\n`,
85
- encoding: "utf8",
86
- stdio: ["pipe", "pipe", "inherit"],
87
- });
88
- if (result.status !== 0) {
89
- return null;
90
- }
91
- const selectedLines = (result.stdout ?? "")
92
- .split("\n")
93
- .map((line) => line.trim())
94
- .filter(Boolean);
95
- if (selectedLines.length === 0)
96
- return null;
97
- const allToken = options.allValue.toLowerCase();
98
- if (options.allowAll &&
99
- selectedLines.some((line) => {
100
- const key = (line.split("\t")[0] ?? line).toLowerCase();
101
- return key === allToken;
102
- })) {
103
- return { kind: "all" };
104
- }
105
- const selected = new Set();
106
- for (const line of selectedLines) {
107
- const key = line.split("\t")[0] ?? "";
108
- const matched = choices.find((choice) => choice.value === key);
109
- if (matched)
110
- selected.add(matched.value);
111
- }
112
- if (selected.size === 0)
113
- return null;
114
- return {
115
- kind: "values",
116
- values: choices.map((choice) => choice.value).filter((value) => selected.has(value)),
117
- };
118
- }
119
- async function promptWithReadline(choices, options) {
120
- const output = options.stdout;
121
- output.write(`${options.title}:\n`);
122
- if (options.allowAll) {
123
- output.write(` ${options.allValue} ${options.allLabel}\n`);
124
- }
125
- choices.forEach((choice, index) => {
126
- output.write(` ${String(index + 1).padStart(2, " ")}. ${displayLabel(choice)}\n`);
127
- });
128
- const allHint = options.allowAll ? `, or '${options.allValue}'` : "";
129
- output.write(`\nEnter numbers and/or values (space/comma separated)${allHint}. Empty cancels.\n`);
130
- const rl = readline.createInterface({ input: options.stdin, output: options.stdout });
131
- try {
132
- const answer = await rl.question(options.prompt.endsWith(" ") ? options.prompt : `${options.prompt} `);
133
- return parseMultiSelectInput(answer, choices, {
134
- allowAll: options.allowAll,
135
- allValue: options.allValue,
136
- });
137
- }
138
- finally {
139
- rl.close();
140
- }
141
- }
142
- /**
143
- * Interactively multi-select from a list of choices.
144
- *
145
- * Prefers fzf multi-select when available; falls back to a numbered prompt.
146
- * Returns `null` when the user cancels or the session is non-interactive.
147
- *
148
- * @example
149
- * ```ts
150
- * import { promptMultiSelect } from "tubeless/cli";
151
- *
152
- * const picked = await promptMultiSelect({
153
- * choices: [
154
- * { value: "shard-a", label: "Shard A (east)" },
155
- * { value: "shard-b", label: "Shard B (west)" },
156
- * ],
157
- * allowAll: true,
158
- * allLabel: "All shards",
159
- * title: "Select shards to process",
160
- * prompt: "shards> ",
161
- * });
162
- * if (!picked) throw new Error("Selection cancelled");
163
- * if (picked.kind === "all") runAll();
164
- * else runSelected(picked.values);
165
- * ```
166
- */
167
- export async function promptMultiSelect(options) {
168
- const choices = normalizeMultiSelectChoices(options.choices);
169
- if (choices.length === 0) {
170
- throw new Error("promptMultiSelect requires at least one choice");
171
- }
172
- const stdin = options.stdin ?? process.stdin;
173
- const stdout = options.stdout ?? process.stdout;
174
- const preferFzf = options.preferFzf ?? true;
175
- const allowAll = options.allowAll ?? false;
176
- const allValue = options.allValue ?? "all";
177
- const allLabel = options.allLabel ?? "All";
178
- const title = options.title ?? "Select one or more options";
179
- const header = options.header ?? "Select options (TAB multi-select, Enter confirm)";
180
- const prompt = options.prompt ?? "> ";
181
- if (!canUseInteractivePrompt(stdin, stdout)) {
182
- return null;
183
- }
184
- if (preferFzf && fzfAvailable()) {
185
- return promptWithFzf(choices, { allowAll, allLabel, allValue, header, prompt });
186
- }
187
- return promptWithReadline(choices, {
188
- allowAll,
189
- allLabel,
190
- allValue,
191
- title,
192
- prompt,
193
- stdin,
194
- stdout,
195
- });
196
- }
197
- //# sourceMappingURL=prompt-select.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompt-select.js","sourceRoot":"","sources":["../../src/reporter/prompt-select.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport * as readline from \"node:readline/promises\";\n\n/**\n * One option in an interactive multi-select list.\n * Domain packages supply their own values and labels; this module stays generic.\n */\nexport interface MultiSelectChoice<T extends string = string> {\n value: T;\n /** Shown in fzf/readline. Defaults to `value`. */\n label?: string;\n}\n\n/**\n * Result of a multi-select prompt.\n * - `all`: user chose the explicit all-option (meaning is domain-defined)\n * - `values`: one or more concrete choice values, in choice-list order\n */\nexport type MultiSelectResult<T extends string = string> =\n | { kind: \"all\" }\n | { kind: \"values\"; values: readonly T[] };\n\nexport interface PromptMultiSelectOptions<T extends string = string> {\n /** Items the user may pick. */\n choices: readonly (MultiSelectChoice<T> | T)[];\n /**\n * When true, show an \"all\" row that returns `{ kind: \"all\" }`.\n * Domains decide what \"all\" expands to after the prompt returns.\n */\n allowAll?: boolean;\n /** Label for the all-option row. Defaults to `\"All\"`. */\n allLabel?: string;\n /** Value token recognized as all (input parse + fzf). Defaults to `\"all\"`. */\n allValue?: string;\n /** Heading printed above the numbered fallback menu. */\n title?: string;\n /** fzf `--header` text. */\n header?: string;\n /** fzf `--prompt` and readline question prefix. Defaults to `\"> \"`. */\n prompt?: string;\n /** Prefer fzf multi-select when the binary is available. Defaults to true. */\n preferFzf?: boolean;\n stdin?: NodeJS.ReadableStream;\n stdout?: NodeJS.WritableStream;\n}\n\nexport interface ParseMultiSelectInputOptions {\n allowAll?: boolean;\n allValue?: string;\n}\n\nfunction canUseInteractivePrompt(\n stdin: NodeJS.ReadableStream,\n stdout: NodeJS.WritableStream\n): boolean {\n const inTty = \"isTTY\" in stdin && stdin.isTTY === true;\n const outTty = \"isTTY\" in stdout && stdout.isTTY === true;\n return Boolean(inTty && outTty);\n}\n\nfunction fzfAvailable(): boolean {\n const result = spawnSync(\"fzf\", [\"--version\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n return result.status === 0;\n}\n\nfunction isStringChoice<T extends string>(choice: MultiSelectChoice<T> | T): choice is T {\n return typeof choice === \"string\";\n}\n\n/** Normalize string or `{ value, label }` choices into a stable list. */\nexport function normalizeMultiSelectChoices<T extends string>(\n choices: readonly (MultiSelectChoice<T> | T)[]\n): MultiSelectChoice<T>[] {\n return choices.map((choice) =>\n isStringChoice(choice)\n ? { value: choice, label: choice }\n : { ...choice, label: choice.label ?? choice.value }\n );\n}\n\nfunction displayLabel(choice: MultiSelectChoice<string>): string {\n return choice.label ?? choice.value;\n}\n\n/**\n * Parse a space/comma-separated selection of 1-based indices, choice values,\n * or the all-token. Returns null when the input is empty or unparseable.\n */\nexport function parseMultiSelectInput<T extends string>(\n input: string,\n choices: readonly (MultiSelectChoice<T> | T)[],\n options: ParseMultiSelectInputOptions = {}\n): MultiSelectResult<T> | null {\n const normalized = normalizeMultiSelectChoices(choices);\n const allowAll = options.allowAll ?? false;\n const allValue = (options.allValue ?? \"all\").toLowerCase();\n const trimmed = input.trim().toLowerCase();\n if (!trimmed) return null;\n\n if (allowAll && (trimmed === allValue || trimmed === \"*\")) {\n return { kind: \"all\" };\n }\n\n const tokens = trimmed.split(/[\\s,]+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const valueByLower = new Map(\n normalized.map((choice) => [choice.value.toLowerCase(), choice.value])\n );\n const selected = new Set<T>();\n\n for (const token of tokens) {\n if (allowAll && (token === allValue || token === \"*\")) {\n return { kind: \"all\" };\n }\n const asIndex = Number(token);\n if (Number.isInteger(asIndex) && asIndex >= 1 && asIndex <= normalized.length) {\n selected.add(normalized[asIndex - 1]!.value);\n continue;\n }\n const matched = valueByLower.get(token);\n if (matched !== undefined) {\n selected.add(matched);\n continue;\n }\n return null;\n }\n\n if (selected.size === 0) return null;\n return {\n kind: \"values\",\n values: normalized.map((choice) => choice.value).filter((value) => selected.has(value)),\n };\n}\n\nfunction promptWithFzf<T extends string>(\n choices: readonly MultiSelectChoice<T>[],\n options: {\n allowAll: boolean;\n allLabel: string;\n allValue: string;\n header: string;\n prompt: string;\n }\n): MultiSelectResult<T> | null {\n const lines = [\n ...(options.allowAll ? [`${options.allValue}\\t${options.allLabel}`] : []),\n ...choices.map((choice) => `${choice.value}\\t${displayLabel(choice)}`),\n ];\n // Candidate list is piped on stdin; fzf opens /dev/tty for the interactive UI.\n const result = spawnSync(\n \"fzf\",\n [\n \"--multi\",\n \"--delimiter=\\t\",\n \"--with-nth=2..\",\n `--header=${options.header}`,\n `--prompt=${options.prompt}`,\n \"--height=40%\",\n \"--reverse\",\n ],\n {\n input: `${lines.join(\"\\n\")}\\n`,\n encoding: \"utf8\",\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n }\n );\n if (result.status !== 0) {\n return null;\n }\n const selectedLines = (result.stdout ?? \"\")\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n if (selectedLines.length === 0) return null;\n\n const allToken = options.allValue.toLowerCase();\n if (\n options.allowAll &&\n selectedLines.some((line) => {\n const key = (line.split(\"\\t\")[0] ?? line).toLowerCase();\n return key === allToken;\n })\n ) {\n return { kind: \"all\" };\n }\n\n const selected = new Set<T>();\n for (const line of selectedLines) {\n const key = line.split(\"\\t\")[0] ?? \"\";\n const matched = choices.find((choice) => choice.value === key);\n if (matched) selected.add(matched.value);\n }\n if (selected.size === 0) return null;\n return {\n kind: \"values\",\n values: choices.map((choice) => choice.value).filter((value) => selected.has(value)),\n };\n}\n\nasync function promptWithReadline<T extends string>(\n choices: readonly MultiSelectChoice<T>[],\n options: {\n allowAll: boolean;\n allLabel: string;\n allValue: string;\n title: string;\n prompt: string;\n stdin: NodeJS.ReadableStream;\n stdout: NodeJS.WritableStream;\n }\n): Promise<MultiSelectResult<T> | null> {\n const output = options.stdout;\n output.write(`${options.title}:\\n`);\n if (options.allowAll) {\n output.write(` ${options.allValue} ${options.allLabel}\\n`);\n }\n choices.forEach((choice, index) => {\n output.write(` ${String(index + 1).padStart(2, \" \")}. ${displayLabel(choice)}\\n`);\n });\n const allHint = options.allowAll ? `, or '${options.allValue}'` : \"\";\n output.write(`\\nEnter numbers and/or values (space/comma separated)${allHint}. Empty cancels.\\n`);\n\n const rl = readline.createInterface({ input: options.stdin, output: options.stdout });\n try {\n const answer = await rl.question(\n options.prompt.endsWith(\" \") ? options.prompt : `${options.prompt} `\n );\n return parseMultiSelectInput(answer, choices, {\n allowAll: options.allowAll,\n allValue: options.allValue,\n });\n } finally {\n rl.close();\n }\n}\n\n/**\n * Interactively multi-select from a list of choices.\n *\n * Prefers fzf multi-select when available; falls back to a numbered prompt.\n * Returns `null` when the user cancels or the session is non-interactive.\n *\n * @example\n * ```ts\n * import { promptMultiSelect } from \"tubeless/cli\";\n *\n * const picked = await promptMultiSelect({\n * choices: [\n * { value: \"shard-a\", label: \"Shard A (east)\" },\n * { value: \"shard-b\", label: \"Shard B (west)\" },\n * ],\n * allowAll: true,\n * allLabel: \"All shards\",\n * title: \"Select shards to process\",\n * prompt: \"shards> \",\n * });\n * if (!picked) throw new Error(\"Selection cancelled\");\n * if (picked.kind === \"all\") runAll();\n * else runSelected(picked.values);\n * ```\n */\nexport async function promptMultiSelect<T extends string>(\n options: PromptMultiSelectOptions<T>\n): Promise<MultiSelectResult<T> | null> {\n const choices = normalizeMultiSelectChoices(options.choices);\n if (choices.length === 0) {\n throw new Error(\"promptMultiSelect requires at least one choice\");\n }\n\n const stdin = options.stdin ?? process.stdin;\n const stdout = options.stdout ?? process.stdout;\n const preferFzf = options.preferFzf ?? true;\n const allowAll = options.allowAll ?? false;\n const allValue = options.allValue ?? \"all\";\n const allLabel = options.allLabel ?? \"All\";\n const title = options.title ?? \"Select one or more options\";\n const header = options.header ?? \"Select options (TAB multi-select, Enter confirm)\";\n const prompt = options.prompt ?? \"> \";\n\n if (!canUseInteractivePrompt(stdin, stdout)) {\n return null;\n }\n\n if (preferFzf && fzfAvailable()) {\n return promptWithFzf(choices, { allowAll, allLabel, allValue, header, prompt });\n }\n\n return promptWithReadline(choices, {\n allowAll,\n allLabel,\n allValue,\n title,\n prompt,\n stdin,\n stdout,\n });\n}\n"]}
@@ -1 +0,0 @@
1
- export declare const PIPELINE_RUN_STUDIO_CLIENT_SOURCE: string;