Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ built, and which output format to prefer.
| Multi-backend generation | Swagger 2.0, OpenAPI 3, protobuf services with `google.api.http` annotations, and curated GraphQL schemas become Cobra command trees. |
| Single runtime shape | Generated modules share one runtime for auth, request building, output formatting, pagination, streaming, and error handling. |
| Agentic-friendly discovery | `search`, `commands --json`, `commands show`, and `commands schema` expose the CLI as structured data. |
| Generated Skills | Codegen writes `skills/<cli-name>/` so agents can load the CLI's operating guide and module references. |
| Generated Skills | Codegen writes `skills/<cli-name>/` and can bundle it into the CLI with `skill.bundle`, exposing `<cli> skill install`. |
| Reproducible inputs | Git specs are pinned by tag and resolved to commit SHA; local sources are staged from checked-in configuration. |
| Real CLI UX | Hostname-keyed auth, --file, --set, --set-str, -o table\|json\|yaml\|raw, enum validation, pagination, and streaming. |
| Overlay polish | Improve summaries, aliases, parameter help, grouping, and examples without editing generated code. |
Expand Down
23 changes: 23 additions & 0 deletions docs/cli-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,29 @@ go mod tidy
go build -o bin/acmectl ./cmd/acmectl
```

## Bundle Skill Installation

Set `skill.bundle: true` when you want the generated CLI binary to carry its
own Skill installer:

```yaml
skill:
bundle: true
```

After codegen, wire the downstream CLI with `generated.Mount` as shown above.
Lathe mirrors the generated Skill into `internal/generated/skillbundle/`, pins
the required kitup Go modules, and mounts `<cli> skill install` for you. You do
not need a downstream `go:embed` bridge or handwritten kitup command wiring for
the standard bundled Skill installer.

Verify the compiled installer after building:

```sh
bin/acmectl __lathe verify --json
bin/acmectl skill install --scope user --agent codex --yes
```

## Agent Operation Loop

Generated CLIs expose machine-readable contracts. Agents should use this loop:
Expand Down
235 changes: 235 additions & 0 deletions docs/workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# Workflow Commands

Status: proposed.

## Purpose

Workflow commands let a CLI builder publish a normal command whose implementation
is a deterministic sequence of generated API operations.

The user-facing command should look like any other Cobra command:

```sh
mycli doctor --json
mycli deploy --app-id app_123
```

The workflow DSL is a builder-facing codegen input. It compiles into named
commands, and end users should not need to know a workflow DSL exists.

## Boundary

Workflow commands sit between generated API operations and handwritten custom Go:

- Use a workflow command when behavior is a linear composition of generated API
operations.
- Keep handwritten Go when behavior needs local environment checks, custom
target resolution, non-API IO, build metadata, interactive prompts, or other
imperative logic.
- Keep overlays and source config for shaping individual generated API commands.

The first version is API-only. A Mosoo-style `doctor` command that checks target
resolution, local credential stores, build metadata, and raw HTTP reachability is
not fully covered by v0 unless those checks are exposed as generated API
operations. It can remain custom Go until diagnostic primitives are designed.

## Non-Goals

The first version does not support:

- Rollback or compensation.
- `if` / `else`, loops, parallel steps, or retry policy.
- Shell commands or external actions.
- Dynamic plugins or remote extension code.
- A public `GeneratedApp` or workflow engine ABI.
- End-user commands not explicitly named by the CLI builder.

## Failure Semantics

Workflow commands run steps in order and stop at the first failure.

If step N fails, steps 1 through N-1 may already have changed remote state. Lathe
does not roll them back. API specifications do not provide a universal undo
contract, so automatic rollback would be a false transaction.

Failures must be structured. JSON output should include:

- The failed step ID.
- The underlying error class.
- Completed step IDs.
- The successful step outputs needed for manual recovery.

Human output should explain that partial completion may have occurred.

## Configuration

Workflow configuration belongs in `cli.yaml` as its own domain block:

```yaml
workflow:
root: workflows
commands:
- doctor.yaml
```

Paths are relative to the directory containing `cli.yaml`. The root defaults to
`workflows` when omitted.

Do not add an `extensions:` block for first-party workflow commands.

## DSL Shape

Each workflow file defines one user-facing command:

```yaml
version: 1
command:
use: doctor
short: Check CLI API readiness
long: Check the configured API by running read-only generated operations.
aliases: [check]

