tubeless 0.2.4 → 0.2.6

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 (72) 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/node/atomic-text.js +3 -2
  8. package/dist/node/atomic-text.js.map +1 -1
  9. package/dist/node/env.d.ts +1 -0
  10. package/dist/node/env.js +8 -0
  11. package/dist/node/env.js.map +1 -0
  12. package/dist/node/file-utils.d.ts +3 -0
  13. package/dist/node/file-utils.js +21 -0
  14. package/dist/node/file-utils.js.map +1 -0
  15. package/dist/node/node.d.ts +3 -0
  16. package/dist/node/node.js +3 -0
  17. package/dist/node/node.js.map +1 -1
  18. package/dist/node/paths.d.ts +10 -0
  19. package/dist/node/paths.js +19 -0
  20. package/dist/node/paths.js.map +1 -0
  21. package/dist/project/project-compiler.d.ts +19 -0
  22. package/dist/project/project-compiler.js +94 -0
  23. package/dist/project/project-compiler.js.map +1 -0
  24. package/dist/project/project-document.d.ts +47 -0
  25. package/dist/project/project-document.js +136 -0
  26. package/dist/project/project-document.js.map +1 -0
  27. package/dist/{workbench/workbench-project.js → project/project-manifest.js} +1 -1
  28. package/dist/project/project-manifest.js.map +1 -0
  29. package/dist/project/project.d.ts +3 -0
  30. package/dist/project/project.js +4 -0
  31. package/dist/project/project.js.map +1 -0
  32. package/dist/workbench/workbench-project-loader.d.ts +1 -1
  33. package/dist/workbench/workbench-project-loader.js +1 -1
  34. package/dist/workbench/workbench-project-loader.js.map +1 -1
  35. package/dist/workbench/workbench-validate.d.ts +2 -0
  36. package/dist/workbench/workbench-validate.js +76 -0
  37. package/dist/workbench/workbench-validate.js.map +1 -0
  38. package/dist/workbench/workbench.js +4 -0
  39. package/dist/workbench/workbench.js.map +1 -1
  40. package/docs/README.md +7 -4
  41. package/docs/agent-guide.md +27 -1
  42. package/docs/api-reference.md +66 -12
  43. package/docs/api-report.json +70 -6
  44. package/docs/cli.md +61 -9
  45. package/docs/declarative-pipelines.md +247 -0
  46. package/docs/getting-started.md +4 -3
  47. package/docs/llms.txt +5 -1
  48. package/docs/pipeline-document.schema.json +93 -0
  49. package/docs/recipes.md +58 -23
  50. package/docs/studio.md +1 -1
  51. package/examples/catalog/scripts/enrich.ts +1 -1
  52. package/examples/catalog/scripts/import.ts +1 -1
  53. package/examples/catalog/scripts/publish.ts +1 -1
  54. package/examples/catalog/tubeless.project.ts +25 -2
  55. package/examples/cli-job.ts +1 -1
  56. package/examples/declarative/handlers.ts +44 -0
  57. package/examples/declarative/peloton-handlers.ts +231 -0
  58. package/examples/declarative/peloton.yaml +65 -0
  59. package/examples/declarative/pipelines.yaml +33 -0
  60. package/examples/declarative/yaml.d.ts +5 -0
  61. package/examples/live-tui.ts +1 -1
  62. package/examples/node-artifacts.ts +28 -0
  63. package/examples/peloton.ts +1 -1
  64. package/examples/yaml-peloton.ts +49 -0
  65. package/examples/yaml-pipelines.ts +26 -0
  66. package/package.json +12 -5
  67. package/skills/tubeless/SKILL.md +20 -1
  68. package/dist/workbench/workbench-entry.d.ts +0 -2
  69. package/dist/workbench/workbench-entry.js +0 -3
  70. package/dist/workbench/workbench-entry.js.map +0 -1
  71. package/dist/workbench/workbench-project.js.map +0 -1
  72. /package/dist/{workbench/workbench-project.d.ts → project/project-manifest.d.ts} +0 -0
