tubeless 0.2.4 → 0.2.5

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 (36) hide show
  1. package/README.md +1 -1
  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 +4 -2
  5. package/dist/cli/cli.js +1 -1
  6. package/dist/cli/cli.js.map +1 -1
  7. package/dist/{workbench/workbench-project.js → project/project-manifest.js} +1 -1
  8. package/dist/project/project-manifest.js.map +1 -0
  9. package/dist/project/project.d.ts +1 -0
  10. package/dist/project/project.js +2 -0
  11. package/dist/project/project.js.map +1 -0
  12. package/dist/workbench/workbench-project-loader.d.ts +1 -1
  13. package/dist/workbench/workbench-project-loader.js +1 -1
  14. package/dist/workbench/workbench-project-loader.js.map +1 -1
  15. package/docs/agent-guide.md +5 -1
  16. package/docs/api-reference.md +51 -12
  17. package/docs/api-report.json +49 -4
  18. package/docs/cli.md +45 -0
  19. package/docs/getting-started.md +4 -3
  20. package/docs/llms.txt +1 -1
  21. package/docs/recipes.md +4 -1
  22. package/docs/studio.md +1 -1
  23. package/examples/catalog/scripts/enrich.ts +1 -1
  24. package/examples/catalog/scripts/import.ts +1 -1
  25. package/examples/catalog/scripts/publish.ts +1 -1
  26. package/examples/catalog/tubeless.project.ts +4 -2
  27. package/examples/cli-job.ts +1 -1
  28. package/examples/live-tui.ts +1 -1
  29. package/examples/peloton.ts +1 -1
  30. package/package.json +10 -5
  31. package/skills/tubeless/SKILL.md +4 -1
  32. package/dist/workbench/workbench-entry.d.ts +0 -2
  33. package/dist/workbench/workbench-entry.js +0 -3
  34. package/dist/workbench/workbench-entry.js.map +0 -1
  35. package/dist/workbench/workbench-project.js.map +0 -1
  36. /package/dist/{workbench/workbench-project.d.ts → project/project-manifest.d.ts} +0 -0
package/README.md CHANGED
@@ -74,7 +74,7 @@ report with step statuses, errors, and timings; `plan` to preview execution; or
74
74
 
75
75
  ## Use the CLI
76
76
 
77
- For command-line workflows, register commands in `tubeless.project.ts`.
77
+ Define commands with `tubeless/cli` and catalogs with `tubeless/project`.
78
78
  The [CLI guide](./docs/cli.md) explains how to set up the manifest and commands.
79
79
  With an `import-rows` command registered, you can run:
80
80
 
