Tools / awf-cli
awf-cli
awf is a command-line tool for OpenAWF documents, licensed MIT. It validates, seals, bundles, renders, installs and exports workflows and tasks. It supports spec version 0.1.0.
Install
$ go install github.com/dwwescalelol/awf-cli/cmd/awf@latest
awf <command> --help lists every flag. Source: github.com/dwwescalelol/awf-cli.
Quick start
$ awf init # create .awf in this directory $ awf new workflow feat-dev # write the draft $ $EDITOR .awf/_/workflow/feat-dev/draft.yaml # add version: 0.1.0 $ awf validate workflow feat-dev $ awf seal workflow feat-dev # draft.yaml becomes 0.1.0.yaml $ awf render workflow feat-dev@0.1.0 $ awf export workflow feat-dev@0.1.0 ./feat-dev.yaml
Documents
A workflow is a .yaml file with a top-level openawf key. A task is a .md file whose YAML frontmatter has an openawf key, with the task's instructions in the Markdown body.
Scope
A scope is an .awf directory. Each command finds one scope.
- The project scope is the nearest
.awfdirectory above the working directory. The search stops at the home directory. - The global scope is
$AWF_HOME, or~/.awfwhen it is unset. --globalselects the global scope.- With no project scope found, the global scope is used.
A path is in a scope when it lies inside that .awf directory. A path next to .awf, such as the project root, is outside it.
Layout
.awf/ <namespace>/workflow/<id>/<version>.yaml <namespace>/task/<id>/<version>.md _/workflow/<id>/draft.yaml _/task/<id>/draft.md
Versions of one id sit side by side, so a workflow can pin any task version that exists.
Namespaces
A namespace is the first part of a document's path in a scope.
_is the local namespace. It holds your own documents and is the only writable namespace.- Every other namespace is foreign and read-only. A foreign namespace is named after the git repository its documents came from, such as
example.com/team/flows.
An id names a local document as <id>, and a foreign one as <namespace>#<id>. install is the only command that writes into a foreign namespace.
Drafts and versions
A document in a scope is a draft or a version.
Draft
A draft is an unsealed document you edit.
- Its file is
draft.yamlordraft.md, and it exists only in_. - Each id has at most one draft.
- It has no sha.
newandimportwrite it with no version. - It may hold
$reftasks. A draft is a source, not a bundle: its tasks can live in other files, so one draft can span many files.
Version
A version is a sealed document.
- Its file is
<major.minor.patch>.yamlor.md. - It has a sha that matches its content.
- Its tasks are inline. Sealing a workflow inlines every
$ref. - It never changes. A change requires a new draft.
Sealing
seal is the only way to turn a draft into a version. It reads the version written in the draft, requires it to be newer than the latest version, writes the sha, and moves the draft to that version's file.
$ awf seal workflow feat-dev .awf/_/workflow/feat-dev/0.1.0.yaml sealed sha256-55ddf384c2ec93f0b2617e35b0b5ceb12b3f5514294e60e5b2a985135fa23891
File names
Every command that loads a document from a scope applies these rules, including validate, seal, bundle and the language server.
- A file named other than
<major.minor.patch>ordraftis an error:a document in an awf scope is named <major.minor.patch> or draft. - An unsealed version file is an error:
an unsealed document in an awf scope is a draft, name it draft or seal it.
Files outside a scope have no naming rule.
Outside a scope
A document outside a scope is a sealed, bundled document. The CLI never edits one.
validate -fandrender -fread a file anywhere.import -fcopies a file into_as a draft.exportwrites a sealed document to a file outside every scope.
To keep a workflow next to your code, write and seal it in your scope, then export it.
The CLI defines intended use, and the file system cannot enforce it. Nothing stops an unbundled or unsealed file from sitting outside a scope, or a sealed version from being edited and re-hashed by hand. Neither is correct use. awf offers no command that produces either, and validate reports a sha that does not match its content.
Targets
A command that reads a document takes a target: [<namespace>#]<id>[@<version>] in the scope, or -f <path> for a file.
- An unpinned
<id>resolves to its draft, or to its latest version when it has no draft. - A target that is not an id fails with
not an id, use -f for a file path.
A command that acts on a draft takes <id> only. It refuses @<version>, <namespace># and -f.
References
A $ref points a workflow task at a task document. Only drafts hold $ref, so references exist only in the local namespace _. The task a reference points at can live anywhere: another file in the project, or a version in any namespace.
- A path, absolute or relative to the directory that holds the scope.
- An id pinned to a version:
[<namespace>#]<id>@<version>. A bare<id>means_.
tasks: review: $ref: tasks/review.md # a path deploy: $ref: example.com/team/flows#deploy@2.0.0 # a pinned id
Sealing a workflow refuses unsealed $ref tasks, then inlines them all.
Commands
Commands that act on a document take its kind, then a target: awf <command> <kind> <target>. The kind is workflow or task.
| Command | Reads | Writes |
|---|---|---|
init | A new project scope. Refuses inside an existing one. | |
new <kind> <id> | The latest version of <id>, if any. | The draft of <id> in _. A new draft of an existing id copies its latest version. |
import <kind> <target> [<id>] | Any target, any namespace, or -f. | The draft of <id> in _, or of the source's own id. Clears version and sha, inlines $ref. Refuses an existing draft. |
seal <kind> <id> | The draft of <id> in _. | The sealed version file. Removes the draft. |
bundle workflow <id> | The draft of <id> in _. | Stdout, or the file given by -o. Inlines $ref. |
export <kind> <target> <path> | A sealed target, any namespace, or -f. | <path>, a file outside every scope. Refuses a draft, an unsealed source, an existing path, and an extension that does not match the kind. --format claude writes a Claude Code plugin to the directory <path>. |
install <namespace>#<id>[@<version>] | A sealed document in a git repository. A document's URL also works; -b picks a branch. | A version in that repository's foreign namespace. |
validate <kind> <target> | Any target. | Nothing. |
render <kind> <target> | Any target. | A temporary HTML page, opened in the browser. --source prints it. |
ls [<kind>] | The scope. | Nothing. |
ui | The scope. | Nothing. Browses the scope's workflows in the browser. |
lsp | Open editor buffers. | Nothing. Runs the language server over stdio. |
The boundary
Inside a scope, only drafts in _ change. Every version, in every namespace, is sealed and fixed. Outside a scope, every document is sealed. import crosses the boundary inward as a draft. export crosses it outward as a sealed file.
Editors
awf lsp gives editors diagnostics, go-to-definition on $ref, and a live preview of the rendered page. The VS Code extension and Neovim setup are in editors/.
The server handles workflows (.yaml files with a top-level openawf key) and tasks (.md files whose frontmatter has an openawf key). It ignores every other file.