@@ -0,0 +1,3 @@
1
+ export { definePipelineProject, type PipelineProjectCommandModule, type PipelineProjectManifest, type PipelineProjectManifestInput, } from "./project-manifest.js";
2
+ export { validatePipelineDocument, type PipelineDocumentMetadata } from "./project-document.js";
3
+ export { compilePipelineDocument, PipelineDocumentError, type PipelineDocument, type PipelineDocumentDefinition, type PipelineDocumentFinalizer, type PipelineDocumentHandler, type PipelineDocumentRegistry, type PipelineDocumentStep, } from "./project-compiler.js";
@@ -0,0 +1,4 @@
1
+ export { definePipelineProject, } from "./project-manifest.js";
2
+ export { validatePipelineDocument } from "./project-document.js";
3
+ export { compilePipelineDocument, PipelineDocumentError, } from "./project-compiler.js";
4
+ //# 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;AAE/B,OAAO,EAAE,wBAAwB,EAAiC,MAAM,uBAAuB,CAAC;AAEhG,OAAO,EACL,uBAAuB,EACvB,qBAAqB,GAOtB,MAAM,uBAAuB,CAAC","sourcesContent":["export {\n definePipelineProject,\n type PipelineProjectCommandModule,\n type PipelineProjectManifest,\n type PipelineProjectManifestInput,\n} from \"./project-manifest.js\";\n\nexport { validatePipelineDocument, type PipelineDocumentMetadata } from \"./project-document.js\";\n\nexport {\n compilePipelineDocument,\n PipelineDocumentError,\n type PipelineDocument,\n type PipelineDocumentDefinition,\n type PipelineDocumentFinalizer,\n type PipelineDocumentHandler,\n type PipelineDocumentRegistry,\n type PipelineDocumentStep,\n} from \"./project-compiler.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"]}
@@ -0,0 +1,2 @@
1
+ import { type WorkbenchCliIo } from "./workbench-shared.js";
2
+ export declare function runValidate(argv: readonly string[], io: WorkbenchCliIo): Promise<number>;
@@ -0,0 +1,76 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { extname, resolve } from "node:path";
3
+ import { parseArgs } from "node:util";
4
+ import { PipelineDocumentError, validatePipelineDocument } from "../project/project.js";
5
+ import { errorMessage, TUBELESS_WORKBENCH_EXIT_CODE, } from "./workbench-shared.js";
6
+ import { runWorkbenchSubcommand } from "./workbench-subcommand.js";
7
+ const VALIDATE_USAGE = `Usage: tubeless validate [--json] <document.yaml|document.yml|document.json>
8
+
9
+ Check pipeline document structure and metadata without importing handlers.
10
+ Does not check graph semantics, registered names, or domain values. Use inspect
11
+ or plan on the compiled command module for engine validation.
12
+ YAML uses Bun's parser; duplicate mapping keys may be overwritten during parsing.
13
+
14
+ Options:
15
+ --json Emit a structured validation result
16
+ -h, --help Show this help
17
+ `;
18
+ export async function runValidate(argv, io) {
19
+ return runWorkbenchSubcommand({
20
+ usage: VALIDATE_USAGE,
21
+ parse: (args) => parseArgs({
22
+ args: [...args],
23
+ allowPositionals: true,
24
+ strict: true,
25
+ options: {
26
+ help: { type: "boolean", short: "h" },
27
+ json: { type: "boolean" },
28
+ },
29
+ }),
30
+ positionalCountError: { count: 1, message: "Pass exactly one YAML or JSON document." },
31
+ async run(parsed, commandIo) {
32
+ const file = resolve(commandIo.cwd, parsed.positionals[0]);
33
+ try {
34
+ const extension = extname(file).toLowerCase();
35
+ if (![".yaml", ".yml", ".json"].includes(extension)) {
36
+ throw new Error("Expected a .yaml, .yml, or .json document");
37
+ }
38
+ const source = await readFile(file, "utf8");
39
+ let value;
40
+ if (extension === ".json")
41
+ value = JSON.parse(source);
42
+ else {
43
+ // The executable runs under Bun; keep its parser out of library entrypoints.
44
+ const specifier = "bun";
45
+ // SAFETY: Bun's documented YAML.parse API returns parsed, untrusted data.
46
+ const { YAML } = (await import(specifier));
47
+ value = YAML.parse(source);
48
+ }
49
+ const document = validatePipelineDocument(value);
50
+ const pipelines = Object.keys(document.pipelines);
51
+ commandIo.stdout.write(parsed.values.json
52
+ ? `${JSON.stringify({ ok: true, file, version: document.version, metadata: document.metadata, pipelines })}\n`
53
+ : `Valid pipeline document: ${file} (${pipelines.length} pipeline(s))\n`);
54
+ return TUBELESS_WORKBENCH_EXIT_CODE.success;
55
+ }
56
+ catch (error) {
57
+ if (parsed.values.json) {
58
+ commandIo.stdout.write(`${JSON.stringify({
59
+ ok: false,
60
+ file,
61
+ error: {
62
+ message: errorMessage(error),
63
+ ...(error instanceof PipelineDocumentError
64
+ ? { code: error.code, path: error.path }
65
+ : {}),
66
+ },
67
+ })}\n`);
68
+ }
69
+ else
70
+ commandIo.stderr.write(`${errorMessage(error)}\n`);
71
+ return TUBELESS_WORKBENCH_EXIT_CODE.validation;
72
+ }
73
+ },
74
+ }, argv, io);
75
+ }
76
+ //# sourceMappingURL=workbench-validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbench-validate.js","sourceRoot":"","sources":["../../src/workbench/workbench-validate.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { extname, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { PipelineDocumentError, validatePipelineDocument } from \"../project/project.js\";\nimport {\n errorMessage,\n TUBELESS_WORKBENCH_EXIT_CODE,\n type WorkbenchCliIo,\n} from \"./workbench-shared.js\";\nimport { runWorkbenchSubcommand } from \"./workbench-subcommand.js\";\n\nconst VALIDATE_USAGE = `Usage: tubeless validate [--json] <document.yaml|document.yml|document.json>\n\nCheck pipeline document structure and metadata without importing handlers.\nDoes not check graph semantics, registered names, or domain values. Use inspect\nor plan on the compiled command module for engine validation.\nYAML uses Bun's parser; duplicate mapping keys may be overwritten during parsing.\n\nOptions:\n --json Emit a structured validation result\n -h, --help Show this help\n`;\n\nexport async function runValidate(argv: readonly string[], io: WorkbenchCliIo): Promise<number> {\n return runWorkbenchSubcommand(\n {\n usage: VALIDATE_USAGE,\n parse: (args) =>\n parseArgs({\n args: [...args],\n allowPositionals: true,\n strict: true,\n options: {\n help: { type: \"boolean\", short: \"h\" },\n json: { type: \"boolean\" },\n },\n }),\n positionalCountError: { count: 1, message: \"Pass exactly one YAML or JSON document.\" },\n async run(parsed, commandIo) {\n const file = resolve(commandIo.cwd, parsed.positionals[0]!);\n try {\n const extension = extname(file).toLowerCase();\n if (![\".yaml\", \".yml\", \".json\"].includes(extension)) {\n throw new Error(\"Expected a .yaml, .yml, or .json document\");\n }\n const source = await readFile(file, \"utf8\");\n let value: unknown;\n if (extension === \".json\") value = JSON.parse(source);\n else {\n // The executable runs under Bun; keep its parser out of library entrypoints.\n const specifier = \"bun\";\n // SAFETY: Bun's documented YAML.parse API returns parsed, untrusted data.\n const { YAML } = (await import(specifier)) as {\n YAML: { parse(text: string): unknown };\n };\n value = YAML.parse(source);\n }\n const document = validatePipelineDocument(value);\n const pipelines = Object.keys(document.pipelines);\n commandIo.stdout.write(\n parsed.values.json\n ? `${JSON.stringify({ ok: true, file, version: document.version, metadata: document.metadata, pipelines })}\\n`\n : `Valid pipeline document: ${file} (${pipelines.length} pipeline(s))\\n`\n );\n return TUBELESS_WORKBENCH_EXIT_CODE.success;\n } catch (error) {\n if (parsed.values.json) {\n commandIo.stdout.write(\n `${JSON.stringify({\n ok: false,\n file,\n error: {\n message: errorMessage(error),\n ...(error instanceof PipelineDocumentError\n ? { code: error.code, path: error.path }\n : {}),\n },\n })}\\n`\n );\n } else commandIo.stderr.write(`${errorMessage(error)}\\n`);\n return TUBELESS_WORKBENCH_EXIT_CODE.validation;\n }\n },\n },\n argv,\n io\n );\n}\n"]}
@@ -6,6 +6,7 @@ import { runPlan } from "./workbench-plan.js";
6
6
  import { runCommand } from "./workbench-run.js";