@@ -135,7 +135,7 @@ export type CliParams<TSchema extends CliParamsSchema> = {
135
135
  export interface CliContext {
136
136
  cwd: string;
137
137
  /** Environment used for `param.env` fallbacks. Defaults to `process.env`. */
138
- env?: NodeJS.ProcessEnv;
138
+ env?: Record<string, string | undefined>;
139
139
  log: PipelineLogger;
140
140
  /** Optional executor-only context used when this command runs a pipeline. */
141
141
  pipelineContext?: Omit<PipelineContext, "cwd" | "log" | "signal">;
@@ -1 +1 @@
1
- {"version":3,"file":"cli-types.js","sourceRoot":"","sources":["../../src/cli/cli-types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAgLpE,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAEhC,MAAM;IACN,QAAQ;IAFnB,YACW,MAAyB,EACzB,QAAgB;QAEzB,KAAK,CAAC,oCAAoC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;sBAHrF,MAAM;wBACN,QAAQ;QAGjB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC7C,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACpB,QAAQ;IAA7B,YAAqB,QAAgB;QACnC,KAAK,CAAC,QAAQ,CAAC,CAAC;wBADG,QAAQ;QAE3B,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,kBAAkB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACvC,CAAC;CACF","sourcesContent":["import type { CheckpointStore } from \"../node/checkpoint.js\";\nimport type { PipelineContext, PipelineLogger } from \"../core/pipeline.js\";\nimport { brandTubelessError } from \"../utilities/tubeless-error.js\";\n\n/**\n * Typed command-line entry points for scripts, mirroring how `definePipeline` turns a\n * declarative step list into a runnable pipeline: declare a flag schema once and get a\n * fully-typed, validated `values` object handed to `run`, instead of each script hand-\n * rolling its own `process.argv` scanning and `as` casts.\n */\n\nexport type CliParamType = \"string\" | \"number\" | \"boolean\" | \"path\";\n\ninterface CliParamBase {\n /** Shown next to the flag in generated `--help` output. */\n description?: string;\n /** UI adapters place execution controls in one section. Built-ins set this. */\n group?: \"execution\";\n /** UI adapters treat exclusive repeatable parameters as mutually exclusive. Built-ins set this on step and target selection. */\n exclusive?: true;\n /** Overrides the derived `--flag-name`; defaults to the kebab-case of the schema key. */\n flag?: string;\n /** Optional one-letter alias, for example `short: \"v\"` enables `-v`. */\n short?: string;\n /**\n * Optional environment-variable fallback. Explicit argv values always win;\n * the environment value is validated like argv but never echoed in errors.\n */\n env?: string;\n}\n\nexport interface CliStringParam extends CliParamBase {\n type: \"string\";\n choices?: readonly string[];\n default?: string;\n optional?: boolean;\n /** Repeatable: `--flag a --flag b` accumulates into `readonly string[]`, `[]` when absent. Cannot combine with `default`. */\n multiple?: boolean;\n}\n\nexport interface CliNumberParam extends CliParamBase {\n type: \"number\";\n default?: number;\n optional?: boolean;\n integer?: boolean;\n min?: number;\n max?: number;\n /** Repeatable: `--flag 1 --flag 2` accumulates into `readonly number[]`, `[]` when absent. Cannot combine with `default`. */\n multiple?: boolean;\n}\n\nexport interface CliBooleanParam extends CliParamBase {\n type: \"boolean\";\n /** Defaults to `false`. A bare `--flag` sets `true`; `--no-flag` sets `false`. */\n default?: boolean;\n}\n\nexport interface CliPathParam extends CliParamBase {\n type: \"path\";\n /** Relative defaults and values are resolved against `context.cwd`. */\n default?: string;\n optional?: boolean;\n /** Validates the resolved path exists before `run` is called. */\n mustExist?: boolean;\n /** Only checked when `mustExist` is true; asserts the resolved path's kind. */\n kind?: \"file\" | \"directory\";\n}\n\nexport type CliParam = CliStringParam | CliNumberParam | CliBooleanParam | CliPathParam;\n\nexport type CliParamsSchema = Record<string, CliParam>;\n\n/** JSON-safe description of one validated command parameter for non-terminal clients. */\nexport interface CliParameterDescriptor {\n readonly choices?: readonly string[];\n readonly default?: string | number | boolean;\n readonly description?: string;\n readonly environment?: string;\n readonly exclusive?: true;\n readonly flag: string;\n readonly group?: \"execution\";\n readonly integer?: boolean;\n readonly key: string;\n readonly max?: number;\n readonly min?: number;\n readonly multiple: boolean;\n readonly mustExist?: boolean;\n readonly pathKind?: \"directory\" | \"file\";\n readonly positional: boolean;\n readonly required: boolean;\n readonly short?: string;\n readonly type: CliParamType;\n}\n\n/** Immutable, presentation-neutral command contract shared by CLI and UI adapters. */\nexport interface CliCommandDescriptor {\n readonly description?: string;\n readonly name: string;\n readonly parameters: readonly CliParameterDescriptor[];\n}\n\ntype CliScalarStringValue<P extends CliParam> = P extends {\n choices: readonly (infer TChoice extends string)[];\n}\n ? TChoice\n : string;\n\ntype CliParamValue<P extends CliParam> = P extends { type: \"string\" }\n ? P extends { multiple: true }\n ? readonly CliScalarStringValue<P>[]\n : CliScalarStringValue<P>\n : P extends { type: \"number\" }\n ? P extends { multiple: true }\n ? readonly number[]\n : number\n : P extends { type: \"boolean\" }\n ? boolean\n : P extends { type: \"path\" }\n ? string\n : never;\n\n/**\n * A field is optional in the parsed output only when it's explicitly marked `optional:\n * true` (booleans never are — an absent boolean flag just means `false`). A field with a\n * `default` is also always present at runtime, but that's already covered: schemas\n * shouldn't combine `default` with `optional: true`, and if one does, the parsed type is\n * merely wider than necessary (`T | undefined` instead of `T`), never wrong.\n */\ntype CliParamIsOptional<P extends CliParam> = P extends { type: \"boolean\" }\n ? false\n : P extends { multiple: true }\n ? false\n : P extends { optional: true }\n ? true\n : false;\n\ntype RequiredCliParams<TSchema extends CliParamsSchema> = {\n [K in keyof TSchema as CliParamIsOptional<TSchema[K]> extends true ? never : K]: CliParamValue<\n TSchema[K]\n >;\n};\n\ntype OptionalCliParams<TSchema extends CliParamsSchema> = {\n [K in keyof TSchema as CliParamIsOptional<TSchema[K]> extends true ? K : never]?: CliParamValue<\n TSchema[K]\n >;\n};\n\n/**\n * Every command's parsed values include `dryRun` and `resume`, whether or not `TSchema`\n * declares them. `resume` is `false` unless `--resume` is passed or `checkpoint.defaultResume`\n * is `true`; it's meaningful even without `checkpoint` configured — a command can implement\n * its own \"is this done\" check and just read `values.resume` directly.\n */\nexport type CliParams<TSchema extends CliParamsSchema> = {\n dryRun: boolean;\n resume: boolean;\n} & RequiredCliParams<TSchema> &\n OptionalCliParams<TSchema>;\n\nexport interface CliContext {\n cwd: string;\n /** Environment used for `param.env` fallbacks. Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv;\n log: PipelineLogger;\n /** Optional executor-only context used when this command runs a pipeline. */\n pipelineContext?: Omit<PipelineContext, \"cwd\" | \"log\" | \"signal\">;\n /** Optional caller-owned cancellation signal forwarded to command work. */\n signal?: AbortSignal;\n /** Present whenever the command's `checkpoint` config is set; see `CliCheckpointConfig`. */\n checkpoint?: CheckpointStore;\n}\n\nexport type CliParseResult<TSchema extends CliParamsSchema> =\n | { kind: \"values\"; values: CliParams<TSchema> }\n | { kind: \"help\"; helpText: string }\n | { kind: \"error\"; errors: readonly string[]; helpText: string };\n\nexport class CliValidationError extends Error {\n constructor(\n readonly errors: readonly string[],\n readonly helpText: string\n ) {\n super(`Invalid command-line arguments:\\n${errors.map((error) => ` - ${error}`).join(\"\\n\")}`);\n this.name = \"CliValidationError\";\n brandTubelessError(this, \"cli-validation\");\n }\n}\n\nexport class CliHelpRequested extends Error {\n constructor(readonly helpText: string) {\n super(helpText);\n this.name = \"CliHelpRequested\";\n brandTubelessError(this, \"cli-help\");\n }\n}\n\nexport interface CliCheckpointConfig {\n /** Relative paths resolve against `context.cwd`, like `CliPathParam`. */\n path: string;\n /**\n * Defaults to `true`: a fully successful, non-dry-run `run` clears the checkpoint —\n * the one-shot resumable-job pattern (start fresh next time). Set `false` for an\n * incrementally-growing \"done\" set (e.g. entity enrichment) that must never auto-clear,\n * since clearing it would cause already-processed items to be reprocessed.\n */\n clearOnSuccess?: boolean;\n /**\n * Defaults to `false`: start fresh unless `--resume` is passed. Set `true` to always\n * resume by default (skip already-recorded items every run); `--no-resume` is the free\n * opt-out, since boolean flags already support `--no-<flag>` negation.\n */\n defaultResume?: boolean;\n}\n\nexport interface CliCommandConfig<TSchema extends CliParamsSchema, TResult> {\n /** Shown in generated usage text; defaults to the running script's file name. */\n name?: string;\n description?: string;\n /** `dryRun`/`--dry-run` and `-h`/`--help` are provided automatically; don't redeclare them. */\n params: TSchema;\n /**\n * Schema keys accepted in positional order. Flags for an earlier positional\n * let the next positional fill the next unset field; a repeatable positional\n * must be last.\n */\n positionals?: readonly (keyof TSchema & string)[];\n /**\n * Opts into a managed checkpoint file. Adds a reserved `--resume`/`--no-resume` flag\n * (also don't redeclare a `resume` param) and populates `context.checkpoint` before\n * `run` is called: resolved fresh (existing file cleared) unless resuming, and cleared\n * again after a successful non-dry-run unless `clearOnSuccess` is `false`.\n */\n checkpoint?: CliCheckpointConfig;\n /** Runs after every field passes its own validation; return error messages to reject. */\n validate?(values: CliParams<TSchema>, context: CliContext): string[] | void;\n run(values: CliParams<TSchema>, context: CliContext): TResult | Promise<TResult>;\n}\n\nexport interface CliCommand<TSchema extends CliParamsSchema, TResult> {\n /** Structured schema metadata for adapters that should not parse generated help text. */\n readonly descriptor: CliCommandDescriptor;\n /** Pure: parses and validates without calling `run` or touching the process. */\n parse(argv?: readonly string[], context?: Partial<CliContext>): CliParseResult<TSchema>;\n /** Validates structured parameter values without tokenizing command-line arguments. */\n parseValues(\n values: Record<string, unknown>,\n context?: Partial<CliContext>\n ): CliParseResult<TSchema>;\n /** Calls `run` from already validated parameter values. */\n execute(values: CliParams<TSchema>, context?: Partial<CliContext>): Promise<TResult>;\n /** Parses and calls `run`; throws `CliValidationError`/`CliHelpRequested` instead of running it. */\n run(argv?: readonly string[], context?: Partial<CliContext>): Promise<TResult>;\n /**\n * Entry point for `if (import.meta.main) void command.main();`. Prints help/errors and\n * sets `process.exitCode` — never calls `process.exit`, so buffered output still flushes\n * — instead of throwing.\n */\n main(argv?: readonly string[], context?: Partial<CliContext>): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"cli-types.js","sourceRoot":"","sources":["../../src/cli/cli-types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAgLpE,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAEhC,MAAM;IACN,QAAQ;IAFnB,YACW,MAAyB,EACzB,QAAgB;QAEzB,KAAK,CAAC,oCAAoC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;sBAHrF,MAAM;wBACN,QAAQ;QAGjB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC7C,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACpB,QAAQ;IAA7B,YAAqB,QAAgB;QACnC,KAAK,CAAC,QAAQ,CAAC,CAAC;wBADG,QAAQ;QAE3B,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,kBAAkB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACvC,CAAC;CACF","sourcesContent":["import type { CheckpointStore } from \"../node/checkpoint.js\";\nimport type { PipelineContext, PipelineLogger } from \"../core/pipeline.js\";\nimport { brandTubelessError } from \"../utilities/tubeless-error.js\";\n\n/**\n * Typed command-line entry points for scripts, mirroring how `definePipeline` turns a\n * declarative step list into a runnable pipeline: declare a flag schema once and get a\n * fully-typed, validated `values` object handed to `run`, instead of each script hand-\n * rolling its own `process.argv` scanning and `as` casts.\n */\n\nexport type CliParamType = \"string\" | \"number\" | \"boolean\" | \"path\";\n\ninterface CliParamBase {\n /** Shown next to the flag in generated `--help` output. */\n description?: string;\n /** UI adapters place execution controls in one section. Built-ins set this. */\n group?: \"execution\";\n /** UI adapters treat exclusive repeatable parameters as mutually exclusive. Built-ins set this on step and target selection. */\n exclusive?: true;\n /** Overrides the derived `--flag-name`; defaults to the kebab-case of the schema key. */\n flag?: string;\n /** Optional one-letter alias, for example `short: \"v\"` enables `-v`. */\n short?: string;\n /**\n * Optional environment-variable fallback. Explicit argv values always win;\n * the environment value is validated like argv but never echoed in errors.\n */\n env?: string;\n}\n\nexport interface CliStringParam extends CliParamBase {\n type: \"string\";\n choices?: readonly string[];\n default?: string;\n optional?: boolean;\n /** Repeatable: `--flag a --flag b` accumulates into `readonly string[]`, `[]` when absent. Cannot combine with `default`. */\n multiple?: boolean;\n}\n\nexport interface CliNumberParam extends CliParamBase {\n type: \"number\";\n default?: number;\n optional?: boolean;\n integer?: boolean;\n min?: number;\n max?: number;\n /** Repeatable: `--flag 1 --flag 2` accumulates into `readonly number[]`, `[]` when absent. Cannot combine with `default`. */\n multiple?: boolean;\n}\n\nexport interface CliBooleanParam extends CliParamBase {\n type: \"boolean\";\n /** Defaults to `false`. A bare `--flag` sets `true`; `--no-flag` sets `false`. */\n default?: boolean;\n}\n\nexport interface CliPathParam extends CliParamBase {\n type: \"path\";\n /** Relative defaults and values are resolved against `context.cwd`. */\n default?: string;\n optional?: boolean;\n /** Validates the resolved path exists before `run` is called. */\n mustExist?: boolean;\n /** Only checked when `mustExist` is true; asserts the resolved path's kind. */\n kind?: \"file\" | \"directory\";\n}\n\nexport type CliParam = CliStringParam | CliNumberParam | CliBooleanParam | CliPathParam;\n\nexport type CliParamsSchema = Record<string, CliParam>;\n\n/** JSON-safe description of one validated command parameter for non-terminal clients. */\nexport interface CliParameterDescriptor {\n readonly choices?: readonly string[];\n readonly default?: string | number | boolean;\n readonly description?: string;\n readonly environment?: string;\n readonly exclusive?: true;\n readonly flag: string;\n readonly group?: \"execution\";\n readonly integer?: boolean;\n readonly key: string;\n readonly max?: number;\n readonly min?: number;\n readonly multiple: boolean;\n readonly mustExist?: boolean;\n readonly pathKind?: \"directory\" | \"file\";\n readonly positional: boolean;\n readonly required: boolean;\n readonly short?: string;\n readonly type: CliParamType;\n}\n\n/** Immutable, presentation-neutral command contract shared by CLI and UI adapters. */\nexport interface CliCommandDescriptor {\n readonly description?: string;\n readonly name: string;\n readonly parameters: readonly CliParameterDescriptor[];\n}\n\ntype CliScalarStringValue<P extends CliParam> = P extends {\n choices: readonly (infer TChoice extends string)[];\n}\n ? TChoice\n : string;\n\ntype CliParamValue<P extends CliParam> = P extends { type: \"string\" }\n ? P extends { multiple: true }\n ? readonly CliScalarStringValue<P>[]\n : CliScalarStringValue<P>\n : P extends { type: \"number\" }\n ? P extends { multiple: true }\n ? readonly number[]\n : number\n : P extends { type: \"boolean\" }\n ? boolean\n : P extends { type: \"path\" }\n ? string\n : never;\n\n/**\n * A field is optional in the parsed output only when it's explicitly marked `optional:\n * true` (booleans never are — an absent boolean flag just means `false`). A field with a\n * `default` is also always present at runtime, but that's already covered: schemas\n * shouldn't combine `default` with `optional: true`, and if one does, the parsed type is\n * merely wider than necessary (`T | undefined` instead of `T`), never wrong.\n */\ntype CliParamIsOptional<P extends CliParam> = P extends { type: \"boolean\" }\n ? false\n : P extends { multiple: true }\n ? false\n : P extends { optional: true }\n ? true\n : false;\n\ntype RequiredCliParams<TSchema extends CliParamsSchema> = {\n [K in keyof TSchema as CliParamIsOptional<TSchema[K]> extends true ? never : K]: CliParamValue<\n TSchema[K]\n >;\n};\n\ntype OptionalCliParams<TSchema extends CliParamsSchema> = {\n [K in keyof TSchema as CliParamIsOptional<TSchema[K]> extends true ? K : never]?: CliParamValue<\n TSchema[K]\n >;\n};\n\n/**\n * Every command's parsed values include `dryRun` and `resume`, whether or not `TSchema`\n * declares them. `resume` is `false` unless `--resume` is passed or `checkpoint.defaultResume`\n * is `true`; it's meaningful even without `checkpoint` configured — a command can implement\n * its own \"is this done\" check and just read `values.resume` directly.\n */\nexport type CliParams<TSchema extends CliParamsSchema> = {\n dryRun: boolean;\n resume: boolean;\n} & RequiredCliParams<TSchema> &\n OptionalCliParams<TSchema>;\n\nexport interface CliContext {\n cwd: string;\n /** Environment used for `param.env` fallbacks. Defaults to `process.env`. */\n env?: Record<string, string | undefined>;\n log: PipelineLogger;\n /** Optional executor-only context used when this command runs a pipeline. */\n pipelineContext?: Omit<PipelineContext, \"cwd\" | \"log\" | \"signal\">;\n /** Optional caller-owned cancellation signal forwarded to command work. */\n signal?: AbortSignal;\n /** Present whenever the command's `checkpoint` config is set; see `CliCheckpointConfig`. */\n checkpoint?: CheckpointStore;\n}\n\nexport type CliParseResult<TSchema extends CliParamsSchema> =\n | { kind: \"values\"; values: CliParams<TSchema> }\n | { kind: \"help\"; helpText: string }\n | { kind: \"error\"; errors: readonly string[]; helpText: string };\n\nexport class CliValidationError extends Error {\n constructor(\n readonly errors: readonly string[],\n readonly helpText: string\n ) {\n super(`Invalid command-line arguments:\\n${errors.map((error) => ` - ${error}`).join(\"\\n\")}`);\n this.name = \"CliValidationError\";\n brandTubelessError(this, \"cli-validation\");\n }\n}\n\nexport class CliHelpRequested extends Error {\n constructor(readonly helpText: string) {\n super(helpText);\n this.name = \"CliHelpRequested\";\n brandTubelessError(this, \"cli-help\");\n }\n}\n\nexport interface CliCheckpointConfig {\n /** Relative paths resolve against `context.cwd`, like `CliPathParam`. */\n path: string;\n /**\n * Defaults to `true`: a fully successful, non-dry-run `run` clears the checkpoint —\n * the one-shot resumable-job pattern (start fresh next time). Set `false` for an\n * incrementally-growing \"done\" set (e.g. entity enrichment) that must never auto-clear,\n * since clearing it would cause already-processed items to be reprocessed.\n */\n clearOnSuccess?: boolean;\n /**\n * Defaults to `false`: start fresh unless `--resume` is passed. Set `true` to always\n * resume by default (skip already-recorded items every run); `--no-resume` is the free\n * opt-out, since boolean flags already support `--no-<flag>` negation.\n */\n defaultResume?: boolean;\n}\n\nexport interface CliCommandConfig<TSchema extends CliParamsSchema, TResult> {\n /** Shown in generated usage text; defaults to the running script's file name. */\n name?: string;\n description?: string;\n /** `dryRun`/`--dry-run` and `-h`/`--help` are provided automatically; don't redeclare them. */\n params: TSchema;\n /**\n * Schema keys accepted in positional order. Flags for an earlier positional\n * let the next positional fill the next unset field; a repeatable positional\n * must be last.\n */\n positionals?: readonly (keyof TSchema & string)[];\n /**\n * Opts into a managed checkpoint file. Adds a reserved `--resume`/`--no-resume` flag\n * (also don't redeclare a `resume` param) and populates `context.checkpoint` before\n * `run` is called: resolved fresh (existing file cleared) unless resuming, and cleared\n * again after a successful non-dry-run unless `clearOnSuccess` is `false`.\n */\n checkpoint?: CliCheckpointConfig;\n /** Runs after every field passes its own validation; return error messages to reject. */\n validate?(values: CliParams<TSchema>, context: CliContext): string[] | void;\n run(values: CliParams<TSchema>, context: CliContext): TResult | Promise<TResult>;\n}\n\nexport interface CliCommand<TSchema extends CliParamsSchema, TResult> {\n /** Structured schema metadata for adapters that should not parse generated help text. */\n readonly descriptor: CliCommandDescriptor;\n /** Pure: parses and validates without calling `run` or touching the process. */\n parse(argv?: readonly string[], context?: Partial<CliContext>): CliParseResult<TSchema>;\n /** Validates structured parameter values without tokenizing command-line arguments. */\n parseValues(\n values: Record<string, unknown>,\n context?: Partial<CliContext>\n ): CliParseResult<TSchema>;\n /** Calls `run` from already validated parameter values. */\n execute(values: CliParams<TSchema>, context?: Partial<CliContext>): Promise<TResult>;\n /** Parses and calls `run`; throws `CliValidationError`/`CliHelpRequested` instead of running it. */\n run(argv?: readonly string[], context?: Partial<CliContext>): Promise<TResult>;\n /**\n * Entry point for `if (import.meta.main) void command.main();`. Prints help/errors and\n * sets `process.exitCode` — never calls `process.exit`, so buffered output still flushes\n * — instead of throwing.\n */\n main(argv?: readonly string[], context?: Partial<CliContext>): Promise<void>;\n}\n"]}
package/dist/cli/cli.d.ts CHANGED
@@ -1,3 +1,5 @@
1
- export { CliHelpRequested, CliValidationError, type CliCommandDescriptor, type CliContext, type CliParameterDescriptor, } from "./cli-types.js";
1
+ export { CliHelpRequested, CliValidationError, type CliBooleanParam, type CliCheckpointConfig, type CliCommand, type CliCommandConfig, type CliCommandDescriptor, type CliContext, type CliNumberParam, type CliParam, type CliParameterDescriptor, type CliParams, type CliParamsSchema, type CliParamType, type CliParseResult, type CliPathParam, type CliStringParam, } from "./cli-types.js";
2
2
  export { defineCommand } from "./cli-command.js";
3
- export { definePipelineCommand } from "./cli-pipeline-command.js";
3
+ export { definePipelineCommand, type DefinePipelineCommandConfig, type PipelineCliParseResult, type PipelineCliValues, type PipelineCommand, type PipelineCommandHookConfig, type PipelineCommandHookContext, type PipelineCommandHookSets, } from "./cli-pipeline-command.js";
4
+ export type { PipelineReporterConfig, PipelineReporterMode, ReporterOutput, } from "../reporter/interactive-reporter.js";
5
+ export type { ReporterColorMode, ReporterSymbolMode, ReporterTerminalCapabilities, RunReporterConfig, } from "../reporter/reporter.js";
package/dist/cli/cli.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { CliHelpRequested, CliValidationError, } from "./cli-types.js";
2
2
  export { defineCommand } from "./cli-command.js";
3
- export { definePipelineCommand } from "./cli-pipeline-command.js";
3
+ export { definePipelineCommand, } from "./cli-pipeline-command.js";
4
4
  //# sourceMappingURL=cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC","sourcesContent":["export {\n CliHelpRequested,\n CliValidationError,\n type CliCommandDescriptor,\n type CliContext,\n type CliParameterDescriptor,\n} from \"./cli-types.js\";\nexport { defineCommand } from \"./cli-command.js\";\nexport { definePipelineCommand } from \"./cli-pipeline-command.js\";\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GAgBnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EACL,qBAAqB,GAQtB,MAAM,2BAA2B,CAAC","sourcesContent":["export {\n CliHelpRequested,\n CliValidationError,\n type CliBooleanParam,\n type CliCheckpointConfig,\n type CliCommand,\n type CliCommandConfig,\n type CliCommandDescriptor,\n type CliContext,\n type CliNumberParam,\n type CliParam,\n type CliParameterDescriptor,\n type CliParams,\n type CliParamsSchema,\n type CliParamType,\n type CliParseResult,\n type CliPathParam,\n type CliStringParam,\n} from \"./cli-types.js\";\nexport { defineCommand } from \"./cli-command.js\";\nexport {\n definePipelineCommand,\n type DefinePipelineCommandConfig,\n type PipelineCliParseResult,\n type PipelineCliValues,\n type PipelineCommand,\n type PipelineCommandHookConfig,\n type PipelineCommandHookContext,\n type PipelineCommandHookSets,\n} from \"./cli-pipeline-command.js\";\nexport type {\n PipelineReporterConfig,\n PipelineReporterMode,\n ReporterOutput,\n} from \"../reporter/interactive-reporter.js\";\nexport type {\n ReporterColorMode,\n ReporterSymbolMode,\n ReporterTerminalCapabilities,\n RunReporterConfig,\n} from \"../reporter/reporter.js\";\n"]}
@@ -63,4 +63,4 @@ export function isPipelineProjectManifest(value) {
63
63
  const candidate = value;
64
64
  return (candidate.version === PIPELINE_PROJECT_MANIFEST_VERSION && Array.isArray(candidate.commands));
65
65
  }
66
- //# sourceMappingURL=workbench-project.js.map
66
+ //# sourceMappingURL=project-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-manifest.js","sourceRoot":"","sources":["../../src/project/project-manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/** Current version of the declarative Tubeless project manifest contract. */\nexport const PIPELINE_PROJECT_MANIFEST_VERSION = 1 as const;\n\nconst PIPELINE_PROJECT_MANIFEST_MARKER = Symbol.for(\"tubeless/pipeline-project-manifest/v1\");\n\nexport interface PipelineProjectCommandModule {\n /** Stable project-wide identity used by workbench commands and the studio. */\n id: string;\n /** Command module path, resolved relative to the project manifest file. */\n file: string;\n /** Select a named definePipelineCommand export when the module has more than one. */\n export?: string;\n /** Optional display-name override. Defaults to the command's generated name. */\n name?: string;\n}\n\nexport interface PipelineProjectManifestInput {\n /** Explicitly registered definePipelineCommand modules. */\n commands: readonly PipelineProjectCommandModule[];\n /** Command execution directory, relative to this file. Defaults to this file's directory. */\n cwd?: string;\n}\n\nexport interface PipelineProjectManifest {\n readonly commands: readonly PipelineProjectCommandModule[];\n readonly cwd?: string;\n readonly version: typeof PIPELINE_PROJECT_MANIFEST_VERSION;\n}\n\ntype MutableProjectManifest = {\n commands: readonly PipelineProjectCommandModule[];\n cwd?: string;\n version: typeof PIPELINE_PROJECT_MANIFEST_VERSION;\n};\n\nfunction requiredText(value: unknown, label: string): asserts value is string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n}\n\n/** Declare a versioned, dependency-free project manifest of workbench commands. */\nexport function definePipelineProject(\n manifest: PipelineProjectManifestInput\n): PipelineProjectManifest {\n if (!Array.isArray(manifest.commands) || manifest.commands.length === 0) {\n throw new Error(\"Pipeline project manifest must declare at least one command.\");\n }\n if (manifest.cwd !== undefined) requiredText(manifest.cwd, \"Pipeline project cwd\");\n\n const ids = new Set<string>();\n const modules = new Set<string>();\n const commands = manifest.commands.map((command, index): PipelineProjectCommandModule => {\n requiredText(command.id, `Pipeline project command ${index + 1} id`);\n requiredText(command.file, `Pipeline project command ${index + 1} file`);\n if (command.export !== undefined) {\n requiredText(command.export, `Pipeline project command ${index + 1} export`);\n }\n if (command.name !== undefined) {\n requiredText(command.name, `Pipeline project command ${index + 1} name`);\n }\n if (ids.has(command.id)) {\n throw new Error(\n `Pipeline project command id ${JSON.stringify(command.id)} is declared more than once.`\n );\n }\n ids.add(command.id);\n const moduleIdentity = `${command.file}\\0${command.export ?? \"\"}`;\n if (modules.has(moduleIdentity)) {\n throw new Error(\n `Pipeline project command ${JSON.stringify(command.file)}${command.export ? ` export ${JSON.stringify(command.export)}` : \"\"} is declared more than once.`\n );\n }\n modules.add(moduleIdentity);\n\n const frozenCommand: PipelineProjectCommandModule = {\n file: command.file,\n id: command.id,\n };\n if (command.export !== undefined) frozenCommand.export = command.export;\n if (command.name !== undefined) frozenCommand.name = command.name;\n return Object.freeze(frozenCommand);\n });\n\n const defined: MutableProjectManifest = {\n commands: Object.freeze(commands),\n version: PIPELINE_PROJECT_MANIFEST_VERSION,\n };\n if (manifest.cwd !== undefined) defined.cwd = manifest.cwd;\n Object.defineProperty(defined, PIPELINE_PROJECT_MANIFEST_MARKER, { value: true });\n return Object.freeze(defined);\n}\n\n/** Runtime guard used by the workbench when selecting an exported project manifest. */\nexport function isPipelineProjectManifest(value: unknown): value is PipelineProjectManifest {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(PIPELINE_PROJECT_MANIFEST_MARKER in value)) return false;\n // SAFETY: The marker is only installed by definePipelineProject, which sets version and\n // commands to their declared types, so reading them via a structural view is safe.\n const candidate = value as { version?: unknown; commands?: unknown };\n return (\n candidate.version === PIPELINE_PROJECT_MANIFEST_VERSION && Array.isArray(candidate.commands)\n );\n}\n"]}
@@ -0,0 +1 @@
1
+ export { definePipelineProject, type PipelineProjectCommandModule, type PipelineProjectManifest, type PipelineProjectManifestInput, } from "./project-manifest.js";
@@ -0,0 +1,2 @@
1
+ export { definePipelineProject, } from "./project-manifest.js";
2
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/project/project.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAItB,MAAM,uBAAuB,CAAC","sourcesContent":["export {\n definePipelineProject,\n type PipelineProjectCommandModule,\n type PipelineProjectManifest,\n type PipelineProjectManifestInput,\n} from \"./project-manifest.js\";\n"]}
@@ -1,5 +1,5 @@
1
1
  import { type WorkbenchPipeline, type WorkbenchPipelineCommand } from "./pipeline-module.js";
2
- import { type PipelineProjectManifest } from "./workbench-project.js";
2
+ import { type PipelineProjectManifest } from "../project/project-manifest.js";
3
3
  import { type WorkbenchCliIo } from "./workbench-shared.js";
4
4
  /** Default project manifest checked in the invocation directory. No parent search is performed. */
5
5
  export declare const DEFAULT_PIPELINE_PROJECT_MANIFEST = "tubeless.project.ts";
@@ -1,7 +1,7 @@
1
1
  import { stat } from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { importModuleNamespace, selectUniqueExport, } from "./pipeline-module.js";
4
- import { isPipelineProjectManifest, } from "./workbench-project.js";
4
+ import { isPipelineProjectManifest, } from "../project/project-manifest.js";
5
5
  import { errorMessage, loadPipelineCommand, loadPlanSource, TUBELESS_WORKBENCH_EXIT_CODE, writeUsageError, } from "./workbench-shared.js";
6
6
  /** Default project manifest checked in the invocation directory. No parent search is performed. */
7
7
  export const DEFAULT_PIPELINE_PROJECT_MANIFEST = "tubeless.project.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"workbench-project-loader.js","sourceRoot":"","sources":["../../src/workbench/workbench-project-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport {\n importModuleNamespace,\n selectUniqueExport,\n type WorkbenchPipeline,\n type WorkbenchPipelineCommand,\n} from \"./pipeline-module.js\";\nimport {\n isPipelineProjectManifest,\n type PipelineProjectCommandModule,\n type PipelineProjectManifest,\n} from \"./workbench-project.js\";\nimport {\n errorMessage,\n loadPipelineCommand,\n loadPlanSource,\n TUBELESS_WORKBENCH_EXIT_CODE,\n writeUsageError,\n type WorkbenchCliIo,\n} from \"./workbench-shared.js\";\n\n/** Default project manifest checked in the invocation directory. No parent search is performed. */\nexport const DEFAULT_PIPELINE_PROJECT_MANIFEST = \"tubeless.project.ts\";\n\nexport interface LoadedPipelineProjectManifest {\n filePath: string;\n manifest: PipelineProjectManifest;\n}\n\nexport interface ResolvedPipelineProjectCommand {\n cwd: string;\n exportName?: string;\n filePath: string;\n id: string;\n name?: string;\n}\n\ntype LoadFailure = { exitCode: number };\n\nconst EXPORT_PROJECT_MUTEX_ERROR =\n \"--export cannot be combined with --project; the manifest owns export selection.\";\n\ninterface ResolvedTargetLoad {\n exportName: string | undefined;\n fileArgument: string;\n loadIo: WorkbenchCliIo;\n registration?: ResolvedPipelineProjectCommand;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Load one explicitly named project manifest without searching parent directories. */\nexport async function loadPipelineProjectManifest(\n fileArgument: string,\n io: WorkbenchCliIo\n): Promise<LoadedPipelineProjectManifest | LoadFailure> {\n const filePath = path.resolve(io.cwd, fileArgument);\n try {\n const fileStat = await stat(filePath);\n if (!fileStat.isFile()) throw new Error(`${filePath} is not a file.`);\n const manifest = selectUniqueExport(\n await importModuleNamespace(filePath),\n undefined,\n isPipelineProjectManifest,\n \"project manifest\",\n { hintExport: false }\n ).value;\n const moduleIdentities = new Set<string>();\n for (const command of manifest.commands) {\n const moduleIdentity = `${path.resolve(path.dirname(filePath), command.file)}\\0${command.export ?? \"\"}`;\n if (moduleIdentities.has(moduleIdentity)) {\n throw new Error(\n `Project manifest command ${JSON.stringify(command.file)}${command.export ? ` export ${JSON.stringify(command.export)}` : \"\"} resolves to a duplicate module registration.`\n );\n }\n moduleIdentities.add(moduleIdentity);\n }\n return { filePath, manifest };\n } catch (error) {\n io.stderr.write(`Error: ${errorMessage(error)}\\n`);\n return { exitCode: TUBELESS_WORKBENCH_EXIT_CODE.load };\n }\n}\n\n/** Resolve one stable command id from a loaded manifest. */\nfunction resolvePipelineProjectCommand(\n loaded: LoadedPipelineProjectManifest,\n id: string,\n io: WorkbenchCliIo\n): ResolvedPipelineProjectCommand | LoadFailure {\n const command = loaded.manifest.commands.find((candidate) => candidate.id === id);\n if (!command) {\n io.stderr.write(\n `Error: Project manifest ${loaded.filePath} does not register command ${JSON.stringify(id)}.\\n`\n );\n return { exitCode: TUBELESS_WORKBENCH_EXIT_CODE.load };\n }\n return resolveProjectCommandModule(loaded, command);\n}\n\nfunction resolveProjectCommandModule(\n loaded: LoadedPipelineProjectManifest,\n command: PipelineProjectCommandModule\n): ResolvedPipelineProjectCommand {\n const manifestDirectory = path.dirname(loaded.filePath);\n const resolved: ResolvedPipelineProjectCommand = {\n cwd: path.resolve(manifestDirectory, loaded.manifest.cwd ?? \".\"),\n filePath: path.resolve(manifestDirectory, command.file),\n id: command.id,\n };\n if (command.export !== undefined) resolved.exportName = command.export;\n if (command.name !== undefined) resolved.name = command.name;\n return resolved;\n}\n\nasync function resolveRegisteredTarget(\n target: string,\n projectFile: string,\n io: WorkbenchCliIo\n): Promise<ResolvedPipelineProjectCommand | LoadFailure> {\n const loaded = await loadPipelineProjectManifest(projectFile, io);\n if (\"exitCode\" in loaded) return loaded;\n return resolvePipelineProjectCommand(loaded, target, io);\n}\n\nfunction isPathLike(target: string): boolean {\n return (\n path.isAbsolute(target) ||\n target.startsWith(\".\") ||\n target.includes(\"/\") ||\n path.extname(target).length > 0\n );\n}\n\nasync function projectFileForTarget(\n target: string,\n explicitProjectFile: string | undefined,\n io: WorkbenchCliIo\n): Promise<string | undefined> {\n if (explicitProjectFile !== undefined) return explicitProjectFile;\n if (isPathLike(target) || (await pathExists(path.resolve(io.cwd, target)))) return undefined;\n const defaultFile = path.resolve(io.cwd, DEFAULT_PIPELINE_PROJECT_MANIFEST);\n return (await pathExists(defaultFile)) ? defaultFile : undefined;\n}\n\nasync function resolveTargetLoad(\n target: string,\n exportName: string | undefined,\n projectFile: string | undefined,\n io: WorkbenchCliIo,\n usage: string\n): Promise<ResolvedTargetLoad | LoadFailure> {\n if (exportName !== undefined && projectFile !== undefined) {\n return { exitCode: writeUsageError(io, EXPORT_PROJECT_MUTEX_ERROR, usage) };\n }\n\n const resolvedProjectFile = await projectFileForTarget(target, projectFile, io);\n if (resolvedProjectFile === undefined) {\n return { exportName, fileArgument: target, loadIo: io };\n }\n\n const registration = await resolveRegisteredTarget(target, resolvedProjectFile, io);\n if (\"exitCode\" in registration) return registration;\n return {\n exportName: registration.exportName,\n fileArgument: registration.filePath,\n loadIo: { ...io, cwd: registration.cwd },\n registration,\n };\n}\n\nexport async function loadPipelineCommandTarget(\n target: string,\n exportName: string | undefined,\n projectFile: string | undefined,\n io: WorkbenchCliIo,\n usage: string\n): Promise<\n | {\n command: WorkbenchPipelineCommand;\n commandIo: WorkbenchCliIo;\n exportName: string;\n registration?: ResolvedPipelineProjectCommand;\n }\n | LoadFailure\n> {\n const resolved = await resolveTargetLoad(target, exportName, projectFile, io, usage);\n if (\"exitCode\" in resolved) return resolved;\n const loaded = await loadPipelineCommand(\n resolved.fileArgument,\n resolved.exportName,\n resolved.loadIo\n );\n if (\"exitCode\" in loaded) return loaded;\n return { ...loaded, commandIo: resolved.loadIo, registration: resolved.registration };\n}\n\nexport async function loadPlanSourceTarget(\n target: string,\n exportName: string | undefined,\n projectFile: string | undefined,\n io: WorkbenchCliIo,\n usage: string\n): Promise<\n | {\n registration?: ResolvedPipelineProjectCommand;\n view: WorkbenchPipeline;\n }\n | LoadFailure\n> {\n const resolved = await resolveTargetLoad(target, exportName, projectFile, io, usage);\n if (\"exitCode\" in resolved) return resolved;\n const loaded = await loadPlanSource(resolved.fileArgument, resolved.exportName, resolved.loadIo);\n if (\"exitCode\" in loaded) return loaded;\n return {\n view: loaded.source.kind === \"command\" ? loaded.source.command : loaded.source.pipeline,\n registration: resolved.registration,\n };\n}\n"]}
