flowsh-cli 0.3.0__tar.gz → 0.4.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.3.0
3
+ Version: 0.4.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
@@ -38,13 +38,15 @@ workflows:
38
38
  - type: agent
39
39
  name: Ask OpenCode
40
40
  agent: general
41
+ model: openai/gpt-5
42
+ command: review
41
43
  prompt: |
42
44
  Summarize the current repository state.
43
45
  ```
44
46
 
45
47
  Supported step types are only `vars`, `bash`, and `agent`.
46
48
 
47
- The input path must be a regular file no larger than 1 MiB. The input file must be valid UTF-8, non-empty YAML with a mapping root, no duplicate mapping keys, and no YAML aliases. Workflow and step names are single-line labels. Executable fields reject unsafe control bytes while allowing normal newlines and tabs. `vars` keys must be uppercase shell variable names, and `agent` names may contain only letters, digits, `_`, and `-`.
49
+ The input path must be a regular file no larger than 1 MiB. The input file must be valid UTF-8, non-empty YAML with a mapping root, no duplicate mapping keys, and no YAML aliases. Workflow and step names are single-line labels. Executable fields reject unsafe control bytes while allowing normal newlines and tabs. `vars` keys must be uppercase shell variable names, and `agent` names may contain only letters, digits, `_`, and `-`. Agent `model` values are passed through to OpenCode, so provider/model IDs such as `openai/gpt-5` are valid. Agent `command` values map to OpenCode `--command`; the prompt remains the message/arguments after `--`.
48
50
 
49
51
  Agent prompts are literal by default. Set `expandPrompt: true` on an `agent` step only when the prompt should be expanded by Bash at harness runtime, for example to insert values exported by earlier `vars` steps:
50
52
 
@@ -59,6 +61,8 @@ Agent prompts are literal by default. Set `expandPrompt: true` on an `agent` ste
59
61
 
60
62
  `expandPrompt: true` is a security-sensitive opt-in: Bash also performs command substitution such as `$(...)` and backticks in the prompt body before OpenCode receives it. Keep it disabled for prompts that contain shell examples or untrusted content.
61
63
 
64
+ Set `dangerouslySkipPermissions: true` on an `agent` step only when the generated harness should pass OpenCode `--dangerously-skip-permissions`. The flag is false by default, accepts the YAML alias `dangerously-skip-permissions`, and auto-approves permissions that are not explicitly denied. Treat it as security-sensitive and avoid it for untrusted workflows.
65
+
62
66
  Harness paths are derived from workflow ids. `wf_example` writes `.harness/example.sh`.
63
67
 
64
68
  ## Commands
@@ -130,7 +134,7 @@ Generated harnesses are also non-interactive. `harness.sh --dry-run` exits `0` a
130
134
 
131
135
  Generated harnesses are written with owner-only executable permissions and refuse to overwrite existing paths unless `--force` is passed. Multi-workflow generation preflights overwrite conflicts before writing any harness. `--force` replaces regular harness files and harness-file symlinks, but never replaces a directory at a harness file path. The `.harness` output directory must be a real directory, not a symlink or file. Harness dry runs do not create log files or directories. Real harness logs go to `.flowsh/logs` by default with owner-private directory and file permissions. Set `FLOWSH_LOG_DIR` when running a harness to use another local relative log directory; absolute paths, `..` path segments, symlinked path components, and non-directory log paths are refused. Logging setup and write failures fail the harness instead of being silently ignored.
132
136
 
133
- Generated `bash` and `vars` bodies run with `bash -euo pipefail`, so command failures stop the workflow instead of being masked by later successful commands. Captured `vars` values are exported for later `bash` steps. `agent` prompt heredocs are quoted by default and unquoted only when `expandPrompt: true` is set. `agent` steps invoke only `opencode run --format json -- <prompt>` with optional `--agent <agent>` before `--`, so dash-prefixed prompts are message content rather than OpenCode flags. Agent steps fail with a clear error if `opencode` is not on `PATH`.
137
+ Generated `bash` and `vars` bodies run with `bash -euo pipefail`, so command failures stop the workflow instead of being masked by later successful commands. Captured `vars` values are exported for later `bash` steps. `agent` prompt heredocs are quoted by default and unquoted only when `expandPrompt: true` is set. `agent` steps invoke only `opencode run --format json` with optional `--agent <agent>`, `--model <provider/model>`, `--command <command>`, and `--dangerously-skip-permissions` flags before `-- <prompt>`, so dash-prefixed prompts are message content rather than OpenCode flags. Agent steps fail with a clear error if `opencode` is not on `PATH`.
134
138
 
135
139
  ## Development
136
140
 
@@ -22,13 +22,15 @@ workflows:
22
22
  - type: agent
23
23
  name: Ask OpenCode
24
24
  agent: general
25
+ model: openai/gpt-5
26
+ command: review
25
27
  prompt: |
26
28
  Summarize the current repository state.
27
29
  ```
