oh-my-workflow 0.2.0 → 0.4.0
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.
- package/README.md +191 -106
- package/conformance/budget-loop.ts +16 -0
- package/conformance/fanout.ts +20 -0
- package/conformance/pipeline.ts +21 -0
- package/conformance/schema-gate.ts +21 -0
- package/conformance/strict-throws.ts +13 -0
- package/docs/launch/show-hn.md +41 -0
- package/docs/site/index.html +540 -0
- package/docs/site/robots.txt +2 -0
- package/examples/deep-research/workflow.ts +11 -11
- package/package.json +11 -3
- package/scripts/build-docs.ts +10 -0
- package/scripts/check-docs.ts +58 -0
- package/skill/SKILL.md +247 -137
- package/src/adapters/claude.ts +31 -5
- package/src/adapters/codex.ts +5 -3
- package/src/adapters/exec.ts +103 -0
- package/src/adapters/fake.ts +4 -4
- package/src/adapters/hermes.ts +24 -0
- package/src/adapters/types.ts +33 -3
- package/src/cli/codemod.ts +99 -0
- package/src/cli/omw.ts +7 -2
- package/src/cli/run.ts +222 -13
- package/src/cli/skill.ts +32 -10
- package/src/runtime.ts +171 -11
- package/src/worktree.ts +72 -0
- package/vercel.json +5 -0
package/README.md
CHANGED
|
@@ -1,115 +1,184 @@
|
|
|
1
1
|
# oh-my-workflow
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
8
|
-
> your script.
|
|
3
|
+
> **Workflow mode for coding agents** — add `/omw`, describe a multi-step job,
|
|
4
|
+
> and let your agent write and run the workflow. Under the hood it is the open,
|
|
5
|
+
> portable twin of Claude Code's native dynamic Workflow: a plain-JS script whose
|
|
6
|
+
> nodes are whole coding-agent CLIs (`claude -p`, `codex exec`). omw is the thin
|
|
7
|
+
> glue: schema gates, bounded concurrency, repair, resume, and a JSONL journal
|
|
8
|
+
> your authoring agent can read to fix its own script.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Quickstart — add `/omw` to your coding agent
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
|
|
14
|
-
bun install
|
|
15
|
-
bun src/cli/omw.ts run examples/deep-research --agent fake
|
|
13
|
+
npx skills add domuk-k/oh-my-workflow --skill omw
|
|
16
14
|
```
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
Then ask from Claude Code, Codex, opencode, or any agent that reads skills:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
/omw write a workflow that reviews this repo, fans out three implementation plans,
|
|
20
|
+
verifies them independently, and returns the best one with evidence.
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
That
|
|
23
|
+
That is the intended onboarding: install one skill, then ask your own coding
|
|
24
|
+
agent to author and run the workflow. The skill teaches it to create a plain-JS
|
|
25
|
+
workflow file, run `omw run`, read `.omw/<run>.jsonl`, and repair the script from
|
|
26
|
+
structured failures.
|
|
27
|
+
|
|
28
|
+
Today the same skill is bundled in the package:
|
|
23
29
|
|
|
24
30
|
```sh
|
|
25
|
-
|
|
31
|
+
bunx github:domuk-k/oh-my-workflow skill install # Claude Code
|
|
32
|
+
bunx github:domuk-k/oh-my-workflow skill install --codex # Codex
|
|
33
|
+
bunx github:domuk-k/oh-my-workflow skill install --opencode
|
|
26
34
|
```
|
|
27
35
|
|
|
36
|
+
Prefer `npx skills add domuk-k/oh-my-workflow --skill omw` for the shared skills
|
|
37
|
+
CLI; use `bunx github:domuk-k/oh-my-workflow skill install` when you want the
|
|
38
|
+
bundled copy directly from the repo.
|
|
39
|
+
|
|
40
|
+
## Try the runtime without a live agent
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
bunx github:domuk-k/oh-my-workflow run examples/deep-research --agent fake --pretty
|
|
28
44
|
```
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
|
|
46
|
+
`--agent fake` is a built-in deterministic adapter: no API key, no network, no
|
|
47
|
+
cost. It exercises the same spine the skill asks your agent to use for real work:
|
|
48
|
+
fan-out, pipeline, schema-fail to self-repair, timeout to `null`, and one final
|
|
49
|
+
result JSON.
|
|
50
|
+
|
|
51
|
+
## The twin framing
|
|
52
|
+
|
|
53
|
+
Claude Code has a built-in **dynamic Workflow** tool: when a task is big enough,
|
|
54
|
+
the model writes a deterministic JS orchestration script on the fly and the
|
|
55
|
+
harness runs it — `agent()` to spawn subagents, `parallel`/`pipeline` to fan out,
|
|
56
|
+
`budget` to bound the run, `workflow()` to nest. That surface is excellent and
|
|
57
|
+
**closed** (it only runs inside Claude Code, and its nodes are in-harness
|
|
58
|
+
subagents).
|
|
59
|
+
|
|
60
|
+
**omw is the open twin of that surface.** Same authoring shape, same vocabulary —
|
|
61
|
+
but the nodes are *whole external coding-agent CLIs* you already have, it runs
|
|
62
|
+
from any host (Claude Code, Codex, opencode, a cron job), and the whole thing is
|
|
63
|
+
a few hundred lines of standard TypeScript you can read.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// native dynamic Workflow — inside Claude Code
|
|
67
|
+
export default async function ({ agent, parallel }) {
|
|
68
|
+
const found = await parallel(topics.map((t) => () => agent(`research ${t}`)));
|
|
69
|
+
return { found };
|
|
70
|
+
}
|
|
49
71
|
```
|
|
50
72
|
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
```ts
|
|
74
|
+
// omw — anywhere, same shape
|
|
75
|
+
export default async function ({ agent, parallel }, args) {
|
|
76
|
+
const found = await parallel(topics.map((t) => () => agent(`research ${t}`)));
|
|
77
|
+
return { found: found.filter(Boolean) };
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The script is nearly the same; what differs is what a node *is* and where it runs.
|
|
53
82
|
|
|
54
|
-
|
|
55
|
-
stranger runs the full fan-out + pipeline + a scripted schema-fail→self-repair +
|
|
56
|
-
a scripted timeout→drop, and gets a stable result JSON. Swap `--agent claude`
|
|
57
|
-
(after `claude login`) to run it for real.
|
|
83
|
+
## Why this matters now
|
|
58
84
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
Coding agents have crossed from chat surfaces into everyday CLIs. Claude Code,
|
|
86
|
+
Codex, opencode-style hosts, and smaller agent CLIs can all run meaningful work
|
|
87
|
+
from a shell, but multi-agent jobs still need the same missing glue: fan-out,
|
|
88
|
+
schema validation, retries, bounded concurrency, run journals, resume, and a
|
|
89
|
+
single result for the caller.
|
|
90
|
+
|
|
91
|
+
omw packages that glue without claiming to be a new agent framework. A node is a
|
|
92
|
+
whole external coding-agent CLI. The workflow is plain JavaScript. The journal is
|
|
93
|
+
JSONL you can inspect. That makes the pattern portable enough for a local script,
|
|
94
|
+
a CI job, another coding agent, or a launch demo someone can try in 30 seconds
|
|
95
|
+
with no key.
|
|
62
96
|
|
|
63
97
|
## What it is
|
|
64
98
|
|
|
65
|
-
You write a plain-JS orchestration script. Its
|
|
66
|
-
|
|
67
|
-
|
|
99
|
+
You write a plain-JS orchestration script. Its default export takes the **hooks**
|
|
100
|
+
as a destructured first arg and your `args` as the second — exactly the native
|
|
101
|
+
shape:
|
|
68
102
|
|
|
69
103
|
```ts
|
|
70
|
-
export
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
104
|
+
export const meta = { name: "research", phases: [{ title: "Search" }] };
|
|
105
|
+
|
|
106
|
+
export default async function ({ agent, parallel, phase, budget }, args) {
|
|
107
|
+
phase("Search");
|
|
108
|
+
const hits = (await parallel(
|
|
109
|
+
args.queries.map((q) => () => agent(`SEARCH: ${q}`, { schema: HIT, label: q })),
|
|
74
110
|
)).filter(Boolean); // agent() returns null on failure, never throws
|
|
75
111
|
return { hits, count: hits.length };
|
|
76
112
|
}
|
|
77
113
|
```
|
|
78
114
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
115
|
+
The hooks — destructure only what you use:
|
|
116
|
+
|
|
117
|
+
- **`agent(prompt, opts?)`** — run one coding-agent CLI node. With a `schema`, omw
|
|
118
|
+
extracts JSON, validates it (ajv), and **re-prompts the node with the validation
|
|
119
|
+
errors** up to 2 times before giving up. Returns the validated object, or
|
|
120
|
+
`null`. **Never throws** — the load-bearing *null-contract* (the one exception
|
|
121
|
+
is `budget` exhaustion; see below). `opts`: `schema`, `model`, `label`, `phase`,
|
|
122
|
+
`effort`, `agentType`, `isolation: 'worktree'`, `cwd`, `inheritMcp`, `timeoutMs`.
|
|
123
|
+
- **`parallel(thunks)`** — concurrent, barrier; failures become `null`.
|
|
124
|
+
- **`pipeline(items, …stages)`** — each item flows through all stages independently.
|
|
125
|
+
- **`workflow(ref, args?)`** — run another workflow inline as a sub-step (one level
|
|
126
|
+
deep), sharing the adapter, journal, and budget pool.
|
|
127
|
+
- **`budget`** — `{ total, spent(), remaining() }`. Set a ceiling with `--budget N`;
|
|
128
|
+
once spent reaches it, `agent()` throws `BudgetExceededError` (the documented
|
|
129
|
+
null-contract exception) so a bounded loop terminates instead of spinning.
|
|
130
|
+
- **`phase(title)` / `log(msg)`** — journal / `--pretty` side-channel only.
|
|
131
|
+
|
|
132
|
+
`export const meta` is optional — `{ name, description, whenToUse, model, phases }`,
|
|
133
|
+
matching native. A per-phase or default `model` resolves along
|
|
134
|
+
`opts.model > phase model > meta.model`.
|
|
86
135
|
|
|
87
136
|
Concurrency is bounded at the `agent()` boundary (default 4, `--concurrency N`).
|
|
88
|
-
Every step is recorded to
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
137
|
+
Every step is recorded to `.omw/<runId>.jsonl`, so when a node fails you read the
|
|
138
|
+
`kind` (`timeout` / `nonzero_exit` / `refusal` / `schema_violation` / …) and fix
|
|
139
|
+
your script. stdout is one result JSON; the `--pretty` tree and a `journal: <path>`
|
|
140
|
+
pointer go to stderr.
|
|
92
141
|
|
|
93
142
|
**The full agent-facing guide is [`skill/SKILL.md`](skill/SKILL.md)** — patterns
|
|
94
143
|
(fan-out / verify-vote / pipeline / loop-until-dry), the debug loop, and the
|
|
95
144
|
conventions. That skill is the primary product; this README is the human intro.
|
|
96
145
|
|
|
146
|
+
## No magic (the differentiator)
|
|
147
|
+
|
|
148
|
+
The native tool can lean on the harness; an open twin has to earn portability.
|
|
149
|
+
omw's deliberate choice: **be boring standard JS.**
|
|
150
|
+
|
|
151
|
+
- **No source transform.** Your script is run as-is (`await import`), not rewritten.
|
|
152
|
+
What you write is what executes.
|
|
153
|
+
- **No ambient globals.** Hooks arrive as a destructured argument, not injected
|
|
154
|
+
into scope. No hidden `agent` global to explain.
|
|
155
|
+
- **No sandbox by default.** Determinism is a *convention you keep*; opt into
|
|
156
|
+
enforcement with `--strict` (freezes `Date`/`Math.random` to throw) when you
|
|
157
|
+
want a reproducible run.
|
|
158
|
+
|
|
159
|
+
That's why the same script reads cleanly in a README, a repo, or another agent's
|
|
160
|
+
context — there's nothing non-standard to carry along.
|
|
161
|
+
|
|
97
162
|
## Install the skill (the primary product)
|
|
98
163
|
|
|
99
164
|
omw's primary product is an **agent-authoring skill** (`skill/SKILL.md`) — it
|
|
100
|
-
teaches a coding agent to write, run, and repair omw workflows.
|
|
101
|
-
is installed, wire the skill into your agent in one step:
|
|
165
|
+
teaches a coding agent to write, run, and repair omw workflows. The short path is:
|
|
102
166
|
|
|
103
167
|
```sh
|
|
104
|
-
|
|
105
|
-
omw skill install --project # → ./.claude/skills/oh-my-workflow (this repo only)
|
|
106
|
-
omw skill path # print the bundled SKILL.md path (cat / pipe / point an agent at it)
|
|
168
|
+
npx skills add domuk-k/oh-my-workflow --skill omw
|
|
107
169
|
```
|
|
108
170
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
171
|
+
For hosts that do not yet read that registry, install the bundled copy:
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
bunx github:domuk-k/oh-my-workflow skill install # → ~/.claude/skills/omw
|
|
175
|
+
bunx github:domuk-k/oh-my-workflow skill install --codex # → ~/.codex/skills/omw
|
|
176
|
+
bunx github:domuk-k/oh-my-workflow skill install --opencode # → ~/.config/opencode/skills/omw
|
|
177
|
+
bunx github:domuk-k/oh-my-workflow skill install --project # → ./.claude/skills/omw
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Then ask your coding agent with `/omw <task>`. It authors a workflow file, runs it
|
|
181
|
+
with `omw run`, and uses the JSONL journal to repair failures.
|
|
113
182
|
|
|
114
183
|
## Adapters
|
|
115
184
|
|
|
@@ -117,17 +186,32 @@ A node is a coding agent driven through its headless prompt→result CLI.
|
|
|
117
186
|
|
|
118
187
|
| adapter | status | notes |
|
|
119
188
|
|---|---|---|
|
|
189
|
+
| **auto** | default | honors `OMW_AGENT`, then host hints, then installed CLIs in order: `claude`, `codex`, `hermes` |
|
|
120
190
|
| **fake** | built-in, free, deterministic | the no-key demo engine and test double |
|
|
121
|
-
| **claude** | **full** (live-verified, 2.1.
|
|
122
|
-
| **codex** | **experimental** (live-verified, 0.137.
|
|
191
|
+
| **claude** | **full** (live-verified, 2.1.x) | `claude -p --output-format json --strict-mcp-config` (nodes isolated from host MCP by default; opt in per call with `inheritMcp`); `--resume` (same cwd) powers in-session schema self-repair. `effort`/`agentType` have no faithful CLI flag yet → dropped with a one-time warn (honest-scope) |
|
|
192
|
+
| **codex** | **experimental** (live-verified, 0.137.x) | `codex exec --json`; **no cost field**; tolerates malformed JSONL ([openai/codex#15451](https://github.com/openai/codex/issues/15451)) and fails *actionably* |
|
|
193
|
+
| **hermes** | **experimental** | `hermes -z <prompt> --yolo` (one-shot, prints only the response); no in-session followUp (schema retries go fresh); no cost field |
|
|
123
194
|
| **pi** | planned | not wired yet (`--agent pi` → exit 3 + install hint) |
|
|
124
|
-
| **kiro** | not a fit | its CLI is an IDE launcher (open files/diffs), no headless prompt→result interface |
|
|
125
195
|
|
|
126
196
|
A missing CLI exits `3` with an `install_hint` instead of failing mid-run. A node
|
|
127
197
|
that hits `internal_error` (e.g. an invalid JSON Schema) escalates the run to exit
|
|
128
198
|
`4` (result still on stdout) so an author bug doesn't hide behind the null-contract.
|
|
129
199
|
`omw validate <wf>` is a pre-flight load + fake-fixture lint that spawns no agents.
|
|
130
200
|
|
|
201
|
+
## Migrating from 0.3 (`(rt, args)` → destructured DI)
|
|
202
|
+
|
|
203
|
+
0.3 scripts wrote `export default async function (rt, args) { rt.agent(…) }`. That
|
|
204
|
+
**still works** in 0.4 — the same object is passed as the first arg, so a legacy
|
|
205
|
+
`rt` script and a new `({ agent })` script both run. You'll get a one-time
|
|
206
|
+
deprecation notice on legacy scripts; the bridge is removed in 0.5.
|
|
207
|
+
|
|
208
|
+
Migrate mechanically:
|
|
209
|
+
|
|
210
|
+
```sh
|
|
211
|
+
omw codemod path/to/workflow.ts # prints the destructured-DI version
|
|
212
|
+
omw codemod path/to/workflow.ts --write # rewrites in place
|
|
213
|
+
```
|
|
214
|
+
|
|
131
215
|
## Honest scope (read before you judge the novelty)
|
|
132
216
|
|
|
133
217
|
omw externalizes a pattern Claude Code uses internally for dynamic workflows
|
|
@@ -137,59 +221,60 @@ omw externalizes a pattern Claude Code uses internally for dynamic workflows
|
|
|
137
221
|
|
|
138
222
|
- **"deterministic"** means: the engine's guarantees (stable resume keys, JSONL
|
|
139
223
|
recording, schema-gate) **and** the `--agent fake` demo. Your *script's*
|
|
140
|
-
determinism is a **convention you keep**
|
|
141
|
-
|
|
224
|
+
determinism is a **convention you keep** unless you pass `--strict` (opt-in
|
|
225
|
+
freeze-throw on `Date`/`Math.random`).
|
|
142
226
|
- **resume**: the journal format and resume key `(callIndex, promptHash,
|
|
143
|
-
optsHash)`
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
`
|
|
147
|
-
`
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
`--
|
|
156
|
-
|
|
157
|
-
(no sandbox), so that stays a convention you keep (enforcement is v2). `omw replay` remains a
|
|
158
|
-
read-only **fixture replay** (reconstructing a recorded run's view), a separate
|
|
159
|
-
command — not the resume path.
|
|
227
|
+
optsHash)` are **frozen and byte-stable** (identical re-run = 100% key hits;
|
|
228
|
+
edit the last node = hits up to the first change, then a miss). `omw run <wf>
|
|
229
|
+
--resume <journal|runId>` reuses any node whose key hits (adapter not invoked,
|
|
230
|
+
`agent_end{cached:true}`) and re-runs the rest. The key is **semantic** —
|
|
231
|
+
cosmetic `label`/`phase` changes don't bust the cache; `model`/`schema`/`effort`/
|
|
232
|
+
`isolation` do. Resume is **per-node key match, not dependency-aware**: when
|
|
233
|
+
nodes pass state through the **filesystem** (node 1 writes, node 2 reads), an
|
|
234
|
+
upstream edit re-runs node 1 but a cached node 2 can serve a **stale** result —
|
|
235
|
+
re-run fresh, or thread a file digest into the downstream prompt. A
|
|
236
|
+
dependency-aware cascade is v2.
|
|
237
|
+
- **`budget`** bounds *output-token spend the adapter reports*. A token-less
|
|
238
|
+
failure (a killed timeout reports no `usage`) can't be counted, so a loop on a
|
|
239
|
+
purely-timing-out node isn't bounded by `--budget` alone — pair it with your own
|
|
240
|
+
iteration cap.
|
|
160
241
|
- an omw node is a **whole external coding-agent CLI**, heavier than a single
|
|
161
|
-
in-harness subagent.
|
|
162
|
-
|
|
163
|
-
`budget`, nested `workflow()`, a `meta`/`phases` block, custom `agentType`,
|
|
164
|
-
`run_in_background`, worktree isolation.
|
|
242
|
+
in-harness subagent. JSON is extracted heuristically; cross-node state often
|
|
243
|
+
flows through the **filesystem** (a real side-channel, not modeled by resume).
|
|
165
244
|
|
|
166
245
|
The one genuinely novel piece of code is the **schema-gate self-repair loop** —
|
|
167
246
|
the part a "subprocess + for-loop" comparison misses. Everything else is honest
|
|
168
|
-
glue.
|
|
169
|
-
[`skill/SKILL.md`](skill/SKILL.md) and the
|
|
170
|
-
[launch strategy](https://github.com/domuk-k/oh-my-workflow/blob/main/docs/specs/2026-06-14-omw-launch-strategy.md).
|
|
247
|
+
glue.
|
|
171
248
|
|
|
172
249
|
## Develop
|
|
173
250
|
|
|
174
251
|
```sh
|
|
175
252
|
bun install
|
|
176
|
-
bun test #
|
|
177
|
-
bun test --coverage # ~99% lines on the pure core
|
|
253
|
+
bun test # green (live adapters run only under OMW_LIVE=1) / 0 fail
|
|
178
254
|
bun run typecheck # tsc --noEmit, clean
|
|
179
255
|
```
|
|
180
256
|
|
|
181
|
-
`test/
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
(they spend real tokens)
|
|
257
|
+
The conformance suite (`conformance/*.ts` + `test/conformance.test.ts`) is the
|
|
258
|
+
drop-in proof: native-shaped, destructured-DI scripts — fan-out, pipeline,
|
|
259
|
+
schema-gate, budget-loop, `--strict` — all run green under `--agent fake`. Live
|
|
260
|
+
adapter tests run only under `OMW_LIVE=1` (they spend real tokens).
|
|
185
261
|
|
|
186
262
|
## Docs
|
|
187
263
|
|
|
264
|
+
- **Official docs site source**: [`docs/site/index.html`](docs/site/index.html)
|
|
265
|
+
- Launch note: [`docs/launch/show-hn.md`](docs/launch/show-hn.md)
|
|
188
266
|
- **Skill (primary product)**: [`skill/SKILL.md`](skill/SKILL.md)
|
|
267
|
+
- Open-twin design: [`docs/specs/2026-06-23-omw-open-dynamic-workflow-twin-design.md`](https://github.com/domuk-k/oh-my-workflow/blob/main/docs/specs/2026-06-23-omw-open-dynamic-workflow-twin-design.md)
|
|
189
268
|
- Product spec: [`docs/specs/2026-06-12-oh-my-workflow-design.md`](https://github.com/domuk-k/oh-my-workflow/blob/main/docs/specs/2026-06-12-oh-my-workflow-design.md)
|
|
190
|
-
- Launch strategy + scorecard: [`docs/specs/2026-06-14-omw-launch-strategy.md`](https://github.com/domuk-k/oh-my-workflow/blob/main/docs/specs/2026-06-14-omw-launch-strategy.md)
|
|
191
269
|
- Resume / determinism internals: [`docs/specs/2026-06-15-resume-internals-deepdive.md`](https://github.com/domuk-k/oh-my-workflow/blob/main/docs/specs/2026-06-15-resume-internals-deepdive.md)
|
|
192
270
|
|
|
271
|
+
Deploy the docs with Vercel:
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
bun run docs:build
|
|
275
|
+
bunx vercel deploy --prod
|
|
276
|
+
```
|
|
277
|
+
|
|
193
278
|
## License
|
|
194
279
|
|
|
195
280
|
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Conformance: a budget-bounded loop terminates when the ceiling is hit. Run
|
|
2
|
+
// with `--budget 100`; each node spends 40, so agent() throws BudgetExceededError
|
|
3
|
+
// on the third call and the run halts (exit 1, "budget exhausted").
|
|
4
|
+
|
|
5
|
+
export const meta = { name: "budget-loop" };
|
|
6
|
+
|
|
7
|
+
export default async function ({ agent }, _args) {
|
|
8
|
+
const done = [];
|
|
9
|
+
// No budget.remaining() guard on purpose: the ceiling itself must halt the
|
|
10
|
+
// loop (agent() throws BudgetExceededError once spent >= total).
|
|
11
|
+
while (true) {
|
|
12
|
+
done.push(await agent(`step ${done.length}`));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const fake = { default: { text: "x", outputTokens: 40 } };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Conformance: a fan-out over parallel(), survivors through filter(Boolean).
|
|
2
|
+
// Native-shaped destructured-DI authoring; runs green under `--agent fake`.
|
|
3
|
+
|
|
4
|
+
export const meta = { name: "fanout", phases: [{ title: "Search" }] };
|
|
5
|
+
|
|
6
|
+
export default async function ({ agent, parallel, phase }, _args) {
|
|
7
|
+
phase("Search");
|
|
8
|
+
const found = await parallel(
|
|
9
|
+
["a", "b", "c"].map((t) => () => agent(`SEARCH ${t}`, { label: `search:${t}` })),
|
|
10
|
+
);
|
|
11
|
+
return { found: found.filter(Boolean) };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const fake = {
|
|
15
|
+
rules: [
|
|
16
|
+
{ match: (p) => p.includes("SEARCH a"), responses: [{ text: "A" }] },
|
|
17
|
+
{ match: (p) => p.includes("SEARCH b"), responses: [{ fail: "timeout" }] },
|
|
18
|
+
{ match: (p) => p.includes("SEARCH c"), responses: [{ text: "C" }] },
|
|
19
|
+
],
|
|
20
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Conformance: a two-stage pipeline() — each item flows through both stages
|
|
2
|
+
// independently. Destructured-DI; runs green under `--agent fake`.
|
|
3
|
+
|
|
4
|
+
export const meta = { name: "pipeline", phases: [{ title: "Map" }] };
|
|
5
|
+
|
|
6
|
+
export default async function ({ agent, pipeline, phase }, _args) {
|
|
7
|
+
phase("Map");
|
|
8
|
+
const out = await pipeline(
|
|
9
|
+
["x", "y"],
|
|
10
|
+
(item) => agent(`STAGE1 ${item}`),
|
|
11
|
+
(prev) => agent(`STAGE2 ${prev}`),
|
|
12
|
+
);
|
|
13
|
+
return { out };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const fake = {
|
|
17
|
+
rules: [
|
|
18
|
+
{ match: (p) => p.startsWith("STAGE1"), responses: [{ text: "s1" }] },
|
|
19
|
+
{ match: (p) => p.startsWith("STAGE2"), responses: [{ text: "s2" }] },
|
|
20
|
+
],
|
|
21
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Conformance: the schema gate repairs a first non-conforming response, then
|
|
2
|
+
// returns validated JSON. Destructured-DI; runs green under `--agent fake`.
|
|
3
|
+
|
|
4
|
+
export const meta = { name: "schema-gate" };
|
|
5
|
+
|
|
6
|
+
const numSchema = { type: "object", properties: { n: { type: "number" } }, required: ["n"], additionalProperties: false };
|
|
7
|
+
|
|
8
|
+
export default async function ({ agent }, _args) {
|
|
9
|
+
const got = await agent("COMPUTE", { schema: numSchema });
|
|
10
|
+
return { got };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const fake = {
|
|
14
|
+
rules: [
|
|
15
|
+
{
|
|
16
|
+
match: (p) => p.includes("COMPUTE"),
|
|
17
|
+
// First response is invalid (string n), gate feeds the error back, second is valid.
|
|
18
|
+
responses: [{ text: '{"n":"not-a-number"}' }, { text: '{"n":7}' }],
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Conformance: a script that reaches for wall-clock time fails under `--strict`.
|
|
2
|
+
// Run with strict:true → exit 1 (script_error mentioning strict). The same script
|
|
3
|
+
// without --strict completes. Proves the determinism sandbox is enforced.
|
|
4
|
+
|
|
5
|
+
export const meta = { name: "strict-throws" };
|
|
6
|
+
|
|
7
|
+
export default async function ({ agent }, _args) {
|
|
8
|
+
const t = Date.now(); // forbidden under --strict
|
|
9
|
+
const x = await agent("go");
|
|
10
|
+
return { t, x };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const fake = { default: { text: "ok" } };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Show HN / GeekNews launch note
|
|
2
|
+
|
|
3
|
+
Title:
|
|
4
|
+
|
|
5
|
+
> Show HN: oh-my-workflow - add /omw workflow mode to your coding agent
|
|
6
|
+
|
|
7
|
+
Short post:
|
|
8
|
+
|
|
9
|
+
I built `oh-my-workflow`, a tiny Bun/TypeScript runtime plus agent skill for giving your coding agent a workflow mode. You add `/omw`, describe a multi-step job, and the agent writes and runs a plain-JS workflow whose nodes are whole coding-agent CLIs like `claude -p`, `codex exec`, and `hermes`.
|
|
10
|
+
|
|
11
|
+
The shape mirrors the dynamic workflow vocabulary people already reach for inside coding agents: `agent`, `parallel`, `pipeline`, `workflow`, `budget`, `phase`, and `log`. The difference is that omw runs outside any one host. It gives you a JSONL journal, bounded concurrency, schema-gated node output, automatic schema repair, run-level resume, and a deterministic fake adapter so anyone can try the full flow without an API key.
|
|
12
|
+
|
|
13
|
+
Try it from your coding agent:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npx skills add domuk-k/oh-my-workflow --skill omw
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then ask:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
/omw review this repo, fan out three bug-finding passes, verify the strongest claims, and return fixes with evidence.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The CLI defaults to `--agent auto`, so the generated workflow can run without making you choose Claude vs Codex in the onboarding path. For a no-key runtime demo, use `bunx github:domuk-k/oh-my-workflow run examples/deep-research --agent fake --pretty`.
|
|
26
|
+
|
|
27
|
+
Why now:
|
|
28
|
+
|
|
29
|
+
- Coding agents have become real CLIs, not just chat UIs.
|
|
30
|
+
- Multi-agent work keeps reimplementing the same glue: fan-out, verify, retry, resume, budget.
|
|
31
|
+
- Closed host-native workflow tools are useful, but the pattern should be portable across Claude Code, Codex, opencode, cron jobs, and CI.
|
|
32
|
+
|
|
33
|
+
What is deliberately small:
|
|
34
|
+
|
|
35
|
+
- No DSL.
|
|
36
|
+
- No source transform.
|
|
37
|
+
- No ambient globals.
|
|
38
|
+
- No sandbox by default.
|
|
39
|
+
- A node is a whole coding-agent CLI, not a raw LLM call.
|
|
40
|
+
|
|
41
|
+
Repo: https://github.com/domuk-k/oh-my-workflow
|