1
+ {"version":3,"file":"workbench-project-loader.js","sourceRoot":"","sources":["../../src/workbench/workbench-project-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport {\n importModuleNamespace,\n selectUniqueExport,\n type WorkbenchPipeline,\n type WorkbenchPipelineCommand,\n} from \"./pipeline-module.js\";\nimport {\n isPipelineProjectManifest,\n type PipelineProjectCommandModule,\n type PipelineProjectManifest,\n} from \"../project/project-manifest.js\";\nimport {\n errorMessage,\n loadPipelineCommand,\n loadPlanSource,\n TUBELESS_WORKBENCH_EXIT_CODE,\n writeUsageError,\n type WorkbenchCliIo,\n} from \"./workbench-shared.js\";\n\n/** Default project manifest checked in the invocation directory. No parent search is performed. */\nexport const DEFAULT_PIPELINE_PROJECT_MANIFEST = \"tubeless.project.ts\";\n\nexport interface LoadedPipelineProjectManifest {\n filePath: string;\n manifest: PipelineProjectManifest;\n}\n\nexport interface ResolvedPipelineProjectCommand {\n cwd: string;\n exportName?: string;\n filePath: string;\n id: string;\n name?: string;\n}\n\ntype LoadFailure = { exitCode: number };\n\nconst EXPORT_PROJECT_MUTEX_ERROR =\n \"--export cannot be combined with --project; the manifest owns export selection.\";\n\ninterface ResolvedTargetLoad {\n exportName: string | undefined;\n fileArgument: string;\n loadIo: WorkbenchCliIo;\n registration?: ResolvedPipelineProjectCommand;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Load one explicitly named project manifest without searching parent directories. */\nexport async function loadPipelineProjectManifest(\n fileArgument: string,\n io: WorkbenchCliIo\n): Promise<LoadedPipelineProjectManifest | LoadFailure> {\n const filePath = path.resolve(io.cwd, fileArgument);\n try {\n const fileStat = await stat(filePath);\n if (!fileStat.isFile()) throw new Error(`${filePath} is not a file.`);\n const manifest = selectUniqueExport(\n await importModuleNamespace(filePath),\n undefined,\n isPipelineProjectManifest,\n \"project manifest\",\n { hintExport: false }\n ).value;\n const moduleIdentities = new Set<string>();\n for (const command of manifest.commands) {\n const moduleIdentity = `${path.resolve(path.dirname(filePath), command.file)}\\0${command.export ?? \"\"}`;\n if (moduleIdentities.has(moduleIdentity)) {\n throw new Error(\n `Project manifest command ${JSON.stringify(command.file)}${command.export ? ` export ${JSON.stringify(command.export)}` : \"\"} resolves to a duplicate module registration.`\n );\n }\n moduleIdentities.add(moduleIdentity);\n }\n return { filePath, manifest };\n } catch (error) {\n io.stderr.write(`Error: ${errorMessage(error)}\\n`);\n return { exitCode: TUBELESS_WORKBENCH_EXIT_CODE.load };\n }\n}\n\n/** Resolve one stable command id from a loaded manifest. */\nfunction resolvePipelineProjectCommand(\n loaded: LoadedPipelineProjectManifest,\n id: string,\n io: WorkbenchCliIo\n): ResolvedPipelineProjectCommand | LoadFailure {\n const command = loaded.manifest.commands.find((candidate) => candidate.id === id);\n if (!command) {\n io.stderr.write(\n `Error: Project manifest ${loaded.filePath} does not register command ${JSON.stringify(id)}.\\n`\n );\n return { exitCode: TUBELESS_WORKBENCH_EXIT_CODE.load };\n }\n return resolveProjectCommandModule(loaded, command);\n}\n\nfunction resolveProjectCommandModule(\n loaded: LoadedPipelineProjectManifest,\n command: PipelineProjectCommandModule\n): ResolvedPipelineProjectCommand {\n const manifestDirectory = path.dirname(loaded.filePath);\n const resolved: ResolvedPipelineProjectCommand = {\n cwd: path.resolve(manifestDirectory, loaded.manifest.cwd ?? \".\"),\n filePath: path.resolve(manifestDirectory, command.file),\n id: command.id,\n };\n if (command.export !== undefined) resolved.exportName = command.export;\n if (command.name !== undefined) resolved.name = command.name;\n return resolved;\n}\n\nasync function resolveRegisteredTarget(\n target: string,\n projectFile: string,\n io: WorkbenchCliIo\n): Promise<ResolvedPipelineProjectCommand | LoadFailure> {\n const loaded = await loadPipelineProjectManifest(projectFile, io);\n if (\"exitCode\" in loaded) return loaded;\n return resolvePipelineProjectCommand(loaded, target, io);\n}\n\nfunction isPathLike(target: string): boolean {\n return (\n path.isAbsolute(target) ||\n target.startsWith(\".\") ||\n target.includes(\"/\") ||\n path.extname(target).length > 0\n );\n}\n\nasync function projectFileForTarget(\n target: string,\n explicitProjectFile: string | undefined,\n io: WorkbenchCliIo\n): Promise<string | undefined> {\n if (explicitProjectFile !== undefined) return explicitProjectFile;\n if (isPathLike(target) || (await pathExists(path.resolve(io.cwd, target)))) return undefined;\n const defaultFile = path.resolve(io.cwd, DEFAULT_PIPELINE_PROJECT_MANIFEST);\n return (await pathExists(defaultFile)) ? defaultFile : undefined;\n}\n\nasync function resolveTargetLoad(\n target: string,\n exportName: string | undefined,\n projectFile: string | undefined,\n io: WorkbenchCliIo,\n usage: string\n): Promise<ResolvedTargetLoad | LoadFailure> {\n if (exportName !== undefined && projectFile !== undefined) {\n return { exitCode: writeUsageError(io, EXPORT_PROJECT_MUTEX_ERROR, usage) };\n }\n\n const resolvedProjectFile = await projectFileForTarget(target, projectFile, io);\n if (resolvedProjectFile === undefined) {\n return { exportName, fileArgument: target, loadIo: io };\n }\n\n const registration = await resolveRegisteredTarget(target, resolvedProjectFile, io);\n if (\"exitCode\" in registration) return registration;\n return {\n exportName: registration.exportName,\n fileArgument: registration.filePath,\n loadIo: { ...io, cwd: registration.cwd },\n registration,\n };\n}\n\nexport async function loadPipelineCommandTarget(\n target: string,\n exportName: string | undefined,\n projectFile: string | undefined,\n io: WorkbenchCliIo,\n usage: string\n): Promise<\n | {\n command: WorkbenchPipelineCommand;\n commandIo: WorkbenchCliIo;\n exportName: string;\n registration?: ResolvedPipelineProjectCommand;\n }\n | LoadFailure\n> {\n const resolved = await resolveTargetLoad(target, exportName, projectFile, io, usage);\n if (\"exitCode\" in resolved) return resolved;\n const loaded = await loadPipelineCommand(\n resolved.fileArgument,\n resolved.exportName,\n resolved.loadIo\n );\n if (\"exitCode\" in loaded) return loaded;\n return { ...loaded, commandIo: resolved.loadIo, registration: resolved.registration };\n}\n\nexport async function loadPlanSourceTarget(\n target: string,\n exportName: string | undefined,\n projectFile: string | undefined,\n io: WorkbenchCliIo,\n usage: string\n): Promise<\n | {\n registration?: ResolvedPipelineProjectCommand;\n view: WorkbenchPipeline;\n }\n | LoadFailure\n> {\n const resolved = await resolveTargetLoad(target, exportName, projectFile, io, usage);\n if (\"exitCode\" in resolved) return resolved;\n const loaded = await loadPlanSource(resolved.fileArgument, resolved.exportName, resolved.loadIo);\n if (\"exitCode\" in loaded) return loaded;\n return {\n view: loaded.source.kind === \"command\" ? loaded.source.command : loaded.source.pipeline,\n registration: resolved.registration,\n };\n}\n"]}
@@ -71,7 +71,7 @@ dry runs with fake I/O. The general `tubeless` skill supports ongoing authoring.
71
71
  - Use `runConcurrent` for bounded lightweight functions that do not need child
