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 CHANGED
@@ -1,115 +1,184 @@
1
1
  # oh-my-workflow
2
2
 
3
- > Run the coding-agent CLIs you already have `claude -p`, `codex exec` as
4
- > nodes in a plain-JS workflow your host agent writes. omw is the thin glue: it
5
- > runs the script, schema-gates each node's output, and journals every step so the
6
- > agent can read its own failure and repair its own script. (What's
7
- > "deterministic" is scoped honestly below the engine and `--agent fake`, not
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
- ## Try it now free, no API key
10
+ ## Quickstart add `/omw` to your coding agent
11
11
 
12
12
  ```sh
13
- git clone https://github.com/domuk-k/oh-my-workflow && cd oh-my-workflow
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
- ```json
19
- {"confirmed":[{"topic":"a","hits":3,"verified":true},{"topic":"c","hits":5,"verified":true}],"summary":{"summary":"done","count":2}}
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's the whole spine in one pass a `--pretty` tree shows it:
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
- bun src/cli/omw.ts run examples/deep-research --agent fake --pretty
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
- run r-… (examples/deep-research)
30
- Scope
31
- call#1 [fake]
32
- call#1
33
- Search
34
- • search:a [fake]
35
- search:b [fake]
36
- • search:c [fake]
37
- timeout call#3
38
- call#4
39
- call#2
40
- Verify
41
- call#5 [fake]
42
- • call#6 [fake]
43
- ✓ call#5
44
- call#6
45
- Synthesize
46
- call#7 [fake]
47
- call#7
48
- run_end ok=true · 6 ok / 1 failed
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
- `search:a` (call#2) returns invalid JSON first and self-repairs to `✓`; `search:b`
52
- (call#3) times out and is dropped by `filter(Boolean)` the run still ends green.
73
+ ```ts
74
+ // omwanywhere, 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
- `--agent fake` is a built-in deterministic adapter — no API key, no network. A
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
- > Once on npm this is `bunx oh-my-workflow run examples/deep-research --agent fake`
60
- > the example ships inside the package and resolves from there, so it runs from
61
- > any directory. omw runs under **bun**; `npx` (Node) won't execute the TS bin.
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 nodes are **whole coding agents**
66
- (`claude -p`, `codex exec`) not single LLM calls. The runtime hands your script
67
- **five hooks** and nothing else:
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 default async function (rt, args) {
71
- rt.phase("Search");
72
- const hits = (await rt.parallel(
73
- args.queries.map((q) => () => rt.agent(`SEARCH: ${q}`, { schema: HIT, label: q })),
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
- - `rt.agent(prompt, opts?)` run one coding-agent CLI node. With a `schema`, omw
80
- extracts JSON, validates it (ajv), and **re-prompts the node with the
81
- validation errors** up to 2 times before giving up. Returns the validated
82
- object, or `null`. **Never throws** the load-bearing *null-contract*.
83
- - `rt.parallel(thunks)` concurrent, barrier; failures become `null`.
84
- - `rt.pipeline(items, …stages)`each item flows through all stages independently.
85
- - `rt.phase(title)` / `rt.log(msg)` journal / `--pretty` side-channel only.
115
+ The hooksdestructure 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 the journal file `.omw/<runId>.jsonl`, so when a node
89
- fails you read the `kind` (`timeout` / `nonzero_exit` / `schema_violation` / …)
90
- and fix your script. stdout is one result JSON; the `--pretty` tree and a
91
- `journal: <path>` pointer go to stderr.
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. After the package
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
- omw skill install # → ~/.claude/skills/oh-my-workflow (Claude Code auto-discovers it)
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
- Then ask your coding agent: *"use oh-my-workflow to &lt;task&gt;"* it authors a
110
- `workflow.ts` and runs it with `omw run`. (The skill is Claude-Code-flavored;
111
- for other hosts use `omw skill path` and feed the file in however that host loads
112
- context.)
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.177) | `claude -p --output-format json`; `--resume` powers in-session schema self-repair |
122
- | **codex** | **experimental** (live-verified, 0.137.0) | `codex exec --json`; **no cost field**; tolerates malformed JSONL ([openai/codex#15451](https://github.com/openai/codex/issues/15451)) and fails *actionably* |
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** there is **no sandbox**, so omw
141
- can't stop a workflow from calling `Date.now()`.
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)` (journaled as `call`) are **frozen and proven byte-stable** (identical re-run = 100% key
144
- hits; edit the last node = hits up to the first change, then a miss). **Live
145
- resume has landed**: `omw run <wf> --resume <journal>` reuses any node whose
146
- `(callIndex, promptHash, optsHash)` key hits (adapter not invoked,
147
- `agent_end{cached:true}`) and re-runs the rest — verified end-to-end on
148
- `--agent fake`. Resume is **per-node key match, not dependency-aware**: it
149
- behaves as longest-unchanged-prefix only when upstream outputs flow into
150
- downstream prompts (the usual data-flow shape). When nodes instead pass state
151
- through the **filesystem** (the normal coding-agent idiom node 1 writes files
152
- node 2 reads), an upstream edit re-runs node 1 but a cached node 2 serves a
153
- **stale** result; re-run fresh, or thread a file digest into the downstream
154
- prompt. Keeping per-node preserves parallel/pipeline sibling cache; a
155
- `--strict-resume` prefix-truncation opt-in and dependency-aware cascade are v2.
156
- It holds **only for deterministic workflows**: omw can't *enforce* determinism
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
- - **not in v1** (the CC dynamic-workflow surface has these; omw doesn't yet):
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. The fuller positioning (4-way prior-art table, resemblance ledger) lives in
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 # 136 pass / 2 skip (live adapters, OMW_LIVE=1) / 0 fail
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/spine.test.ts` is the gate: one full `scope → search → verify → synthesize`
182
- pass against the fake adapter, including the scripted schema-fail → self-repair →
183
- `filter(Boolean)` survival cycle. Live adapter tests run only under `OMW_LIVE=1`
184
- (they spend real tokens) and are skipped by default.
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