28
30
 
29
31
  Supported step types are only `vars`, `bash`, and `agent`.
30
32
 
31
- The input path must be a regular file no larger than 1 MiB. The input file must be valid UTF-8, non-empty YAML with a mapping root, no duplicate mapping keys, and no YAML aliases. Workflow and step names are single-line labels. Executable fields reject unsafe control bytes while allowing normal newlines and tabs. `vars` keys must be uppercase shell variable names, and `agent` names may contain only letters, digits, `_`, and `-`.
33
+ The input path must be a regular file no larger than 1 MiB. The input file must be valid UTF-8, non-empty YAML with a mapping root, no duplicate mapping keys, and no YAML aliases. Workflow and step names are single-line labels. Executable fields reject unsafe control bytes while allowing normal newlines and tabs. `vars` keys must be uppercase shell variable names, and `agent` names may contain only letters, digits, `_`, and `-`. Agent `model` values are passed through to OpenCode, so provider/model IDs such as `openai/gpt-5` are valid. Agent `command` values map to OpenCode `--command`; the prompt remains the message/arguments after `--`.
32
34
 
33
35
  Agent prompts are literal by default. Set `expandPrompt: true` on an `agent` step only when the prompt should be expanded by Bash at harness runtime, for example to insert values exported by earlier `vars` steps:
34
36
 
@@ -43,6 +45,8 @@ Agent prompts are literal by default. Set `expandPrompt: true` on an `agent` ste
43
45
 
44
46
  `expandPrompt: true` is a security-sensitive opt-in: Bash also performs command substitution such as `$(...)` and backticks in the prompt body before OpenCode receives it. Keep it disabled for prompts that contain shell examples or untrusted content.
45
47
 
48
+ Set `dangerouslySkipPermissions: true` on an `agent` step only when the generated harness should pass OpenCode `--dangerously-skip-permissions`. The flag is false by default, accepts the YAML alias `dangerously-skip-permissions`, and auto-approves permissions that are not explicitly denied. Treat it as security-sensitive and avoid it for untrusted workflows.
49
+
46
50
  Harness paths are derived from workflow ids. `wf_example` writes `.harness/example.sh`.
47
51
 
48
52
  ## Commands
@@ -114,7 +118,7 @@ Generated harnesses are also non-interactive. `harness.sh --dry-run` exits `0` a
114
118
 
115
119
  Generated harnesses are written with owner-only executable permissions and refuse to overwrite existing paths unless `--force` is passed. Multi-workflow generation preflights overwrite conflicts before writing any harness. `--force` replaces regular harness files and harness-file symlinks, but never replaces a directory at a harness file path. The `.harness` output directory must be a real directory, not a symlink or file. Harness dry runs do not create log files or directories. Real harness logs go to `.flowsh/logs` by default with owner-private directory and file permissions. Set `FLOWSH_LOG_DIR` when running a harness to use another local relative log directory; absolute paths, `..` path segments, symlinked path components, and non-directory log paths are refused. Logging setup and write failures fail the harness instead of being silently ignored.
116
120
 
117
- Generated `bash` and `vars` bodies run with `bash -euo pipefail`, so command failures stop the workflow instead of being masked by later successful commands. Captured `vars` values are exported for later `bash` steps. `agent` prompt heredocs are quoted by default and unquoted only when `expandPrompt: true` is set. `agent` steps invoke only `opencode run --format json -- <prompt>` with optional `--agent <agent>` before `--`, so dash-prefixed prompts are message content rather than OpenCode flags. Agent steps fail with a clear error if `opencode` is not on `PATH`.
121
+ Generated `bash` and `vars` bodies run with `bash -euo pipefail`, so command failures stop the workflow instead of being masked by later successful commands. Captured `vars` values are exported for later `bash` steps. `agent` prompt heredocs are quoted by default and unquoted only when `expandPrompt: true` is set. `agent` steps invoke only `opencode run --format json` with optional `--agent <agent>`, `--model <provider/model>`, `--command <command>`, and `--dangerously-skip-permissions` flags before `-- <prompt>`, so dash-prefixed prompts are message content rather than OpenCode flags. Agent steps fail with a clear error if `opencode` is not on `PATH`.
118
122
 