inputs:
app_id:
flag: app-id
type: string
required: true
help: App ID to inspect

steps:
- id: app
operation: "console apps app-detail"
params:
appId: "${input.app_id}"
capture:
status: "app.status"
deployment: "app.deployment.status"

- id: deployment
operation: "console apps app-deployment-status"
params:
appId: "${input.app_id}"
capture:
state: "deployment.status"

output:
app_status: "${steps.app.status}"
deployment_status: "${steps.deployment.state}"
```

`operation` is the generated command path shown by `commands show`. Codegen must
validate that the path exists and maps to exactly one generated operation.

`params` keys match generated parameter names or flags. Codegen must validate
that every key maps to a known `runtime.ParamSpec`.

`capture` and `output` use dot paths over JSON responses. Missing paths are
errors unless the field is marked optional in a later schema version.

## Runtime Model

Workflow commands must call the same operation path as generated Cobra API
commands. They must not shell out to the CLI.

First extract a runtime operation invoker:

```go
InvokeOperation(ctx, spec, input, opts) (result, error)
```

Generated API commands become thin Cobra adapters around that function. Workflow
commands call it directly with compiled `runtime.CommandSpec` values.

The invoker owns:

- Parameter validation and enum checks.
- Request path, query, header, form, and body construction.
- Auth and host behavior equivalent to generated API commands.
- Dry-run request resolution.
- Pagination and wait behavior.
- Output bytes and HTTP errors.

The generated workflow command owns:

- Step ordering.
- Input interpolation.
- Capturing JSON fields from step outputs.
- Structured partial-failure reporting.

## Generated Output

Codegen should emit workflow commands as static generated Go:

```text
internal/generated/workflows/workflows_gen.go
```

The generated package should mount workflow commands through `generated.Mount`
after generated API modules and before returning.

Workflow command names are reserved root command names. Codegen must reject
conflicts with:

- Lathe framework commands: `auth`, `commands`, `search`, `update`, `skill`,
and `__lathe`.
- Generated module names.
- Generated shortcuts.
- Other workflow commands.

## Catalog Contract

Workflow commands should be discoverable through the runtime catalog, but they
must not pretend to be single API operations.

Add a command kind:

```json
{
"kind": "workflow",
"path": ["doctor"],
"workflow": {
"version": 1,
"steps": [
{"id": "app", "operation": ["console", "apps", "app-detail"]},
{"id": "deployment", "operation": ["console", "apps", "app-deployment-status"]}
],
"rollback": false,
"branching": false
}
}
```

This requires a catalog schema bump. Operation commands can use
`"kind": "operation"` or omit the field only if the schema defines that as the
default.

The binary should also attach a capability:

```text
workflow.dsl
```

## Dry Run

Workflow commands should expose `--dry-run`.

Dry-run must not send HTTP requests. It should resolve and print the ordered
plan, including each step's operation path and resolved request shape when all
inputs are available.

For JSON output, dry-run should use a stable shape so CI and agents can validate
workflow wiring without touching a real API.

## Verification

`__lathe verify --json` should add a workflow contract check when workflows are
compiled in:

- Every workflow command is mounted.
- Every workflow command appears in catalog JSON as `kind=workflow`.
- Every referenced operation resolves.
- Every workflow command dry-run completes without network access.
- Root command conflicts were rejected at codegen time.

The verify report schema does not need to change unless the check result shape
changes.
2 changes: 1 addition & 1 deletion internal/codegen/render/skill.go
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ func renderModuleReference(manifest *config.Manifest, mod SkillModule, flat bool
b.WriteString("\n")
}
}
return b.String()
return strings.TrimRight(b.String(), "\n") + "\n"
}

func writeShortcuts(b *strings.Builder, cli string, spec runtime.CommandSpec) {
Expand Down