oh-my-workflow 0.1.0 → 0.2.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
@@ -94,6 +94,23 @@ and fix your script. stdout is one result JSON; the `--pretty` tree and a
94
94
  (fan-out / verify-vote / pipeline / loop-until-dry), the debug loop, and the
95
95
  conventions. That skill is the primary product; this README is the human intro.
96
96
 
97
+ ## Install the skill (the primary product)
98
+
99
+ 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:
102
+
103
+ ```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)
107
+ ```
108
+
109
+ Then ask your coding agent: *"use oh-my-workflow to <task>"* — 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.)
113
+
97
114
  ## Adapters
98
115
 
99
116
  A node is a coding agent driven through its headless prompt→result CLI.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-workflow",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Run coding-agent CLIs (claude -p / codex exec) as nodes in a plain-JS workflow. The thin deterministic glue.",
5
5
  "type": "module",
6
6
  "author": "Dongwook Kim (https://github.com/domuk-k)",
package/src/cli/omw.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  import { runCommand } from "./run";
7
7
  import { replayCommand } from "./replay";
8
8
  import { validateCommand } from "./validate";
9
+ import { skillCommand } from "./skill";
9
10
 
10
11
  const io = {
11
12
  stdout: (s: string) => process.stdout.write(s),
@@ -21,13 +22,16 @@ async function main(argv: string[]): Promise<number> {
21
22
  return replayCommand(rest, io);
22
23
  case "validate":
23
24
  return validateCommand(rest, io);
25
+ case "skill":
26
+ return skillCommand(rest, io);
24
27
  default:
25
28
  io.stderr(
26
29
  "usage: omw <command>\n\n" +
27
30
  "commands:\n" +
28
31
  " run <workflow> --agent <fake|claude|codex|pi> [--args JSON] [--concurrency N] [--resume <journal.jsonl>] [--pretty]\n" +
29
32
  " replay <journal.jsonl> [--json]\n" +
30
- " validate <workflow> [--json]\n\n" +
33
+ " validate <workflow> [--json]\n" +
34
+ " skill install [--project] install the omw authoring skill for your coding agent\n\n" +
31
35
  "free demo (no API key): omw run examples/deep-research --agent fake\n",
32
36
  );
33
37
  return cmd === undefined ? 2 : 2;
@@ -0,0 +1,104 @@
1
+ // `omw skill <install|path>` — make "installed npm package" → "active authoring
2
+ // skill" one ergonomic step. The bundled skill/SKILL.md is the primary product:
3
+ // it teaches a coding agent to author, run, and repair omw workflows. `install`
4
+ // copies it into a coding agent's skills dir (auto-discovered by Claude Code);
5
+ // `path` prints the bundled copy's location for piping / pointing an agent at it.
6
+ //
7
+ // fs is reachable directly (this is the IO wiring command, like run.ts); the arg
8
+ // parse is a pure function so the contract is testable without touching disk.
9
+
10
+ import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
11
+ import { homedir } from "node:os";
12
+ import { dirname, join, resolve } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+
15
+ /** Package root, so the bundled skill resolves whether omw runs from a clone or
16
+ * an npm install invoked from any cwd. (Same technique as run.ts's PKG_ROOT.) */
17
+ const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
18
+ const SKILL_NAME = "oh-my-workflow";
19
+
20
+ export type SkillIo = {
21
+ stdout: (s: string) => void;
22
+ stderr: (s: string) => void;
23
+ /** Overridable for tests; default to the real environment. */
24
+ homeDir?: string;
25
+ cwd?: string;
26
+ /** Directory holding the bundled SKILL.md; defaults to <pkg>/skill. */
27
+ skillDir?: string;
28
+ };
29
+
30
+ export type SkillParse =
31
+ | { ok: true; sub: "install"; project: boolean }
32
+ | { ok: true; sub: "path" }
33
+ | { ok: true; sub: "help" }
34
+ | { ok: false; error: string };
35
+
36
+ const USAGE =
37
+ "usage: omw skill <command>\n\n" +
38
+ "commands:\n" +
39
+ " install [--project] copy the skill into a skills dir so a coding agent picks it up\n" +
40
+ " (default: ~/.claude/skills/oh-my-workflow; --project: ./.claude/skills/…)\n" +
41
+ " path print the bundled SKILL.md path (for cat / piping / pointing an agent at it)\n";
42
+
43
+ export function parseSkillArgs(argv: string[]): SkillParse {
44
+ const [sub, ...rest] = argv;
45
+ if (sub === undefined || sub === "help" || sub === "--help" || sub === "-h") {
46
+ return { ok: true, sub: "help" };
47
+ }
48
+ if (sub === "path") {
49
+ if (rest.length > 0) return { ok: false, error: `unexpected argument: ${rest[0]}` };
50
+ return { ok: true, sub: "path" };
51
+ }
52
+ if (sub === "install") {
53
+ let project = false;
54
+ for (const tok of rest) {
55
+ if (tok === "--project") project = true;
56
+ else return { ok: false, error: `unexpected argument: ${tok}` };
57
+ }
58
+ return { ok: true, sub: "install", project };
59
+ }
60
+ return { ok: false, error: `unknown skill subcommand: ${sub}` };
61
+ }
62
+
63
+ export async function skillCommand(argv: string[], io: SkillIo): Promise<number> {
64
+ const parsed = parseSkillArgs(argv);
65
+ if (!parsed.ok) {
66
+ io.stderr(`${parsed.error}\n\n${USAGE}`);
67
+ return 2;
68
+ }
69
+ if (parsed.sub === "help") {
70
+ io.stdout(USAGE);
71
+ return 0;
72
+ }
73
+
74
+ const srcDir = io.skillDir ?? join(PKG_ROOT, "skill");
75
+ const srcFile = join(srcDir, "SKILL.md");
76
+ if (!existsSync(srcFile)) {
77
+ io.stderr(`bundled skill not found at ${srcFile} — reinstall oh-my-workflow?\n`);
78
+ return 1;
79
+ }
80
+
81
+ if (parsed.sub === "path") {
82
+ io.stdout(`${srcFile}\n`);
83
+ return 0;
84
+ }
85
+
86
+ // install — idempotent: copy the whole skill dir (SKILL.md + any bundled
87
+ // resources) in place, and report installed vs updated.
88
+ const base = parsed.project ? join(io.cwd ?? process.cwd(), ".claude") : join(io.homeDir ?? homedir(), ".claude");
89
+ const destDir = join(base, "skills", SKILL_NAME);
90
+ const dest = join(destDir, "SKILL.md");
91
+ const updating = existsSync(dest);
92
+ // Clean replace, not an additive copy: drop a prior install first so a file
93
+ // that was removed from the bundle doesn't linger as stale content.
94
+ rmSync(destDir, { recursive: true, force: true });
95
+ mkdirSync(destDir, { recursive: true });
96
+ cpSync(srcDir, destDir, { recursive: true });
97
+
98
+ io.stdout(
99
+ `${updating ? "updated" : "installed"} ${SKILL_NAME} skill → ${dest}\n` +
100
+ `${parsed.project ? "This project's" : "Claude Code"} agent auto-discovers skills here.\n` +
101
+ `Next: ask your coding agent to "use oh-my-workflow to <your task>".\n`,
102
+ );
103
+ return 0;
104
+ }