7
7
  import { TUBELESS_WORKBENCH_EXIT_CODE, writeUsageError, } from "./workbench-shared.js";
8
8
  import { runUi } from "./workbench-ui.js";
9
+ import { runValidate } from "./workbench-validate.js";
9
10
  export { TUBELESS_WORKBENCH_EXIT_CODE } from "./workbench-shared.js";
10
11
  const WORKBENCH_USAGE = `Usage: tubeless <command> [options] <registered-id|pipeline-or-command-file>
11
12
 
@@ -13,6 +14,7 @@ Inspect, plan, visualize, or safely run exported tubeless workflows.
13
14
 
14
15
  Commands:
15
16
  tubeless list List commands explicitly registered in the project manifest
17
+ tubeless validate Check a YAML or JSON pipeline document without loading handlers
16
18
  tubeless inspect Show pipeline identity and the default structural plan
17
19
  tubeless plan Preview step selection without executing the pipeline
18
20
  tubeless graph Generate Mermaid flowchart source
@@ -34,6 +36,8 @@ export async function runWorkbenchCli(argv, io) {
34
36
  }
35
37
  if (command === "list")
36
38
  return runList(commandArgs, io);
39
+ if (command === "validate")
40
+ return runValidate(commandArgs, io);
37
41
  if (command === "inspect")
38
42
  return runInspect(commandArgs, io);
39
43
  if (command === "plan")
@@ -1 +1 @@
1
- {"version":3,"file":"workbench.js","sourceRoot":"","sources":["../../src/workbench/workbench.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,4BAA4B,EAC5B,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,4BAA4B,EAAuB,MAAM,uBAAuB,CAAC;AAE1F,MAAM,eAAe,GAAG;;;;;;;;;;;;;;CAcvB,CAAC;AAEF,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAuB,EACvB,EAAkB;IAElB,MAAM,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,GAAG,IAAI,CAAC;IACvC,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACnE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACjC,OAAO,4BAA4B,CAAC,OAAO,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,eAAe,CAAC,EAAE,EAAE,iBAAiB,EAAE,eAAe,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACpD,OAAO,eAAe,CAAC,EAAE,EAAE,mBAAmB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AAC7F,CAAC","sourcesContent":["import { runGraph } from \"./workbench-graph.js\";\nimport { runHistory } from \"./workbench-history.js\";\nimport { runInspect } from \"./workbench-inspect.js\";\nimport { runList } from \"./workbench-list.js\";\nimport { runPlan } from \"./workbench-plan.js\";\nimport { runCommand } from \"./workbench-run.js\";\nimport {\n TUBELESS_WORKBENCH_EXIT_CODE,\n writeUsageError,\n type WorkbenchCliIo,\n} from \"./workbench-shared.js\";\nimport { runUi } from \"./workbench-ui.js\";\n\nexport { TUBELESS_WORKBENCH_EXIT_CODE, type WorkbenchCliIo } from \"./workbench-shared.js\";\n\nconst WORKBENCH_USAGE = `Usage: tubeless <command> [options] <registered-id|pipeline-or-command-file>\n\nInspect, plan, visualize, or safely run exported tubeless workflows.\n\nCommands:\n tubeless list List commands explicitly registered in the project manifest\n tubeless inspect Show pipeline identity and the default structural plan\n tubeless plan Preview step selection without executing the pipeline\n tubeless graph Generate Mermaid flowchart source\n tubeless run Execute an exported definePipelineCommand\n tubeless history Show recorded runs from the local SQLite store\n tubeless ui Open the optional local run studio\n\nRun tubeless <command> --help for command-specific options.\n`;\n\n/** Run the `tubeless` development workbench. Execution requires a definePipelineCommand export. */\nexport async function runWorkbenchCli(\n argv: readonly string[],\n io: WorkbenchCliIo\n): Promise<number> {\n const [command, ...commandArgs] = argv;\n if (command === \"--help\" || command === \"-h\" || command === \"help\") {\n io.stdout.write(WORKBENCH_USAGE);\n return TUBELESS_WORKBENCH_EXIT_CODE.success;\n }\n if (command === undefined) {\n return writeUsageError(io, \"Pass a command.\", WORKBENCH_USAGE);\n }\n if (command === \"list\") return runList(commandArgs, io);\n if (command === \"inspect\") return runInspect(commandArgs, io);\n if (command === \"plan\") return runPlan(commandArgs, io);\n if (command === \"graph\") return runGraph(commandArgs, io);\n if (command === \"run\") return runCommand(commandArgs, io);\n if (command === \"history\") return runHistory(commandArgs, io);\n if (command === \"ui\") return runUi(commandArgs, io);\n return writeUsageError(io, `Unknown command ${JSON.stringify(command)}.`, WORKBENCH_USAGE);\n}\n"]}
1
+ {"version":3,"file":"workbench.js","sourceRoot":"","sources":["../../src/workbench/workbench.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,4BAA4B,EAC5B,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAE,4BAA4B,EAAuB,MAAM,uBAAuB,CAAC;AAE1F,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;CAevB,CAAC;AAEF,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAuB,EACvB,EAAkB;IAElB,MAAM,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,GAAG,IAAI,CAAC;IACvC,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACnE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACjC,OAAO,4BAA4B,CAAC,OAAO,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,eAAe,CAAC,EAAE,EAAE,iBAAiB,EAAE,eAAe,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,UAAU;QAAE,OAAO,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAChE,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACpD,OAAO,eAAe,CAAC,EAAE,EAAE,mBAAmB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AAC7F,CAAC","sourcesContent":["import { runGraph } from \"./workbench-graph.js\";\nimport { runHistory } from \"./workbench-history.js\";\nimport { runInspect } from \"./workbench-inspect.js\";\nimport { runList } from \"./workbench-list.js\";\nimport { runPlan } from \"./workbench-plan.js\";\nimport { runCommand } from \"./workbench-run.js\";\nimport {\n TUBELESS_WORKBENCH_EXIT_CODE,\n writeUsageError,\n type WorkbenchCliIo,\n} from \"./workbench-shared.js\";\nimport { runUi } from \"./workbench-ui.js\";\nimport { runValidate } from \"./workbench-validate.js\";\n\nexport { TUBELESS_WORKBENCH_EXIT_CODE, type WorkbenchCliIo } from \"./workbench-shared.js\";\n\nconst WORKBENCH_USAGE = `Usage: tubeless <command> [options] <registered-id|pipeline-or-command-file>\n\nInspect, plan, visualize, or safely run exported tubeless workflows.\n\nCommands:\n tubeless list List commands explicitly registered in the project manifest\n tubeless validate Check a YAML or JSON pipeline document without loading handlers\n tubeless inspect Show pipeline identity and the default structural plan\n tubeless plan Preview step selection without executing the pipeline\n tubeless graph Generate Mermaid flowchart source\n tubeless run Execute an exported definePipelineCommand\n tubeless history Show recorded runs from the local SQLite store\n tubeless ui Open the optional local run studio\n\nRun tubeless <command> --help for command-specific options.\n`;\n\n/** Run the `tubeless` development workbench. Execution requires a definePipelineCommand export. */\nexport async function runWorkbenchCli(\n argv: readonly string[],\n io: WorkbenchCliIo\n): Promise<number> {\n const [command, ...commandArgs] = argv;\n if (command === \"--help\" || command === \"-h\" || command === \"help\") {\n io.stdout.write(WORKBENCH_USAGE);\n return TUBELESS_WORKBENCH_EXIT_CODE.success;\n }\n if (command === undefined) {\n return writeUsageError(io, \"Pass a command.\", WORKBENCH_USAGE);\n }\n if (command === \"list\") return runList(commandArgs, io);\n if (command === \"validate\") return runValidate(commandArgs, io);\n if (command === \"inspect\") return runInspect(commandArgs, io);\n if (command === \"plan\") return runPlan(commandArgs, io);\n if (command === \"graph\") return runGraph(commandArgs, io);\n if (command === \"run\") return runCommand(commandArgs, io);\n if (command === \"history\") return runHistory(commandArgs, io);\n if (command === \"ui\") return runUi(commandArgs, io);\n return writeUsageError(io, `Unknown command ${JSON.stringify(command)}.`, WORKBENCH_USAGE);\n}\n"]}
package/docs/README.md CHANGED
@@ -22,13 +22,16 @@ adapt in your project.
22
22
 
23
23
  Install the [agent skill pack](./agent-skills.md) to author pipelines or convert existing code.
24
24
 
25
- | Document | Use it for |
26
- | --------------------------- | -------------------------------------------------------------------- |
27
- | [CLI](./cli.md) | `list`, `inspect`, `plan`, `graph`, `run`, `history`, and exit codes |
28
- | [Local studio](./studio.md) | Optional run history, `tubeless ui`, and catalogs |
25
+ | Document | Use it for |
26
+ | ----------------------------------------------------- | -------------------------------------------------------------------- |
27
+ | [CLI](./cli.md) | `list`, `inspect`, `plan`, `graph`, `run`, `history`, and exit codes |
28
+ | [Local studio](./studio.md) | Optional run history, `tubeless ui`, and catalogs |
29
+ | [YAML and JSON pipelines](./declarative-pipelines.md) | Compile documents with registered application functions |
29
30
 
30
31
  ## Deeper reference
31
32
 
33
+ For agent or editor validation, use the [pipeline document JSON Schema](./pipeline-document.schema.json).
34
+
32
35
  | Document | Use it for |
33
36
  | ------------------------------------------------------------------------------------------------ | -------------------------------------------------- |
34
37
  | [Child-pipeline composition](./child-pipeline-composition.md) | Running a reusable pipeline once or for many items |
@@ -22,6 +22,21 @@ dry runs with fake I/O. The general `tubeless` skill supports ongoing authoring.
22
22
 
23
23
  ## Choose pipeline features
24
24
 
25
+ - For YAML or JSON authoring, read [declarative pipelines](./declarative-pipelines.md)
26
+ and adapt [the YAML recipe](../examples/yaml-pipelines.ts). Parse at the
27
+ application edge, then use `compilePipelineDocument` from `tubeless/project`
28
+ with explicitly registered handlers and schemas. Keep command registration
29
+ explicit for CLI and Studio. Unknown fields and references fail compilation;
30
+ plans still do not validate domain inputs. Dynamic wiring does not infer
31
+ TypeScript output types, so validate or narrow unknown values in handlers.
32
+ Use [YAML Peloton](../examples/yaml-peloton.ts) for a larger example with
33
+ progress, retrying concurrent handlers, dry-run policies, and failure gates.
34
+ Its per-rider progress is ordinary step detail, not child-pipeline fan-out.
35
+ Use `tubeless validate --json <document.yaml>` for a handler-free structure
36
+ check, and the [document JSON Schema](./pipeline-document.schema.json) for
37
+ editor or agent validation. Optional document metadata is descriptive only.
38
+ `inspect` or `plan` on a compiled command still checks graph semantics.
39
+
25
40
  - Use `createSteps<TDomainOptions>()` once per pipeline and `definePipeline` once
26
41
  after declaring its steps. Domain option types contain domain input only;
27
42
  callers pass those options and optional built-in controls to
@@ -71,7 +86,7 @@ dry runs with fake I/O. The general `tubeless` skill supports ongoing authoring.
71
86
  - Use `runConcurrent` for bounded lightweight functions that do not need child
72
87
  lifecycle events. Use `runConcurrentSettled` when the caller needs completed
73
88
  results and the first failure instead of a throw.
74
- - Use `definePipelineCommand` from `tubeless/workbench` for scripts centered on
89
+ - Use `definePipelineCommand` from `tubeless/cli` for scripts centered on
75
90
  a pipeline. Do not parse `process.argv` manually or redeclare built-in dry-run,
76
91
  `--step`, or `--target` flags. `mapOptions`, validation, and hooks receive `stepIds` and `targets`,
77
92
  not `step` or `target`. Omit `mapOptions` when validated flags already satisfy
@@ -81,6 +96,10 @@ dry runs with fake I/O. The general `tubeless` skill supports ongoing authoring.
81
96
  render those parameter definitions instead of parsing help text.
82
97
  Use `command.plan()` or `tubeless plan` for a selection-only preview. Do not
83
98
  simulate planning with `--plan`.
99
+ - Use `defineCommand` from `tubeless/cli` for standalone scripts. Declare command
100
+ catalogs with `definePipelineProject` from `tubeless/project`; the catalog
101
+ works with the CLI before Studio is added. Keep application-specific prompts
102
+ in the consumer.
84
103
  - Use `pipeline.toMermaid()` or `command.toMermaid()` when documentation needs
85
104
  the static graph; do not duplicate dependency edges by hand.
86
105
  - Use `tubeless list` for the explicit project command inventory. Use
@@ -113,6 +132,13 @@ dry runs with fake I/O. The general `tubeless` skill supports ongoing authoring.
113
132
  - Call `context.reportProgress` for long loops and `context.reportAttempt` for
114
133
  retries that operators should see.
115
134
  - Resolve relative files from `context.cwd` with `node:path`.
135
+ - Import optional Node helpers from `tubeless/node`: `definePaths` resolves named
136
+ paths against each run's `context.cwd`; `writeJson` creates parents and atomically
137
+ replaces JSON files, rejecting unserializable top-level values before touching
138
+ disk; `readJson<T>` parses trusted JSON without schema validation;
139
+ `resetDir` deletes and recreates generated output directories; `requireEnv`
140
+ checks required environment values when called. Mark writes and resets with
141
+ `dryRun: "skip"`; these helpers do not inspect pipeline controls themselves.
116
142
  - Use `runOrThrow` when every step must succeed and the caller expects a value.
117
143
  It always throws for an unsuccessful run, including `continueOnError` runs.
118
144
  Use `run` when the caller must inspect failures, skips, timings, or best-effort
@@ -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` | `06411eb43b8d1b5aca5d51d70e68553cb9065447b250c29382e6036ad9eac52c` | 8 |
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` | `464f5d94a3533c4050c32d902badafa4fabfdec176152bb36bb738cf52ce6e81` | 14 |
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`
@@ -100,8 +137,13 @@ Package: `tubeless`
100
137
  ### `tubeless/node`