72
72
  lifecycle events. Use `runConcurrentSettled` when the caller needs completed
73
73
  results and the first failure instead of a throw.
74
- - Use `definePipelineCommand` from `tubeless/workbench` for scripts centered on
74
+ - Use `definePipelineCommand` from `tubeless/cli` for scripts centered on
75
75
  a pipeline. Do not parse `process.argv` manually or redeclare built-in dry-run,
76
76
  `--step`, or `--target` flags. `mapOptions`, validation, and hooks receive `stepIds` and `targets`,
77
77
  not `step` or `target`. Omit `mapOptions` when validated flags already satisfy
@@ -81,6 +81,10 @@ dry runs with fake I/O. The general `tubeless` skill supports ongoing authoring.
81
81
  render those parameter definitions instead of parsing help text.
82
82
  Use `command.plan()` or `tubeless plan` for a selection-only preview. Do not
83
83
  simulate planning with `--plan`.
84
+ - Use `defineCommand` from `tubeless/cli` for standalone scripts. Declare command
85
+ catalogs with `definePipelineProject` from `tubeless/project`; the catalog
86
+ works with the CLI before Studio is added. Keep general filesystem/env helpers
87
+ and application-specific prompts in the consumer.
84
88
  - Use `pipeline.toMermaid()` or `command.toMermaid()` when documentation needs
