flowsh-cli 0.4.2__tar.gz → 0.5.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowsh-cli
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: Generate Bash harness scripts from workflow YAML files.
5
5
  License-Expression: MIT
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "flowsh-cli"
7
- version = "0.4.2"
7
+ version = "0.5.0"
8
8
  description = "Generate Bash harness scripts from workflow YAML files."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -3,7 +3,7 @@
3
3
  from flowsh_cli.models import Workflow, WorkflowParseError, parse_workflows
4
4
  from flowsh_cli.render import harness_path, render_harness
5
5
 
6
- __version__ = "0.4.2"
6
+ __version__ = "0.5.0"
7
7
 
8
8
  __all__ = [
9
9
  "Workflow",
@@ -157,9 +157,23 @@ class VarsStep(BaseStep):
157
157
  Step = Annotated[VarsStep | BashStep | AgentStep, Field(discriminator="type")]
158
158
 
159
159
 
160
+ class WorkflowParam(StrictModel):
161
+ name: str
162
+ description: str | None = None
163
+ required: bool = False
164
+
165
+ @field_validator("name")
166
+ @classmethod
167
+ def validate_name(cls, value: str) -> str:
168
+ if not re.fullmatch(r"[A-Z_][A-Z0-9_]*", value):
169
+ raise ValueError("must match ^[A-Z_][A-Z0-9_]*$")
170
+ return value
171
+
172
+
160
173
  class Workflow(StrictModel):
161
174
  id: str
162
175
  name: str
176
+ params: list[WorkflowParam] = []
163
177
  steps: list[Step]
164
178
 
165
179
  @field_validator("id")
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import re
4
4
  from pathlib import Path
5
5
 
6
- from flowsh_cli.models import AgentStep, BashStep, Step, VarsStep, Workflow
6
+ from flowsh_cli.models import AgentStep, BashStep, Step, VarsStep, Workflow, WorkflowParam
7
7
 
8
8
 
9
9
  def harness_path(workflow: Workflow) -> Path:
@@ -26,13 +26,7 @@ def render_harness(workflow: Workflow) -> str:
26
26
  'LOG_BASENAME="flowsh-${WORKFLOW_SLUG}-${LOG_TIMESTAMP}-$$.log"',
27
27
  "",
28
28
  section("Argument handling"),
29
- "DRY_RUN=false",
30
- 'if [[ $# -eq 1 && "$1" == "--dry-run" ]]; then',
31
- " DRY_RUN=true",
32
- "elif [[ $# -gt 0 ]]; then",
33
- ' printf "Usage: %s [--dry-run]\\n" "$0" >&2',
34
- " exit 2",
35
- "fi",
29
+ *_render_arg_block(workflow.params),
36
30
  "",
37
31
  section("refuse_symlink_path() - keep generated logs inside plain relative paths"),
38
32
  "refuse_symlink_path() {",
@@ -353,6 +347,69 @@ def bash_quote(value: str) -> str:
353
347
  return "'" + value.replace("'", "'\\''") + "'"
354
348
 
355
349
 
350
+ def _render_arg_block(params: list[WorkflowParam]) -> list[str]:
351
+ if not params:
352
+ return [
353
+ "DRY_RUN=false",
354
+ 'if [[ $# -eq 1 && "$1" == "--dry-run" ]]; then',
355
+ " DRY_RUN=true",
356
+ "elif [[ $# -gt 0 ]]; then",
357
+ ' printf "Usage: %s [--dry-run]\\n" "$0" >&2',
358
+ " exit 2",
359
+ "fi",
360
+ ]
361
+
362
+ usage_parts: list[str] = ["[--dry-run]"]
363
+ for p in params:
364
+ usage_parts.append(f"<{p.name}>" if p.required else f"[{p.name}]")
365
+ usage_str = " ".join(usage_parts)
366
+
367
+ lines: list[str] = [
368
+ "DRY_RUN=false",
369
+ "POSITIONAL_ARGS=()",
370
+ "",
371
+ "while [[ $# -gt 0 ]]; do",
372
+ ' case "$1" in',
373
+ " --dry-run)",
374
+ " DRY_RUN=true",
375
+ " shift",
376
+ " ;;",
377
+ " --*)",
378
+ ' printf "Unknown option: %s\\n" "$1" >&2',
379
+ " exit 2",
380
+ " ;;",
381
+ " *)",
382
+ ' POSITIONAL_ARGS+=("$1")',
383
+ " shift",
384
+ " ;;",
385
+ " esac",
386
+ "done",
387
+ "",
388
+ ]
389
+
390
+ for idx, param in enumerate(params):
391
+ lines += [
392
+ f"if [[ ${{#POSITIONAL_ARGS[@]}} -gt {idx} ]]; then",
393
+ f' {param.name}="${{POSITIONAL_ARGS[{idx}]}}"',
394
+ f" export {param.name}",
395
+ "fi",
396
+ ]
397
+
398
+ lines.append("")
399
+
400
+ required = [p for p in params if p.required]
401
+ if required:
402
+ for param in required:
403
+ lines += [
404
+ f'if [[ -z "${{{param.name}:-}}" ]]; then',
405
+ f' printf "Usage: %s {usage_str}\\n" "$0" >&2',
406
+ " exit 2",
407
+ "fi",
408
+ ]
409
+
410
+ return lines
411
+
412
+
356
413
  def section(title: str) -> str:
357
414
  safe_title = truncate_one_line(title, limit=120)
358
415
  return "\n".join(
File without changes