101
138
 
102
139
  - `CheckpointStore`
140
+ - `definePaths`
103
141
  - `openCheckpoint`
142
+ - `readJson`
143
+ - `requireEnv`
144
+ - `resetDir`
104
145
  - `withCheckpointedBatch`
146
+ - `writeJson`
105
147
 
106
148
  ### `tubeless/rate-limit`
107
149
 
@@ -114,10 +156,22 @@ Package: `tubeless`
114
156
  - `RetryOptions`
115
157
  - `withRetry`
116
158
 
117
- ### `tubeless/workbench`
159
+ ### `tubeless/project`
118
160
 
119
- - `definePipelineCommand`
161
+ - `compilePipelineDocument`
120
162
  - `definePipelineProject`
163
+ - `PipelineDocument`
164
+ - `PipelineDocumentDefinition`
165
+ - `PipelineDocumentError`
166
+ - `PipelineDocumentFinalizer`
167
+ - `PipelineDocumentHandler`
168
+ - `PipelineDocumentMetadata`
169
+ - `PipelineDocumentRegistry`
170
+ - `PipelineDocumentStep`
171
+ - `PipelineProjectCommandModule`
172
+ - `PipelineProjectManifest`
173
+ - `PipelineProjectManifestInput`
174
+ - `validatePipelineDocument`
121
175
 