85
89
  the static graph; do not duplicate dependency edges by hand.
86
90
  - Use `tubeless list` for the explicit project command inventory. Use
@@ -6,16 +6,17 @@ Package: `tubeless`
6
6
 
7
7
  ## Public entrypoints
8
8
 
9
- | Entrypoint | Declaration | Surface hash | Exported symbols |
10
- | --------------------- | --------------------------------------- | ------------------------------------------------------------------ | ---------------: |
11
- | `tubeless` | `./dist/core/pipeline.d.ts` | `abb2f1fca6d2e21eda1bc1cf72921fee3110f474660834202d223f805300c8d9` | 65 |
12
- | `tubeless/batch` | `./dist/utilities/batch.d.ts` | `6b7fe7d6eec6532cc835505517c3481e4290a2a606c2fddf6c80367f5c76c5f5` | 7 |
13
- | `tubeless/node` | `./dist/node/node.d.ts` | `1bf8549ca953a11e7202dbda8013db2a8f1d56286f1618bad929efbbb2efd302` | 3 |
14
- | `tubeless/rate-limit` | `./dist/utilities/rate-limit.d.ts` | `5cd093bb780a19e44b087b01e2b38e06f07e7233c9920988ba399036e319c368` | 1 |
15
- | `tubeless/retry` | `./dist/utilities/retry.d.ts` | `f91f83f8e31e3e629ed90d1b04656110572ea970187a92369f0908cd75ae8f00` | 4 |
16
- | `tubeless/workbench` | `./dist/workbench/workbench-entry.d.ts` | `494aa61a6345d9a798f2e1f084a69cc7859439237c6614280a96421eb0f17ffd` | 2 |
17
- | `tubeless/testing` | `./dist/testing/testing.d.ts` | `9510932b8aec600ecf2bcfdb1a188257dad48a3108087c974689c6e6ec3ad5c9` | 7 |
18
- | `tubeless/tracing` | `./dist/tracing/tracing.d.ts` | `c272c955aab09dcd8cc334ffcea3af5cfda420cecc2881a01e3316e3a38801dd` | 3 |
9
+ | Entrypoint | Declaration | Surface hash | Exported symbols |
10
+ | --------------------- | ---------------------------------- | ------------------------------------------------------------------ | ---------------: |
11
+ | `tubeless` | `./dist/core/pipeline.d.ts` | `abb2f1fca6d2e21eda1bc1cf72921fee3110f474660834202d223f805300c8d9` | 65 |
12
+ | `tubeless/cli` | `./dist/cli/cli.d.ts` | `996eeba686bb2a3124c21f30e4c5b5d36873c1a30cbba89ef2b9629206feef48` | 33 |
13
+ | `tubeless/batch` | `./dist/utilities/batch.d.ts` | `6b7fe7d6eec6532cc835505517c3481e4290a2a606c2fddf6c80367f5c76c5f5` | 7 |
14
+ | `tubeless/node` | `./dist/node/node.d.ts` | `1bf8549ca953a11e7202dbda8013db2a8f1d56286f1618bad929efbbb2efd302` | 3 |
15
+ | `tubeless/rate-limit` | `./dist/utilities/rate-limit.d.ts` | `5cd093bb780a19e44b087b01e2b38e06f07e7233c9920988ba399036e319c368` | 1 |
16
+ | `tubeless/retry` | `./dist/utilities/retry.d.ts` | `f91f83f8e31e3e629ed90d1b04656110572ea970187a92369f0908cd75ae8f00` | 4 |
17
+ | `tubeless/project` | `./dist/project/project.d.ts` | `c6a5ebb3f4a6fdfe83af766947ff0be005f370f044b588572ac2357481d0bd19` | 4 |
18
+ | `tubeless/testing` | `./dist/testing/testing.d.ts` | `9510932b8aec600ecf2bcfdb1a188257dad48a3108087c974689c6e6ec3ad5c9` | 7 |
19
+ | `tubeless/tracing` | `./dist/tracing/tracing.d.ts` | `c272c955aab09dcd8cc334ffcea3af5cfda420cecc2881a01e3316e3a38801dd` | 3 |
19
20
 