119
123
  ## Development
120
124
 
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "flowsh-cli"
7
- version = "0.3.0"
7
+ version = "0.4.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.3.0"
6
+ __version__ = "0.4.0"
7
7
 
8
8
  __all__ = [
9
9
  "Workflow",
@@ -7,7 +7,15 @@ from pathlib import Path
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  import yaml
10
- from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_validator
10
+ from pydantic import (
11
+ AliasChoices,
12
+ BaseModel,
13
+ ConfigDict,
14
+ Field,
15
+ ValidationError,
16
+ field_validator,
17
+ model_validator,
18
+ )
11
19
 
12
20
  MAX_WORKFLOW_YAML_BYTES = 1_048_576
13
21
 
@@ -67,9 +75,31 @@ class AgentStep(BaseStep):
67
75
  type: Literal["agent"]
68
76
  prompt: str
69
77
  agent: str | None = None
78
+ model: str | None = None
79
+ command: str | None = None
80
+ dangerouslySkipPermissions: bool = Field(
81
+ default=False,
82
+ validation_alias=AliasChoices(
83
+ "dangerouslySkipPermissions",
84
+ "dangerously-skip-permissions",
85
+ ),
86
+ )
70
87
  expandPrompt: bool = False
71
88
 
72
- @field_validator("prompt", "agent")
89
+ @model_validator(mode="before")
90
+ @classmethod
91
+ def reject_ambiguous_dangerous_aliases(cls, data: object) -> object:
92
+ if (
93
+ isinstance(data, Mapping)
94
+ and "dangerouslySkipPermissions" in data
95
+ and "dangerously-skip-permissions" in data
96
+ ):
97
+ raise ValueError(
98
+ "dangerouslySkipPermissions and dangerously-skip-permissions must not both be set"
99
+ )
100
+ return data
101
+
102
+ @field_validator("prompt", "agent", "model", "command")
73
103
  @classmethod
74
104
  def validate_strings(cls, value: str | None) -> str | None:
75
105
  if value is not None and value.strip() == "":
@@ -174,11 +174,23 @@ def render_harness(workflow: Workflow) -> str:
174
174
  "run_agent() {",
175
175
  ' local prompt="$1"',
176
176
  ' local agent="${2:-}"',
177
+ ' local model="${3:-}"',
178
+ ' local command="${4:-}"',
179
+ ' local dangerously_skip_permissions="${5:-false}"',
177
180
  "",
178
181
  " local cmd=(opencode run --format json)",
179
182
  ' if [[ -n "$agent" ]]; then',
180
183
  ' cmd+=(--agent "$agent")',
181
184
  " fi",
185
+ ' if [[ -n "$model" ]]; then',
186
+ ' cmd+=(--model "$model")',
187
+ " fi",
188
+ ' if [[ -n "$command" ]]; then',
189
+ ' cmd+=(--command "$command")',
190
+ " fi",
191
+ ' if [[ "$dangerously_skip_permissions" == true ]]; then',
192
+ " cmd+=(--dangerously-skip-permissions)",
193
+ " fi",
182
194
  "",
183
195
  ' if [[ "$DRY_RUN" == true ]]; then',
184
196
  ' log INFO "[DRY-RUN] would run: $(printf \'%q \' "${cmd[@]}") (with prompt)"',
@@ -255,11 +267,14 @@ def render_step(index: int, step: Step, used_function_names: set[str] | None = N
255
267
  " )",
256
268
  ]
257
269
  )
258
- if step.agent:
259
- lines.append(f" local agent={bash_quote(step.agent)}")
260
- lines.append(' run_agent "$prompt" "$agent"')
261
- else:
262
- lines.append(' run_agent "$prompt"')
270
+ lines.append(f" local agent={bash_quote(step.agent or '')}")
271
+ lines.append(f" local model={bash_quote(step.model or '')}")
272
+ lines.append(f" local command={bash_quote(step.command or '')}")
273
+ dangerous_skip_permissions = "true" if step.dangerouslySkipPermissions else "false"
274
+ lines.append(f" local dangerously_skip_permissions={dangerous_skip_permissions}")
275
+ lines.append(
276
+ ' run_agent "$prompt" "$agent" "$model" "$command" "$dangerously_skip_permissions"'
277
+ )
263
278
  else:
264
279
  raise AssertionError(f"Unsupported step type: {step}")
265
280