122
176
  ### `tubeless/testing`
123
177
 
@@ -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": [
@@ -88,8 +128,17 @@
88
128
  },
89
129
  {
90
130
  "declaration": "./dist/node/node.d.ts",
91
- "exports": ["CheckpointStore", "openCheckpoint", "withCheckpointedBatch"],
92
- "sha256": "1bf8549ca953a11e7202dbda8013db2a8f1d56286f1618bad929efbbb2efd302",
131
+ "exports": [
132
+ "CheckpointStore",
133
+ "definePaths",
134
+ "openCheckpoint",
135
+ "readJson",
136
+ "requireEnv",
137
+ "resetDir",
138
+ "withCheckpointedBatch",
139
+ "writeJson"
140
+ ],
141
+ "sha256": "06411eb43b8d1b5aca5d51d70e68553cb9065447b250c29382e6036ad9eac52c",
93
142
  "specifier": "tubeless/node"
94
143
  },
95
144
  {
@@ -105,10 +154,25 @@
105
154
  "specifier": "tubeless/retry"
106
155
  },
107
156
  {
108
- "declaration": "./dist/workbench/workbench-entry.d.ts",
109
- "exports": ["definePipelineCommand", "definePipelineProject"],
110
- "sha256": "494aa61a6345d9a798f2e1f084a69cc7859439237c6614280a96421eb0f17ffd",
111
- "specifier": "tubeless/workbench"
157
+ "declaration": "./dist/project/project.d.ts",
158
+ "exports": [
159
+ "compilePipelineDocument",
160
+ "definePipelineProject",
161
+ "PipelineDocument",
162
+ "PipelineDocumentDefinition",
163
+ "PipelineDocumentError",
164
+ "PipelineDocumentFinalizer",
165
+ "PipelineDocumentHandler",
166
+ "PipelineDocumentMetadata",
167
+ "PipelineDocumentRegistry",
168
+ "PipelineDocumentStep",
169
+ "PipelineProjectCommandModule",
170
+ "PipelineProjectManifest",
171
+ "PipelineProjectManifestInput",
172
+ "validatePipelineDocument"
173
+ ],
174
+ "sha256": "464f5d94a3533c4050c32d902badafa4fabfdec176152bb36bb738cf52ce6e81",
175
+ "specifier": "tubeless/project"
112
176
  },
