aworkflow 0.1.12__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.
- aworkflow-0.1.12/.github/workflows/publish-package.yml +100 -0
- aworkflow-0.1.12/.gitignore +8 -0
- aworkflow-0.1.12/ARCHITECTURE.md +249 -0
- aworkflow-0.1.12/LICENSE +21 -0
- aworkflow-0.1.12/PKG-INFO +477 -0
- aworkflow-0.1.12/README.md +467 -0
- aworkflow-0.1.12/aflow/AGENTS.md +6 -0
- aworkflow-0.1.12/aflow/__init__.py +1 -0
- aworkflow-0.1.12/aflow/__main__.py +7 -0
- aworkflow-0.1.12/aflow/aflow.toml +131 -0
- aworkflow-0.1.12/aflow/bundled_skills/aflow-execute-checkpoint/SKILL.md +108 -0
- aworkflow-0.1.12/aflow/bundled_skills/aflow-execute-plan/SKILL.md +106 -0
- aworkflow-0.1.12/aflow/bundled_skills/aflow-merge/SKILL.md +47 -0
- aworkflow-0.1.12/aflow/bundled_skills/aflow-plan/SKILL.md +251 -0
- aworkflow-0.1.12/aflow/bundled_skills/aflow-review-checkpoint/SKILL.md +111 -0
- aworkflow-0.1.12/aflow/bundled_skills/aflow-review-final/SKILL.md +111 -0
- aworkflow-0.1.12/aflow/bundled_skills/aflow-review-squash/SKILL.md +117 -0
- aworkflow-0.1.12/aflow/cli.py +556 -0
- aworkflow-0.1.12/aflow/config.py +829 -0
- aworkflow-0.1.12/aflow/git_status.py +237 -0
- aworkflow-0.1.12/aflow/harnesses/__init__.py +30 -0
- aworkflow-0.1.12/aflow/harnesses/base.py +32 -0
- aworkflow-0.1.12/aflow/harnesses/claude.py +46 -0
- aworkflow-0.1.12/aflow/harnesses/codex.py +42 -0
- aworkflow-0.1.12/aflow/harnesses/copilot.py +42 -0
- aworkflow-0.1.12/aflow/harnesses/gemini.py +42 -0
- aworkflow-0.1.12/aflow/harnesses/kiro.py +39 -0
- aworkflow-0.1.12/aflow/harnesses/opencode.py +42 -0
- aworkflow-0.1.12/aflow/harnesses/pi.py +43 -0
- aworkflow-0.1.12/aflow/plan.py +265 -0
- aworkflow-0.1.12/aflow/run_state.py +133 -0
- aworkflow-0.1.12/aflow/runlog.py +329 -0
- aworkflow-0.1.12/aflow/skill_installer.py +244 -0
- aworkflow-0.1.12/aflow/status.py +518 -0
- aworkflow-0.1.12/aflow/workflow.py +2038 -0
- aworkflow-0.1.12/aflow/workflows.toml +112 -0
- aworkflow-0.1.12/devlog/DEVLOG.md +89 -0
- aworkflow-0.1.12/plans/done/config-roles-teams-workflow-split-20260403-v1.md +346 -0
- aworkflow-0.1.12/plans/done/feature-branch-worktree-merge-20260403-v1.md +442 -0
- aworkflow-0.1.12/pyproject.toml +21 -0
- aworkflow-0.1.12/tests/test_aflow.py +6093 -0
- aworkflow-0.1.12/tests/test_skill_install.py +191 -0
- aworkflow-0.1.12/uv.lock +113 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: Publish package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- pyproject.toml
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
publish:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
id-token: write
|
|
20
|
+
environment:
|
|
21
|
+
name: pypi
|
|
22
|
+
url: https://pypi.org/p/aworkflow
|
|
23
|
+
steps:
|
|
24
|
+
- name: Check out repository
|
|
25
|
+
uses: actions/checkout@v4
|
|
26
|
+
with:
|
|
27
|
+
fetch-depth: 2
|
|
28
|
+
|
|
29
|
+
- name: Set up Python
|
|
30
|
+
uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.11"
|
|
33
|
+
|
|
34
|
+
- name: Set up uv
|
|
35
|
+
uses: astral-sh/setup-uv@v6
|
|
36
|
+
|
|
37
|
+
- name: Detect version bump
|
|
38
|
+
id: version
|
|
39
|
+
env:
|
|
40
|
+
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
|
41
|
+
run: |
|
|
42
|
+
python - <<'PY'
|
|
43
|
+
import os
|
|
44
|
+
import pathlib
|
|
45
|
+
import subprocess
|
|
46
|
+
import tomllib
|
|
47
|
+
|
|
48
|
+
data = tomllib.loads(pathlib.Path("pyproject.toml").read_text())
|
|
49
|
+
package_name = data["project"]["name"]
|
|
50
|
+
version = data["project"]["version"]
|
|
51
|
+
should_publish = os.environ["GITHUB_EVENT_NAME"] == "workflow_dispatch"
|
|
52
|
+
previous_version = ""
|
|
53
|
+
|
|
54
|
+
if not should_publish:
|
|
55
|
+
try:
|
|
56
|
+
previous_text = subprocess.check_output(
|
|
57
|
+
["git", "show", "HEAD^:pyproject.toml"],
|
|
58
|
+
text=True,
|
|
59
|
+
)
|
|
60
|
+
except subprocess.CalledProcessError:
|
|
61
|
+
should_publish = True
|
|
62
|
+
else:
|
|
63
|
+
previous_data = tomllib.loads(previous_text)
|
|
64
|
+
previous_version = previous_data["project"]["version"]
|
|
65
|
+
should_publish = previous_version != version
|
|
66
|
+
|
|
67
|
+
github_output = pathlib.Path(os.environ["GITHUB_OUTPUT"])
|
|
68
|
+
with github_output.open("a", encoding="utf-8") as handle:
|
|
69
|
+
handle.write(f"package_name={package_name}\n")
|
|
70
|
+
handle.write(f"version={version}\n")
|
|
71
|
+
handle.write(f"previous_version={previous_version}\n")
|
|
72
|
+
handle.write(
|
|
73
|
+
f"should_publish={'true' if should_publish else 'false'}\n"
|
|
74
|
+
)
|
|
75
|
+
PY
|
|
76
|
+
|
|
77
|
+
- name: Skip when version did not change
|
|
78
|
+
if: steps.version.outputs.should_publish != 'true'
|
|
79
|
+
run: |
|
|
80
|
+
echo "pyproject.toml changed without a version bump."
|
|
81
|
+
echo "Current version: ${{ steps.version.outputs.version }}"
|
|
82
|
+
echo "Previous version: ${{ steps.version.outputs.previous_version }}"
|
|
83
|
+
|
|
84
|
+
- name: Install dependencies
|
|
85
|
+
if: steps.version.outputs.should_publish == 'true'
|
|
86
|
+
run: uv sync --frozen
|
|
87
|
+
|
|
88
|
+
- name: Run tests
|
|
89
|
+
if: steps.version.outputs.should_publish == 'true'
|
|
90
|
+
run: uv run pytest -q
|
|
91
|
+
|
|
92
|
+
- name: Build distributions
|
|
93
|
+
if: steps.version.outputs.should_publish == 'true'
|
|
94
|
+
run: uv build
|
|
95
|
+
|
|
96
|
+
- name: Publish to PyPI
|
|
97
|
+
if: steps.version.outputs.should_publish == 'true'
|
|
98
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
99
|
+
with:
|
|
100
|
+
skip-existing: true
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
AFlow is a plan-driven workflow orchestrator that runs coding tasks through existing AI agent CLIs (Claude, Codex, Gemini, Kiro, OpenCode, Pi). It reads a checkpoint-based Markdown plan, dispatches steps to configurable harness profiles, evaluates condition-based transitions between steps, and logs every turn to disk.
|
|
4
|
+
|
|
5
|
+
## High-Level Data Flow
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart TD
|
|
9
|
+
User["User runs: aflow run [workflow] plan.md"]
|
|
10
|
+
CLI["cli.py — parse args, resolve repo root"]
|
|
11
|
+
Config["config.py — load & validate aflow.toml + workflows.toml"]
|
|
12
|
+
PlanParse["plan.py — parse Markdown plan into checkpoints"]
|
|
13
|
+
Backup["workflow.py — back up original plan to plans/backups/"]
|
|
14
|
+
WorkflowLoop["workflow.py — main turn loop"]
|
|
15
|
+
PromptRender["workflow.py — render_step_prompts()"]
|
|
16
|
+
Role["workflow.py — resolve_role_selector()"]
|
|
17
|
+
Adapter["harnesses/ — build CLI invocation"]
|
|
18
|
+
Subprocess["workflow.py — _run_process() via subprocess.Popen"]
|
|
19
|
+
PlanReload["plan.py — reload plan, compute post-snapshot"]
|
|
20
|
+
Transition["workflow.py — evaluate_condition() + pick_transition()"]
|
|
21
|
+
RunLog["runlog.py — write run metadata & turn artifacts"]
|
|
22
|
+
Banner["status.py — Rich live banner on stderr"]
|
|
23
|
+
|
|
24
|
+
User --> CLI
|
|
25
|
+
CLI --> Config
|
|
26
|
+
Config --> CLI
|
|
27
|
+
CLI --> PlanParse
|
|
28
|
+
CLI --> WorkflowLoop
|
|
29
|
+
WorkflowLoop --> PlanParse
|
|
30
|
+
PlanParse --> Backup
|
|
31
|
+
Backup --> WorkflowLoop
|
|
32
|
+
WorkflowLoop --> PromptRender
|
|
33
|
+
WorkflowLoop --> Role
|
|
34
|
+
Role --> Adapter
|
|
35
|
+
PromptRender --> Subprocess
|
|
36
|
+
Adapter --> Subprocess
|
|
37
|
+
Subprocess --> PlanReload
|
|
38
|
+
PlanReload --> Transition
|
|
39
|
+
Transition -->|"to = step_name"| WorkflowLoop
|
|
40
|
+
Transition -->|"to = END"| Done["Return result"]
|
|
41
|
+
WorkflowLoop --> RunLog
|
|
42
|
+
WorkflowLoop --> Banner
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Module Breakdown
|
|
46
|
+
|
|
47
|
+
### `cli.py`
|
|
48
|
+
Entry point. Exposes two subcommands:
|
|
49
|
+
- **`aflow run [workflow_name] plan.md [-- extra instructions]`** -- runs a workflow.
|
|
50
|
+
- **`aflow run [workflow_name] --start-step STEP_NAME plan.md [-- extra instructions]`** -- starts a workflow from a named step.
|
|
51
|
+
- **`aflow install-skills [destination]`** -- copies bundled skills into harness skill directories.
|
|
52
|
+
|
|
53
|
+
Resolves the repo root via `git rev-parse`, loads and validates the TOML config, resolves the selected workflow and optional team, and handles startup step selection or recovery before calling `run_workflow()`.
|
|
54
|
+
|
|
55
|
+
Startup resolution happens in `main()` before the workflow loop starts:
|
|
56
|
+
|
|
57
|
+
1. Ensure `~/.config/aflow/aflow.toml` and sibling `workflows.toml` exist. If either file was created, print both paths and exit so the user can edit them first.
|
|
58
|
+
2. Resolve config and workflow.
|
|
59
|
+
3. Load the original plan strictly.
|
|
60
|
+
4. If the plan is complete and `--start-step` was given, fail with a clear error.
|
|
61
|
+
5. If the plan is half-done and the workflow has more than one step, require a TTY and prompt for an explicit step unless `--start-step` was given.
|
|
62
|
+
6. If strict plan loading fails with `inconsistent_checkpoint_state`, require a TTY and ask whether to recover.
|
|
63
|
+
7. When recovery is accepted, load a tolerant snapshot from the invalid plan, seed startup retry state, and pass both the parsed plan and retry context into `run_workflow()`.
|
|
64
|
+
|
|
65
|
+
### `config.py`
|
|
66
|
+
Loads `~/.config/aflow/aflow.toml` plus sibling `workflows.toml` (bootstrapped from the bundled defaults on first run). Parses and validates:
|
|
67
|
+
- **`[aflow]`** section: `default_workflow`, `keep_runs`, `max_turns`, `retry_inconsistent_checkpoint_state`, `banner_files_limit`, `max_same_step_turns`, `team_lead`, `branch_prefix`, `worktree_prefix`, `worktree_root`.
|
|
68
|
+
- **`[harness.<name>.profiles.<profile>]`** tables: `model`, optional `effort` per harness profile.
|
|
69
|
+
- **`[roles]`** and **`[teams.<name>]`** tables: role-to-selector mappings, with team tables allowed to override a subset of the global map.
|
|
70
|
+
- **`[prompts]`** section: named prompt templates.
|
|
71
|
+
- Bare **`[workflow]`** table in `workflows.toml`: lifecycle defaults (`setup`, `teardown`, `main_branch`, `merge_prompt`) inherited by all workflows that don't override them. Not a runnable workflow.
|
|
72
|
+
- **`[workflow.<name>]`** tables in `workflows.toml`: concrete workflows define `steps`, alias workflows use `extends` and optional `team`. Both may override lifecycle defaults with `setup`, `teardown`, `main_branch`, and `merge_prompt`.
|
|
73
|
+
- **`[workflow.<name>.steps.<step>]`** tables: `role` (global role key), `prompts` (list of prompt keys), `go` (transition array with `to` and optional `when` condition).
|
|
74
|
+
|
|
75
|
+
Lifecycle validation enforces that `(setup, teardown)` is one of three accepted tuples: `([], [])`, `(["branch"], ["merge"])`, or `(["worktree", "branch"], ["merge", "rm_worktree"])`. Any other combination is rejected at load time with the exact workflow path.
|
|
76
|
+
|
|
77
|
+
When a workflow's effective `teardown` includes `merge`, validation also checks that `[aflow].team_lead` is set and, for config-defined teams, that the role can be resolved through team overrides or global `[roles]`.
|
|
78
|
+
|
|
79
|
+
Cross-validates that harness profiles, roles, teams, prompts, aliases, and transition targets all reference things that exist.
|
|
80
|
+
|
|
81
|
+
### `plan.py`
|
|
82
|
+
Parses a Markdown plan file into structured checkpoint data. Expects `### [x] Checkpoint ...` headings (h3 with checkbox) and `- [ ] step` items underneath. Produces a `PlanSnapshot` with:
|
|
83
|
+
- `current_checkpoint_name`, `current_checkpoint_index`
|
|
84
|
+
- `unchecked_checkpoint_count`, `current_checkpoint_unchecked_step_count`
|
|
85
|
+
- `is_complete` (all checkpoints checked)
|
|
86
|
+
- `total_checkpoint_count`
|
|
87
|
+
|
|
88
|
+
Also detects `## Git Tracking` sections required by review skills.
|
|
89
|
+
|
|
90
|
+
### `workflow.py`
|
|
91
|
+
The core engine. `run_workflow()` executes the turn loop:
|
|
92
|
+
|
|
93
|
+
1. Back up the original plan to `plans/backups/`.
|
|
94
|
+
2. Parse the plan unless `main()` already provided a pre-loaded `ParsedPlan`.
|
|
95
|
+
3. If the workflow's `setup` is non-empty, run lifecycle preflight checks (branch name collision, worktree path collision, clean working tree, correct startup branch, `main_branch` ref exists locally) and then create the execution environment. Branch-only setup creates a local feature branch from `main_branch` in the primary checkout. Worktree setup creates a linked worktree from `main_branch` under `worktree_root` and creates the feature branch inside that worktree. The primary checkout remains the control root for run artifacts; the worktree is the execution root for normal steps.
|
|
96
|
+
4. For each turn (up to `max_turns`):
|
|
97
|
+
a. Reload the plan from disk (the agent may have modified it). For worktree flows, plan path placeholders (`{ORIGINAL_PLAN_PATH}`, `{ACTIVE_PLAN_PATH}`, `{NEW_PLAN_PATH}`) are translated from primary-root-relative to worktree-root-relative before being handed to the agent; they are translated back after the turn.
|
|
98
|
+
b. Resolve the step's role through the selected team and global role map to get the concrete harness selector.
|
|
99
|
+
c. Render prompt templates with path placeholders.
|
|
100
|
+
d. Build a `HarnessInvocation` via the adapter, using `execution_repo_root` as the subprocess cwd.
|
|
101
|
+
e. Run the agent CLI as a subprocess, streaming stdout/stderr.
|
|
102
|
+
f. Before reloading the plan, scan stdout and stderr for a line starting with `AFLOW_STOP:`. If found, fail the run immediately with the extracted reason without entering the plan-reload or transition path.
|
|
103
|
+
g. Reload the plan again to get the post-turn snapshot. If the plan is left in an inconsistent checkpoint state (heading marked complete but unchecked steps remain) and the harness exited cleanly, a retry may be scheduled instead of failing immediately (see `retry_inconsistent_checkpoint_state`).
|
|
104
|
+
h. Evaluate `go` transitions using condition symbols (`DONE`, `NEW_PLAN_EXISTS`, `MAX_TURNS_REACHED`).
|
|
105
|
+
i. Log turn artifacts and update run metadata.
|
|
106
|
+
j. If transition target is `END`, return. For multi-step workflows, check the same-step cap: if the same step has been selected consecutively `max_same_step_turns` times, fail the run before starting the next turn. Otherwise, advance to the next step.
|
|
107
|
+
5. After normal workflow completion, if `teardown` includes `merge`, execute the merge handoff: resolve `[aflow].team_lead` through the effective team, build a merge prompt (built-in `aflow-merge` instruction plus rendered `merge_prompt` entries), and run the `team_lead` agent from the primary checkout. After the agent returns, verify: no unmerged index entries, clean working tree, HEAD on `main_branch`, and feature branch is an ancestor of the target. Only after all checks pass does `rm_worktree` (if configured) remove the linked worktree. Any verification failure leaves the feature branch and worktree intact and fails the run with the specific failed check.
|
|
108
|
+
|
|
109
|
+
A scheduled retry skips the pre-turn plan reload and reuses the last valid snapshot and saved prompt context. The same `ACTIVE_PLAN_PATH`, `NEW_PLAN_PATH`, and resolved step selector are reused; the retry appendix (containing the exact parse error) is added to the prompt. Startup recovery seeds that same retry machinery by passing a `RetryContext` into `run_workflow()`, which stores the step name, role, resolved selector, and prompt context in `state.pending_retry` before turn 1. Retry turns still count toward `max_turns`.
|
|
110
|
+
|
|
111
|
+
The condition evaluator is a full recursive-descent parser supporting `&&`, `||`, `!`, and parentheses over the three condition symbols.
|
|
112
|
+
|
|
113
|
+
Prompt templates support `file://` references (absolute, config-relative, or cwd-relative).
|
|
114
|
+
|
|
115
|
+
### `harnesses/`
|
|
116
|
+
Adapter layer. Each harness implements `HarnessAdapter.build_invocation()` to produce a `HarnessInvocation` (argv, env, prompt texts). Seven adapters:
|
|
117
|
+
|
|
118
|
+
| Harness | CLI binary | Prompt mode | Effort support |
|
|
119
|
+
|------------|-------------|--------------------------------|----------------|
|
|
120
|
+
| `claude` | `claude` | `--system-prompt` flag | Yes |
|
|
121
|
+
| `codex` | `codex` | system prefixed into user prompt | Yes |
|
|
122
|
+
| `copilot` | `copilot` | system prefixed into user prompt | Yes |
|
|
123
|
+
| `gemini` | `gemini` | system prefixed into user prompt | No |
|
|
124
|
+
| `kiro` | `kiro-cli` | system prefixed into user prompt | No |
|
|
125
|
+
| `opencode` | `opencode` | system prefixed into user prompt | No |
|
|
126
|
+
| `pi` | `pi` | `--system-prompt` flag | Yes |
|
|
127
|
+
|
|
128
|
+
All harnesses run in non-interactive, auto-approve mode with full tool access.
|
|
129
|
+
|
|
130
|
+
### `run_state.py`
|
|
131
|
+
Data classes for runtime state:
|
|
132
|
+
- `ControllerConfig` -- immutable run parameters (repo root, plan path, max turns, keep runs, extra instructions).
|
|
133
|
+
- `ControllerState` -- mutable per-run state (snapshot, turn count, issues, timing, status, pending retry context, consecutive same-step streak tracking).
|
|
134
|
+
- `RetryContext` -- frozen dataclass holding everything needed to rerun the same step on the next turn without re-parsing the broken plan (step name, role, resolved selector, pre-failure snapshot, saved plan paths, base prompt, parse error string, attempt counter, retry limit).
|
|
135
|
+
- `ExecutionContext` -- frozen dataclass holding lifecycle execution state: `primary_repo_root`, `execution_repo_root` (worktree path for worktree flows, same as primary for branch-only), `main_branch`, `feature_branch`, `worktree_path` (or `None` for branch-only), `setup`, `teardown`.
|
|
136
|
+
- `ControllerConfig` also carries the selected startup step, if any, so the workflow loop can start from a non-default step without re-parsing CLI arguments.
|
|
137
|
+
- `ControllerRunResult` -- final result with end reason.
|
|
138
|
+
- `WorkflowEndReason` -- literal type: `already_complete`, `done`, `max_turns_reached`, `transition_end`.
|
|
139
|
+
|
|
140
|
+
### `runlog.py`
|
|
141
|
+
Persists run data under `.aflow/runs/<timestamp>-<uuid>/`:
|
|
142
|
+
- `run.json` -- run-level metadata, updated after each turn.
|
|
143
|
+
- `turns/turn-NNN/` -- per-turn artifacts: `system-prompt.txt`, `user-prompt.txt`, `effective-prompt.txt`, `argv.json`, `env.json`, `stdout.txt`, `stderr.txt`, `result.json`.
|
|
144
|
+
|
|
145
|
+
Prunes old run directories to respect `keep_runs`.
|
|
146
|
+
|
|
147
|
+
### `git_status.py`
|
|
148
|
+
Git snapshot helpers used by the banner and CLI. Provides three public data classes (`GitBaseline`, `GitSummary`, `WorktreeProbe`) and three functions:
|
|
149
|
+
- `probe_worktree(repo_root)` — checks whether the working tree is dirty at startup.
|
|
150
|
+
- `capture_baseline(repo_root)` — snapshots the current HEAD SHA and a working-tree tree OID (using a temporary `GIT_INDEX_FILE`) as a before-run baseline.
|
|
151
|
+
- `summarize_since_baseline(repo_root, baseline)` — compares the current working tree against the baseline and returns file-change counts, net line deltas, commit count, and changed paths.
|
|
152
|
+
|
|
153
|
+
All three functions return `None` when git is unavailable or fails, so the workflow always runs regardless of git state.
|
|
154
|
+
|
|
155
|
+
### `status.py`
|
|
156
|
+
Rich-based live banner rendered to stderr during a run. Shows elapsed time, workflow/step name, harness, model, checkpoint progress, turn count, issues, plan paths, git summary (if available), and status.
|
|
157
|
+
|
|
158
|
+
`BannerRenderer` owns a background daemon thread that rebuilds and pushes the panel every `refresh_interval_seconds` (default 1 s) and polls for a new `GitSummary` every `git_poll_interval_seconds` (default 10 s). This keeps the elapsed timer alive between step transitions without requiring external pushes. `set_context(...)` is used to update mutable banner fields instead of directly writing private attributes.
|
|
159
|
+
|
|
160
|
+
### `skill_installer.py`
|
|
161
|
+
Discovers the seven bundled skills from package resources and copies them into harness-specific skill directories. Supports auto-detection (looks for harness CLIs on PATH) and manual mode (explicit destination path). Handles duplicate destinations when multiple harnesses share a path (e.g., codex, copilot, gemini, and pi all use `~/.agents/skills`).
|
|
162
|
+
|
|
163
|
+
### `bundled_skills/`
|
|
164
|
+
Seven Markdown-based skill definitions installed into harness skill directories:
|
|
165
|
+
|
|
166
|
+
| Skill | Purpose |
|
|
167
|
+
|-----------------------------|----------------------------------------------------------------|
|
|
168
|
+
| `aflow-plan` | Create a checkpoint handoff plan |
|
|
169
|
+
| `aflow-execute-plan` | Execute an entire plan autonomously, checkpoint by checkpoint |
|
|
170
|
+
| `aflow-execute-checkpoint` | Execute exactly one checkpoint, then stop |
|
|
171
|
+
| `aflow-review-squash` | Review completed work; approve+squash or create fix plan |
|
|
172
|
+
| `aflow-review-checkpoint` | Review one checkpoint; approve or create fix plan |
|
|
173
|
+
| `aflow-review-final` | Final review without squash; approve or create follow-up plan |
|
|
174
|
+
| `aflow-merge` | Local-only merge handoff; preserves commits, resolves conflicts, emits `AFLOW_STOP:` for irrecoverable states |
|
|
175
|
+
|
|
176
|
+
## Workflow Configuration
|
|
177
|
+
|
|
178
|
+
Workflows are state machines defined in `workflows.toml`. Each step has:
|
|
179
|
+
- A `role` key that resolves through the selected team and then global `[roles]`.
|
|
180
|
+
- A `prompts` list referencing named prompt templates.
|
|
181
|
+
- A `go` array of transitions, each with a `to` target (step name or `END`) and an optional `when` condition expression.
|
|
182
|
+
|
|
183
|
+
Workflow tables can also use `extends` to alias a concrete base workflow and `team` to override the team for that alias. In v1, aliases inherit the base workflow's steps and cannot redefine them.
|
|
184
|
+
|
|
185
|
+
Bare `[workflow]` in `workflows.toml` is a lifecycle defaults table. It supplies `setup`, `teardown`, `main_branch`, and `merge_prompt` values that all concrete workflows and aliases inherit unless they override them individually. It is not a runnable workflow.
|
|
186
|
+
|
|
187
|
+
Lifecycle config controls the git environment created before workflow steps begin and torn down after normal completion. The accepted `(setup, teardown)` pairs are:
|
|
188
|
+
- `([], [])` — no lifecycle, engine behaves exactly as before
|
|
189
|
+
- `(["branch"], ["merge"])` — create a local feature branch, run steps there, then invoke merge handoff
|
|
190
|
+
- `(["worktree", "branch"], ["merge", "rm_worktree"])` — create a linked worktree from `main_branch`, run steps in that worktree, invoke merge handoff from the primary checkout, then remove the worktree after verified merge
|
|
191
|
+
|
|
192
|
+
Merge is model-driven through the `aflow-merge` skill. The engine resolves the `team_lead` role, prepends the built-in `aflow-merge` instruction, appends rendered `merge_prompt` entries, and runs the agent from the primary checkout. After the agent returns, the engine verifies merge success before removing any worktree.
|
|
193
|
+
|
|
194
|
+
Transitions are evaluated top-to-bottom; the first match wins. An entry without `when` is an unconditional fallback.
|
|
195
|
+
|
|
196
|
+
The built-in workflow diagrams live in the README so the default workflow shapes are visible in the main docs without sending readers into the architecture reference first.
|
|
197
|
+
|
|
198
|
+
## Directory Layout
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
aflow/
|
|
202
|
+
__main__.py # entrypoint
|
|
203
|
+
cli.py # argument parsing, main(), dirty-worktree gate
|
|
204
|
+
config.py # TOML config loading and validation
|
|
205
|
+
plan.py # Markdown plan parser
|
|
206
|
+
workflow.py # workflow engine (turn loop, conditions, transitions)
|
|
207
|
+
run_state.py # runtime data classes
|
|
208
|
+
runlog.py # run/turn artifact persistence
|
|
209
|
+
status.py # Rich live banner with background refresh thread
|
|
210
|
+
git_status.py # git snapshot helpers (probe, baseline, summary)
|
|
211
|
+
skill_installer.py # bundled skill installer
|
|
212
|
+
aflow.toml # global config, harness profiles, roles, teams, prompts
|
|
213
|
+
workflows.toml # workflow definitions and aliases
|
|
214
|
+
harnesses/
|
|
215
|
+
__init__.py # adapter registry (ADAPTERS dict)
|
|
216
|
+
base.py # HarnessAdapter protocol, HarnessInvocation dataclass
|
|
217
|
+
claude.py # Claude Code adapter
|
|
218
|
+
codex.py # Codex adapter
|
|
219
|
+
gemini.py # Gemini adapter
|
|
220
|
+
kiro.py # Kiro adapter
|
|
221
|
+
opencode.py # OpenCode adapter
|
|
222
|
+
pi.py # Pi adapter
|
|
223
|
+
bundled_skills/
|
|
224
|
+
aflow-plan/ SKILL.md
|
|
225
|
+
aflow-execute-plan/ SKILL.md
|
|
226
|
+
aflow-execute-checkpoint/ SKILL.md
|
|
227
|
+
aflow-review-squash/ SKILL.md
|
|
228
|
+
aflow-review-checkpoint/ SKILL.md
|
|
229
|
+
aflow-review-final/ SKILL.md
|
|
230
|
+
aflow-merge/ SKILL.md
|
|
231
|
+
tests/
|
|
232
|
+
test_aflow.py # workflow engine tests
|
|
233
|
+
test_skill_install.py # skill installer tests
|
|
234
|
+
plans/ # user plan files and backups
|
|
235
|
+
backups/ # automatic plan backups
|
|
236
|
+
in-progress/ # active handoff plans
|
|
237
|
+
.aflow/
|
|
238
|
+
runs/ # per-run logs (gitignored)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Key Design Decisions
|
|
242
|
+
|
|
243
|
+
- **Plan as source of truth.** The Markdown plan file on disk is authoritative. The engine re-reads it before and after every turn because the agent subprocess may modify it (checking off steps/checkpoints).
|
|
244
|
+
- **Harness-agnostic.** The engine doesn't know how any specific agent CLI works. Adapters translate a uniform interface into CLI-specific argv/env. Adding a new harness means one ~30-line adapter file.
|
|
245
|
+
- **Interactive startup decisions live in the CLI.** Step picking and startup recovery happen before `run_workflow()` so the workflow loop only deals with an already-chosen step and, when needed, a seeded retry context.
|
|
246
|
+
- **Condition-based transitions.** Step transitions use a small expression language over three boolean symbols rather than hardcoded control flow. This keeps workflow definitions declarative.
|
|
247
|
+
- **Structured run logging.** Every turn's prompts, outputs, and snapshots are persisted to `.aflow/runs/` for debugging and auditability. Old runs are pruned automatically.
|
|
248
|
+
- **Skills as Markdown.** The seven bundled skills are plain SKILL.md files that get copied into each harness's skill directory. They contain behavioral instructions that the agent reads at runtime, not executable code.
|
|
249
|
+
- **Local-only lifecycle.** Branch and worktree creation, feature branch setup, and merge handoff all operate on local refs only. The engine never fetches, pulls, or pushes. The primary checkout is the control root for run artifacts and merge verification even when normal steps execute inside a linked worktree.
|
aworkflow-0.1.12/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Evren Ozkan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|