20
21
  ## Symbols
21
22
 
@@ -87,6 +88,42 @@ Package: `tubeless`
87
88
  - `StepFactory`
88
89
  - `StepSkipDecision`
89
90
 
91
+ ### `tubeless/cli`
92
+
93
+ - `CliBooleanParam`
94
+ - `CliCheckpointConfig`
95
+ - `CliCommand`
96
+ - `CliCommandConfig`
97
+ - `CliCommandDescriptor`
98
+ - `CliContext`
99
+ - `CliHelpRequested`
100
+ - `CliNumberParam`
101
+ - `CliParam`
102
+ - `CliParameterDescriptor`
103
+ - `CliParams`
104
+ - `CliParamsSchema`
105
+ - `CliParamType`
106
+ - `CliParseResult`
107
+ - `CliPathParam`
108
+ - `CliStringParam`
109
+ - `CliValidationError`
110
+ - `defineCommand`
111
+ - `definePipelineCommand`
112
+ - `DefinePipelineCommandConfig`
113
+ - `PipelineCliParseResult`
114
+ - `PipelineCliValues`
115
+ - `PipelineCommand`
116
+ - `PipelineCommandHookConfig`
117
+ - `PipelineCommandHookContext`
118
+ - `PipelineCommandHookSets`
119
+ - `PipelineReporterConfig`
120
+ - `PipelineReporterMode`
121
+ - `ReporterColorMode`
122
+ - `ReporterOutput`
123
+ - `ReporterSymbolMode`
124
+ - `ReporterTerminalCapabilities`
125
+ - `RunReporterConfig`
126
+
90
127
  ### `tubeless/batch`
91
128
 
92
129
  - `chunk`
@@ -114,10 +151,12 @@ Package: `tubeless`
114
151
  - `RetryOptions`
115
152
  - `withRetry`
116
153
 
117
- ### `tubeless/workbench`
154
+ ### `tubeless/project`
118
155
 
119
- - `definePipelineCommand`
120
156
  - `definePipelineProject`
157
+ - `PipelineProjectCommandModule`
158
+ - `PipelineProjectManifest`
159
+ - `PipelineProjectManifestInput`
121
160
 
122
161
  ### `tubeless/testing`
123
162
 