113
177
  {
114
178
  "declaration": "./dist/testing/testing.d.ts",
package/docs/cli.md CHANGED
@@ -17,15 +17,67 @@ can run on Node.js 22 or later without Bun.
17
17
 
18
18
  ## Commands
19
19
 
20
- | Command | Accepts | Does |
21
- | ------------------ | ----------------------------------- | --------------------------------------------------------------------- |
22
- | `tubeless list` | A project manifest | Lists explicitly registered command IDs without loading their modules |
23
- | `tubeless inspect` | A pipeline or command export | Shows the pipeline ID, available targets, steps, and default plan |
24
- | `tubeless plan` | A pipeline or command export | Previews selection without executing or requiring domain options |
25
- | `tubeless graph` | A pipeline or command export | Writes Mermaid flowchart source |
26
- | `tubeless run` | A `definePipelineCommand` export | Validates command arguments and runs the pipeline |
27
- | `tubeless history` | An optional run id | Lists or shows recorded runs from SQLite or a finished NDJSON trace |
28
- | `tubeless ui` | An optional project/Studio manifest | Serves the local run studio; see [studio](./studio.md) |
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
+ Import optional filesystem, environment and checkpoint helpers from `tubeless/node`;
61
+ see the [Node helpers recipe](./recipes.md#node-helpers). Application-specific
62
+ selection prompts belong in the application.
63
+
64
+ ### Executable commands
65
+
66
+ | Command | Accepts | Does |
67
+ | ------------------- | ----------------------------------- | --------------------------------------------------------------------- |
68
+ | `tubeless list` | A project manifest | Lists explicitly registered command IDs without loading their modules |
69
+ | `tubeless validate` | A YAML or JSON pipeline document | Checks document structure and metadata without loading handlers |
70
+ | `tubeless inspect` | A pipeline or command export | Shows the pipeline ID, available targets, steps, and default plan |
71
+ | `tubeless plan` | A pipeline or command export | Previews selection without executing or requiring domain options |
72
+ | `tubeless graph` | A pipeline or command export | Writes Mermaid flowchart source |
73
+ | `tubeless run` | A `definePipelineCommand` export | Validates command arguments and runs the pipeline |
74
+ | `tubeless history` | An optional run id | Lists or shows recorded runs from SQLite or a finished NDJSON trace |
75
+ | `tubeless ui` | An optional project/Studio manifest | Serves the local run studio; see [studio](./studio.md) |
76
+
77
+ Use `tubeless validate [--json] pipelines.yaml` for a fast document-only check.
78
+ It supports `.yaml`, `.yml`, and `.json`; it does not resolve handlers, check the
79
+ graph, or run domain schemas. See [declarative pipelines](./declarative-pipelines.md)
80
+ for the downloadable JSON Schema and validation levels.
29
81
 
30
82
  Use a checked-in `tubeless.project.ts` to address commands by stable project ID:
31
83