@@ -72,6 +72,46 @@
72
72
  "sha256": "abb2f1fca6d2e21eda1bc1cf72921fee3110f474660834202d223f805300c8d9",
73
73
  "specifier": "tubeless"
74
74
  },
75
+ {
76
+ "declaration": "./dist/cli/cli.d.ts",
77
+ "exports": [
78
+ "CliBooleanParam",
79
+ "CliCheckpointConfig",
80
+ "CliCommand",
81
+ "CliCommandConfig",
82
+ "CliCommandDescriptor",
83
+ "CliContext",
84
+ "CliHelpRequested",
85
+ "CliNumberParam",
86
+ "CliParam",
87
+ "CliParameterDescriptor",
88
+ "CliParams",
89
+ "CliParamsSchema",
90
+ "CliParamType",
91
+ "CliParseResult",
92
+ "CliPathParam",
93
+ "CliStringParam",
94
+ "CliValidationError",
95
+ "defineCommand",
96
+ "definePipelineCommand",
97
+ "DefinePipelineCommandConfig",
98
+ "PipelineCliParseResult",
99
+ "PipelineCliValues",
100
+ "PipelineCommand",
101
+ "PipelineCommandHookConfig",
102
+ "PipelineCommandHookContext",
103
+ "PipelineCommandHookSets",
104
+ "PipelineReporterConfig",
105
+ "PipelineReporterMode",
106
+ "ReporterColorMode",
107
+ "ReporterOutput",
108
+ "ReporterSymbolMode",
109
+ "ReporterTerminalCapabilities",
110
+ "RunReporterConfig"
111
+ ],
112
+ "sha256": "996eeba686bb2a3124c21f30e4c5b5d36873c1a30cbba89ef2b9629206feef48",
113
+ "specifier": "tubeless/cli"
114
+ },
75
115
  {
76
116
  "declaration": "./dist/utilities/batch.d.ts",
77
117
  "exports": [
@@ -105,10 +145,15 @@
105
145
  "specifier": "tubeless/retry"
106
146
  },
107
147
  {
108
- "declaration": "./dist/workbench/workbench-entry.d.ts",
109
- "exports": ["definePipelineCommand", "definePipelineProject"],
110
- "sha256": "494aa61a6345d9a798f2e1f084a69cc7859439237c6614280a96421eb0f17ffd",
111
- "specifier": "tubeless/workbench"
148
+ "declaration": "./dist/project/project.d.ts",
149
+ "exports": [
150
+ "definePipelineProject",
151
+ "PipelineProjectCommandModule",
152
+ "PipelineProjectManifest",
153
+ "PipelineProjectManifestInput"
154
+ ],
155
+ "sha256": "c6a5ebb3f4a6fdfe83af766947ff0be005f370f044b588572ac2357481d0bd19",
156
+ "specifier": "tubeless/project"
112
157
  },
113
158
  {
114
159
  "declaration": "./dist/testing/testing.d.ts",
package/docs/cli.md CHANGED
@@ -17,6 +17,51 @@ can run on Node.js 22 or later without Bun.
17
17
 
18
18
  ## Commands
19
19
 
20
+ ### Authoring API
21
+
22
+ Import terminal commands from `tubeless/cli` and project declarations from
23
+ `tubeless/project`:
24
+
25
+ ```ts
26
+ import { defineCommand, definePipelineCommand } from "tubeless/cli";
27
+ import { definePipelineProject } from "tubeless/project";
28
+ ```
29
+
30
+ Use `defineCommand` for a standalone script and `definePipelineCommand` for a
31
+ pipeline-backed command with built-in selection, dry runs and terminal reporting.
32
+ Both expose typed parsing, validation, descriptors and execution. Their public
33
+ configuration, parameter, result and hook types live on the CLI subpath.
34
+ Import `PipelineReporterConfig` from `tubeless/cli` to share reporter settings
35
+ between commands. Its mode, output stream, color, symbols and terminal-capability
36
+ types are exported there too. CLI declarations do not require `@types/node`;
37
+ `CliContext.env` accepts a record of string or undefined values.
38
+
39
+ Use `definePipelineProject` in `tubeless.project.ts` to register command modules
40
+ with stable IDs. A project catalog is shared by the terminal and Studio; declaring
41
+ one does not load its command modules or start either interface.
42
+
43
+ | Layer | Public entry | Responsibility |
44
+ | ------- | ------------------ | -------------------------------------------------------------------- |
45
+ | Core | `tubeless` | Typed graphs, planning, execution, hooks and run reports |
46
+ | CLI | `tubeless/cli` | Standalone and pipeline commands, typed flags and terminal execution |
47
+ | Project | `tubeless/project` | Project catalogs, command IDs and module registrations |
48
+ | Studio | `tubeless ui` | Optional browser interface for recorded runs and registered commands |
49
+
50
+ Users interact with a project through the `tubeless` executable: `list` shows its
51
+ commands, `plan` previews work, and `run` executes a command. `ui` opens Studio
52
+ against the same catalog. There is no separate workbench application to start.
53
+ Core pipelines and standalone CLI commands do not need a project catalog.
54
+
55
+ Each authoring API has one public entrypoint. Neither the CLI nor the project
56
+ catalog import loads storage or Studio. The executable supplies those optional
57
+ integrations. Storage readers, Studio's server/protocol, terminal renderer
58
+ internals and argument-parser internals remain private.
59
+
60
+ General filesystem/env helpers and application-specific selection prompts belong
61
+ in the application. `tubeless/node` supplies the pipeline checkpoint helpers.
62
+
63
+ ### Executable commands
64
+
20
65
  | Command | Accepts | Does |
21
66
  | ------------------ | ----------------------------------- | --------------------------------------------------------------------- |
22
67
  | `tubeless list` | A project manifest | Lists explicitly registered command IDs without loading their modules |
@@ -14,8 +14,9 @@ npm install tubeless
14
14
  The same package works with `pnpm add tubeless`, `yarn add tubeless`, and
15
15
  `bun add tubeless`. The README quick start is a complete first program.
16
16
 
17
- The optional `tubeless/workbench` entry and bundled CLI load operational tools
18
- only when you use them. The CLI requires Bun 1.3.14 or later:
17
+ Add `tubeless/cli` for terminal commands and `tubeless/project` for project
18
+ catalogs. Neither import loads storage or Studio; the executable supplies those optional tools.
19
+ The executable requires Bun 1.3.14 or later:
19
20
 
20
21
  ```sh
21
22
  bunx tubeless --help
@@ -123,7 +124,7 @@ const normalizePlan = ImportPipeline.plan({ targets: ["normalize"] });
123
124
  failure gates. `stepIds` is an exact low-level filter and cannot be combined
124
125
  with `targets`. See [core concepts](./concepts.md) for examples of both controls.
125
126
 
126
- For a CLI script, import `definePipelineCommand` from `tubeless/workbench` and
127
+ For a CLI script, import `definePipelineCommand` from `tubeless/cli` and
127
128
  wrap the pipeline. It supplies help, target and step selection, dry-run flags,
128
129
  and cancellation handling. See [the CLI](./cli.md) and
129
130
  [`cli-job.ts`](../examples/cli-job.ts).
package/docs/llms.txt CHANGED
@@ -35,7 +35,7 @@ Agent instructions:
35
35
  - Use context.log, context.signal, context.sleep, and progress APIs.
36
36
  - Use createPipelineTestRuntime from tubeless/testing for deterministic tests with captured logs, statuses, progress, clock, and cancellation.
37
37
  - Compile generated agent submissions with bun run eval:agent --; runtime tests, provider invocation, and semantic assessment remain externally controlled.
38
- - Import definePipelineCommand and definePipelineProject from tubeless/workbench.
38
+ - Import definePipelineCommand from tubeless/cli and definePipelineProject from tubeless/project.
39
39
  - Omit mapOptions only when validated CLI flags already satisfy same-name pipeline options; map renamed, loaded, or derived values explicitly. --step and --target are argv flags; mapOptions and hooks receive stepIds and targets.
40
40
  - Declare public goals with targets: [step] on definePipeline, then select their
41
41
  IDs to include required inputs and failure gates. stepIds is an exact filter
package/docs/recipes.md CHANGED
@@ -46,7 +46,10 @@ one of these recipes.
46
46
  throw on the first failure. Use `runConcurrentSettled` when the caller needs
47
47
  completed results plus that failure without throwing.
48
48
 
49
- 6. Use `definePipelineCommand` from `tubeless/workbench` for pipeline scripts.
49
+ 6. Use `definePipelineCommand` from `tubeless/cli` for pipeline scripts.
50
+ Use `defineCommand` from the same entrypoint for standalone scripts and
51
+ `definePipelineProject` from `tubeless/project` for project catalogs shared
52
+ by terminal commands and Studio.
50
53
  Preview selection with
51
54
  `command.plan()` or `tubeless plan`; do not simulate planning with `--plan`.
52
55
  `--step` and `--target` are argv flags; `mapOptions` and hooks read `stepIds`
package/docs/studio.md CHANGED
@@ -82,7 +82,7 @@ Use a project manifest to register commands once for both the CLI and Studio:
82
82
 
83
83
  ```ts
84
84
  // tubeless.project.ts
85
- import { definePipelineProject } from "tubeless/workbench";
85
+ import { definePipelineProject } from "tubeless/project";
86
86
 
87
87
  export default definePipelineProject({
88
88
  cwd: ".",
@@ -1,4 +1,4 @@
1
- import { definePipelineCommand } from "tubeless/workbench";
1
+ import { definePipelineCommand } from "tubeless/cli";
2
2
  import { EnrichPipeline } from "../pipelines/enrich.ts";
3
3
 
4
4
  export const EnrichCommand = definePipelineCommand(EnrichPipeline, {
@@ -1,5 +1,5 @@
1
1
  import * as fs from "node:fs";
2
- import { definePipelineCommand } from "tubeless/workbench";
2
+ import { definePipelineCommand } from "tubeless/cli";
3
3
  import { ImportPipeline } from "../pipelines/import.ts";
4
4
 
5
5
  export const ImportCommand = definePipelineCommand(ImportPipeline, {
@@ -1,5 +1,5 @@
1
1
  import * as fs from "node:fs";
2
- import { definePipelineCommand } from "tubeless/workbench";
2
+ import { definePipelineCommand } from "tubeless/cli";
3
3
  import { PublishPipeline } from "../pipelines/publish.ts";
4
4
 
5
5
  export const PublishCommand = definePipelineCommand(PublishPipeline, {
@@ -1,6 +1,8 @@
1
- import { definePipelineProject } from "tubeless/workbench";
1
+ import { definePipelineProject } from "tubeless/project";
2
2
 
3
- // Register commands here so the CLI, Studio, and coding agents can find them.
3
+ // Project catalog; commands use definePipelineCommand from tubeless/cli.
4
+ // Register commands here so the CLI and coding agents can find them;
5
+ // Studio can consume the same catalog later without changing the pipelines.
4
6
  // Module paths resolve from this file; cwd controls command
5
7
  // execution. Command argv flags stay --step/--target, while mapOptions and hooks
6
8
  // receive stepIds and targets.
@@ -1,6 +1,6 @@
1
1
  import * as fs from "fs";
2
2
  import { createSteps, definePipeline, requireOutputs } from "tubeless";
3
- import { definePipelineCommand } from "tubeless/workbench";
3
+ import { definePipelineCommand } from "tubeless/cli";
4
4
 
5
5
  interface ImportOptions {
6
6
  lines: readonly string[];
@@ -1,5 +1,5 @@
1
1
  import { createSteps, definePipeline } from "tubeless";
2
- import { definePipelineCommand } from "tubeless/workbench";
2
+ import { definePipelineCommand } from "tubeless/cli";
3
3
 
4
4
  interface LiveTuiOptions {
5
5
  delay: number;
@@ -1,6 +1,6 @@
1
1
  import { createSteps, definePipeline } from "tubeless";
2
2
  import { runConcurrent } from "tubeless/batch";
3
- import { definePipelineCommand } from "tubeless/workbench";
3
+ import { definePipelineCommand } from "tubeless/cli";
4
4
  import { RateLimiter } from "tubeless/rate-limit";
5
5
  import { withRetry } from "tubeless/retry";
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tubeless",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Typed pipeline primitives for composable data-processing workflows.",
5
5
  "author": {
6
6
  "name": "Chet Mancini",
@@ -84,6 +84,11 @@
84
84
  "import": "./dist/core/pipeline.js",
85
85
  "default": "./dist/core/pipeline.js"
86
86
  },
87
+ "./cli": {
88
+ "types": "./dist/cli/cli.d.ts",
89
+ "import": "./dist/cli/cli.js",
90
+ "default": "./dist/cli/cli.js"
91
+ },
87
92
  "./batch": {
88
93
  "types": "./dist/utilities/batch.d.ts",
89
94
  "import": "./dist/utilities/batch.js",
@@ -104,10 +109,10 @@
104
109
  "import": "./dist/utilities/retry.js",
105
110
  "default": "./dist/utilities/retry.js"
106
111
  },
107
- "./workbench": {
108
- "types": "./dist/workbench/workbench-entry.d.ts",
109
- "import": "./dist/workbench/workbench-entry.js",
110
- "default": "./dist/workbench/workbench-entry.js"
112
+ "./project": {
113
+ "types": "./dist/project/project.d.ts",
114
+ "import": "./dist/project/project.js",
115
+ "default": "./dist/project/project.js"
111
116
  },
112
117
  "./testing": {
113
118
  "types": "./dist/testing/testing.d.ts",
@@ -57,9 +57,12 @@ Read the corresponding package recipe before using these features:
57
57
  - `createSteps(optionsSchema)`, `outputSchema`, or `resultSchema` for runtime
58
58
  validation at untrusted boundaries. Reuse the project's Standard Schema
59
59
  implementation; core needs no schema dependency.
60
- - `definePipelineCommand` from `tubeless/workbench` for pipeline-backed scripts.
60
+ - `definePipelineCommand` from `tubeless/cli` for pipeline-backed scripts.
61
61
  Built-in `--step` / `--target` flags map to `stepIds` / `targets`. Do not
62
62
  redeclare built-in flags. Read `docs/cli.md` for option mapping.
63
+ Use `defineCommand` from `tubeless/cli` for standalone scripts and
64
+ `definePipelineProject` from `tubeless/project` for project catalogs shared
65
+ by the CLI and optional Studio.
63
66
  - Use `pipelines/<name>.ts` for definitions and `scripts/<name>.ts` for command
64
67
  wrappers when introducing a layout. Register commands explicitly in
65
68
  `tubeless.project.ts`; adapt the package's `examples/catalog/` without copying
@@ -1,2 +0,0 @@
1
- export { definePipelineCommand } from "../cli/cli-pipeline-command.js";
2
- export { definePipelineProject } from "./workbench-project.js";
@@ -1,3 +0,0 @@
1
- export { definePipelineCommand } from "../cli/cli-pipeline-command.js";
2
- export { definePipelineProject } from "./workbench-project.js";
3
- //# sourceMappingURL=workbench-entry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workbench-entry.js","sourceRoot":"","sources":["../../src/workbench/workbench-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC","sourcesContent":["export { definePipelineCommand } from \"../cli/cli-pipeline-command.js\";\nexport { definePipelineProject } from \"./workbench-project.js\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"workbench-project.js","sourceRoot":"","sources":["../../src/workbench/workbench-project.ts"],"names":[],"mappings":"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","sourcesContent":["/** Current version of the declarative Tubeless project manifest contract. */\nexport const PIPELINE_PROJECT_MANIFEST_VERSION = 1 as const;\n\nconst PIPELINE_PROJECT_MANIFEST_MARKER = Symbol.for(\"tubeless/pipeline-project-manifest/v1\");\n\nexport interface PipelineProjectCommandModule {\n /** Stable project-wide identity used by workbench commands and the studio. */\n id: string;\n /** Command module path, resolved relative to the project manifest file. */\n file: string;\n /** Select a named definePipelineCommand export when the module has more than one. */\n export?: string;\n /** Optional display-name override. Defaults to the command's generated name. */\n name?: string;\n}\n\nexport interface PipelineProjectManifestInput {\n /** Explicitly registered definePipelineCommand modules. */\n commands: readonly PipelineProjectCommandModule[];\n /** Command execution directory, relative to this file. Defaults to this file's directory. */\n cwd?: string;\n}\n\nexport interface PipelineProjectManifest {\n readonly commands: readonly PipelineProjectCommandModule[];\n readonly cwd?: string;\n readonly version: typeof PIPELINE_PROJECT_MANIFEST_VERSION;\n}\n\ntype MutableProjectManifest = {\n commands: readonly PipelineProjectCommandModule[];\n cwd?: string;\n version: typeof PIPELINE_PROJECT_MANIFEST_VERSION;\n};\n\nfunction requiredText(value: unknown, label: string): asserts value is string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n}\n\n/** Declare a versioned, dependency-free project manifest of workbench commands. */\nexport function definePipelineProject(\n manifest: PipelineProjectManifestInput\n): PipelineProjectManifest {\n if (!Array.isArray(manifest.commands) || manifest.commands.length === 0) {\n throw new Error(\"Pipeline project manifest must declare at least one command.\");\n }\n if (manifest.cwd !== undefined) requiredText(manifest.cwd, \"Pipeline project cwd\");\n\n const ids = new Set<string>();\n const modules = new Set<string>();\n const commands = manifest.commands.map((command, index): PipelineProjectCommandModule => {\n requiredText(command.id, `Pipeline project command ${index + 1} id`);\n requiredText(command.file, `Pipeline project command ${index + 1} file`);\n if (command.export !== undefined) {\n requiredText(command.export, `Pipeline project command ${index + 1} export`);\n }\n if (command.name !== undefined) {\n requiredText(command.name, `Pipeline project command ${index + 1} name`);\n }\n if (ids.has(command.id)) {\n throw new Error(\n `Pipeline project command id ${JSON.stringify(command.id)} is declared more than once.`\n );\n }\n ids.add(command.id);\n const moduleIdentity = `${command.file}\\0${command.export ?? \"\"}`;\n if (modules.has(moduleIdentity)) {\n throw new Error(\n `Pipeline project command ${JSON.stringify(command.file)}${command.export ? ` export ${JSON.stringify(command.export)}` : \"\"} is declared more than once.`\n );\n }\n modules.add(moduleIdentity);\n\n const frozenCommand: PipelineProjectCommandModule = {\n file: command.file,\n id: command.id,\n };\n if (command.export !== undefined) frozenCommand.export = command.export;\n if (command.name !== undefined) frozenCommand.name = command.name;\n return Object.freeze(frozenCommand);\n });\n\n const defined: MutableProjectManifest = {\n commands: Object.freeze(commands),\n version: PIPELINE_PROJECT_MANIFEST_VERSION,\n };\n if (manifest.cwd !== undefined) defined.cwd = manifest.cwd;\n Object.defineProperty(defined, PIPELINE_PROJECT_MANIFEST_MARKER, { value: true });\n return Object.freeze(defined);\n}\n\n/** Runtime guard used by the workbench when selecting an exported project manifest. */\nexport function isPipelineProjectManifest(value: unknown): value is PipelineProjectManifest {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(PIPELINE_PROJECT_MANIFEST_MARKER in value)) return false;\n // SAFETY: The marker is only installed by definePipelineProject, which sets version and\n // commands to their declared types, so reading them via a structural view is safe.\n const candidate = value as { version?: unknown; commands?: unknown };\n return (\n candidate.version === PIPELINE_PROJECT_MANIFEST_VERSION && Array.isArray(candidate.commands)\n );\n}\n"]}