flowbook 0.1.6 → 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
@@ -30,12 +30,14 @@ npm run build-flowbook
30
30
  flowbook init Set up Flowbook in your project
31
31
  flowbook dev [--port 6200] Start the dev server
32
32
  flowbook build [--out-dir d] Build a static site
33
+ flowbook skill <agent> [-g] Install AI agent skill & /flowbook command
33
34
  ```
34
35
 
35
36
  ### `flowbook init`
36
37
 
37
38
  - Adds `"flowbook"` and `"build-flowbook"` scripts to your `package.json`
38
39
  - Creates `flows/example.flow.md` as a starter template
40
+ - Installs AI agent skills to all supported agent directories
39
41
 
40
42
  ### `flowbook dev`
41
43
 
@@ -99,56 +101,77 @@ When a coding agent (Claude Code, OpenAI Codex, VS Code Copilot, Cursor, Gemini
99
101
  3. Generate `.flow.md` files with Mermaid diagrams for every significant flow
100
102
  4. Verify the build
101
103
 
102
- ### Install Skill via CLI
104
+ ### `flowbook skill`
103
105
 
104
- You can also install the skill standalone using [skills.sh](https://skills.sh):
106
+ Install skills and `/flowbook` slash commands for specific agents:
105
107
 
106
108
  ```bash
107
- npx skills add Epsilondelta-ai/flowbook
109
+ # Install for a specific agent (project-level)
110
+ flowbook skill opencode
111
+ flowbook skill claude
112
+ flowbook skill cursor
113
+
114
+ # Install for all agents
115
+ flowbook skill all
116
+
117
+ # Install globally (available across all projects)
118
+ flowbook skill opencode -g
119
+ flowbook skill all --global
108
120
  ```
109
121
 
110
- This auto-detects your installed coding agents and installs the skill to the correct directories.
122
+ **What gets installed:**
123
+
124
+ | Component | Description |
125
+ |-----------|-------------|
126
+ | **Skill** (`SKILL.md`) | Auto-triggers when you mention "flowbook" in prompts |
127
+ | **Slash command** (`/flowbook`) | Explicit trigger — type `/flowbook` to generate flowcharts |
111
128
 
112
- ### Global Installation
129
+ Slash commands are installed for agents that support them: **Claude Code**, **Cursor**, **Windsurf**, **OpenCode**.
113
130
 
114
- To install the skill globally (available across all projects):
131
+ ### Install via skills.sh
132
+
133
+ You can also install the skill standalone using [skills.sh](https://skills.sh):
115
134
 
116
135
  ```bash
136
+ # Project-level
137
+ npx skills add Epsilondelta-ai/flowbook
138
+
139
+ # Global
117
140
  npx skills add -g Epsilondelta-ai/flowbook
118
141
  ```
119
142
 
120
- This installs the skill to each agent's global directory (e.g., `~/.claude/skills/`, `~/.config/opencode/skills/`, etc.).
143
+ > **Note:** `npx skills add` installs skills only (SKILL.md). Use `flowbook skill` to also install `/flowbook` slash commands.
121
144
 
122
- ### Compatible Agents
145
+ ### Supported Agents
123
146
 
124
- | Agent | Skill Location |
125
- |-------|---------------|
126
- | Claude Code | `.claude/skills/flowbook/SKILL.md` |
127
- | OpenAI Codex | `.agents/skills/flowbook/SKILL.md` |
128
- | VS Code / GitHub Copilot | `.github/skills/flowbook/SKILL.md` |
129
- | Google Antigravity | `.agent/skills/flowbook/SKILL.md` |
130
- | Gemini CLI | `.gemini/skills/flowbook/SKILL.md` |
131
- | Cursor | `.cursor/skills/flowbook/SKILL.md` |
132
- | Windsurf (Codeium) | `.windsurf/skills/flowbook/SKILL.md` |
133
- | AmpCode | `.amp/skills/flowbook/SKILL.md` |
134
- | OpenCode / oh-my-opencode | `.opencode/skills/flowbook/SKILL.md` |
147
+ | Agent | Skill | Slash Command |
148
+ |-------|-------|---------------|
149
+ | Claude Code | `.claude/skills/flowbook/SKILL.md` | `.claude/commands/flowbook.md` |
150
+ | OpenAI Codex | `.agents/skills/flowbook/SKILL.md` | — |
151
+ | VS Code / GitHub Copilot | `.github/skills/flowbook/SKILL.md` | — |
152
+ | Google Antigravity | `.agent/skills/flowbook/SKILL.md` | — |
153
+ | Gemini CLI | `.gemini/skills/flowbook/SKILL.md` | — |
154
+ | Cursor | `.cursor/skills/flowbook/SKILL.md` | `.cursor/commands/flowbook.md` |
155
+ | Windsurf (Codeium) | `.windsurf/skills/flowbook/SKILL.md` | `.windsurf/workflows/flowbook.md` |
156
+ | AmpCode | `.amp/skills/flowbook/SKILL.md` | — |
157
+ | OpenCode / oh-my-opencode | `.opencode/skills/flowbook/SKILL.md` | `.opencode/command/flowbook.md` |
135
158
 
136
159
  <details>
137
- <summary>Manual Skill Installation</summary>
160
+ <summary>Manual Installation</summary>
138
161
 
139
- If you didn't use `flowbook init` or `npx skills add`, copy the skill manually:
162
+ If you didn't use `flowbook skill` or `npx skills add`, copy files manually:
140
163
 
141
164
  ```bash
142
- # Example: Claude Code
165
+ # Skill
143
166
  mkdir -p .claude/skills/flowbook
144
167
  cp node_modules/flowbook/src/skills/flowbook/SKILL.md .claude/skills/flowbook/
145
168
 
146
- # Example: Cursor
147
- mkdir -p .cursor/skills/flowbook
148
- cp node_modules/flowbook/src/skills/flowbook/SKILL.md .cursor/skills/flowbook/
169
+ # Slash command (Claude Code)
170
+ mkdir -p .claude/commands
171
+ cp node_modules/flowbook/src/commands/flowbook.md .claude/commands/
149
172
  ```
150
173
 
151
- Replace the directory with the appropriate path from the Compatible Agents table above.
174
+ Replace directories with the appropriate paths from the table above.
152
175
 
153
176
  </details>
154
177
  ## How It Works
@@ -175,12 +198,19 @@ Replace the directory with the appropriate path from the Compatible Agents table
175
198
  src/
176
199
  ├── types.ts # Shared types (FlowEntry, FlowbookData)
177
200
  ├── node/
178
- │ ├── cli.ts # CLI entry point (init, dev, build)
201
+ │ ├── cli.ts # CLI entry point (init, dev, build, skill)
179
202
  │ ├── server.ts # Programmatic Vite server & build
180
203
  │ ├── init.ts # Project initialization logic
204
+ │ ├── skill.ts # AI agent skill & command installer
181
205
  │ ├── discovery.ts # File scanner (fast-glob)
182
206
  │ ├── parser.ts # Frontmatter + mermaid extraction
183
207
  │ └── plugin.ts # Vite virtual module plugin
208
+ ├── skills/
209
+ │ └── flowbook/
210
+ │ └── SKILL.md # AI agent skill definition
211
+ ├── commands/
212
+ │ ├── flowbook.md # Slash command (frontmatter format)
213
+ │ └── flowbook.plain.md # Slash command (plain markdown format)
184
214
  └── client/
185
215
  ├── index.html # Entry HTML
186
216
  ├── main.tsx # React entry
@@ -193,7 +223,6 @@ src/
193
223
  ├── MermaidRenderer.tsx # Mermaid diagram rendering
194
224
  ├── FlowView.tsx # Single flow detail view
195
225
  └── EmptyState.tsx # Empty state with guide
196
- ```
197
226
 
198
227
  ## Development (Contributing)
199
228
 
package/dist/cli.js CHANGED
@@ -1,11 +1,214 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/node/init.ts
4
- import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync } from "fs";
5
- import { resolve, dirname } from "path";
4
+ import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
5
+ import { resolve as resolve2 } from "path";
6
6
  import { execSync } from "child_process";
7
+
8
+ // src/node/skill.ts
9
+ import { existsSync, mkdirSync, copyFileSync } from "fs";
10
+ import { resolve, dirname } from "path";
7
11
  import { fileURLToPath } from "url";
12
+ import { homedir } from "os";
8
13
  var __dirname = dirname(fileURLToPath(import.meta.url));
14
+ var AGENTS = [
15
+ {
16
+ name: "Claude Code",
17
+ aliases: ["claude"],
18
+ skill: {
19
+ project: ".claude/skills/flowbook",
20
+ global: ".claude/skills/flowbook"
21
+ },
22
+ command: {
23
+ project: ".claude/commands",
24
+ global: ".claude/commands",
25
+ format: "frontmatter"
26
+ }
27
+ },
28
+ {
29
+ name: "OpenAI Codex",
30
+ aliases: ["codex"],
31
+ skill: {
32
+ project: ".agents/skills/flowbook",
33
+ global: ".codex/skills/flowbook"
34
+ }
35
+ },
36
+ {
37
+ name: "VS Code / GitHub Copilot",
38
+ aliases: ["copilot", "vscode"],
39
+ skill: {
40
+ project: ".github/skills/flowbook",
41
+ global: ".copilot/skills/flowbook"
42
+ }
43
+ },
44
+ {
45
+ name: "Google Antigravity",
46
+ aliases: ["antigravity"],
47
+ skill: {
48
+ project: ".agent/skills/flowbook",
49
+ global: ".gemini/antigravity/skills/flowbook"
50
+ }
51
+ },
52
+ {
53
+ name: "Gemini CLI",
54
+ aliases: ["gemini"],
55
+ skill: {
56
+ project: ".gemini/skills/flowbook",
57
+ global: ".gemini/skills/flowbook"
58
+ }
59
+ },
60
+ {
61
+ name: "Cursor",
62
+ aliases: ["cursor"],
63
+ skill: {
64
+ project: ".cursor/skills/flowbook",
65
+ global: ".cursor/skills/flowbook"
66
+ },
67
+ command: {
68
+ project: ".cursor/commands",
69
+ global: ".cursor/commands",
70
+ format: "plain"
71
+ }
72
+ },
73
+ {
74
+ name: "Windsurf",
75
+ aliases: ["windsurf"],
76
+ skill: {
77
+ project: ".windsurf/skills/flowbook",
78
+ global: ".codeium/windsurf/skills/flowbook"
79
+ },
80
+ command: {
81
+ project: ".windsurf/workflows",
82
+ global: ".codeium/windsurf/workflows",
83
+ format: "plain"
84
+ }
85
+ },
86
+ {
87
+ name: "AmpCode",
88
+ aliases: ["amp"],
89
+ skill: {
90
+ project: ".amp/skills/flowbook",
91
+ global: ".config/agents/skills/flowbook"
92
+ }
93
+ },
94
+ {
95
+ name: "OpenCode",
96
+ aliases: ["opencode"],
97
+ skill: {
98
+ project: ".opencode/skills/flowbook",
99
+ global: ".config/opencode/skills/flowbook"
100
+ },
101
+ command: {
102
+ project: ".opencode/command",
103
+ global: ".config/opencode/command",
104
+ format: "frontmatter"
105
+ }
106
+ }
107
+ ];
108
+ function getSkillSrc() {
109
+ return resolve(__dirname, "..", "src", "skills", "flowbook", "SKILL.md");
110
+ }
111
+ function getCommandSrc(format) {
112
+ const file = format === "frontmatter" ? "flowbook.md" : "flowbook.plain.md";
113
+ return resolve(__dirname, "..", "src", "commands", file);
114
+ }
115
+ function resolveAgents(agentArg) {
116
+ if (agentArg === "all") return AGENTS;
117
+ const lower = agentArg.toLowerCase();
118
+ const found = AGENTS.filter(
119
+ (a) => a.aliases.includes(lower) || a.name.toLowerCase().includes(lower)
120
+ );
121
+ if (found.length === 0) {
122
+ console.error(` Unknown agent: "${agentArg}"`);
123
+ console.error(` Available: all, ${AGENTS.flatMap((a) => a.aliases).join(", ")}`);
124
+ process.exit(1);
125
+ }
126
+ return found;
127
+ }
128
+ function installFile(src, destDir, destFilename) {
129
+ const dest = resolve(destDir, destFilename);
130
+ if (existsSync(dest)) return false;
131
+ mkdirSync(destDir, { recursive: true });
132
+ copyFileSync(src, dest);
133
+ return true;
134
+ }
135
+ function installSkills(agentArg, global) {
136
+ const agents = resolveAgents(agentArg);
137
+ const base = global ? homedir() : process.cwd();
138
+ const skillSrc = getSkillSrc();
139
+ if (!existsSync(skillSrc)) {
140
+ console.error(" \u2717 Skill source file not found. Reinstall flowbook.");
141
+ process.exit(1);
142
+ }
143
+ let skillCount = 0;
144
+ let cmdCount = 0;
145
+ for (const agent of agents) {
146
+ const skillDir = resolve(base, global ? agent.skill.global : agent.skill.project);
147
+ if (installFile(skillSrc, skillDir, "SKILL.md")) {
148
+ skillCount++;
149
+ }
150
+ if (agent.command) {
151
+ const cmdSrc = getCommandSrc(agent.command.format);
152
+ if (existsSync(cmdSrc)) {
153
+ const cmdDir = resolve(base, global ? agent.command.global : agent.command.project);
154
+ if (installFile(cmdSrc, cmdDir, "flowbook.md")) {
155
+ cmdCount++;
156
+ }
157
+ }
158
+ }
159
+ }
160
+ const scope = global ? "global" : "project";
161
+ const agentNames = agents.map((a) => a.name).join(", ");
162
+ if (skillCount > 0 || cmdCount > 0) {
163
+ if (skillCount > 0) console.log(` \u2713 Installed skill to ${skillCount} ${scope} director${skillCount > 1 ? "ies" : "y"}`);
164
+ if (cmdCount > 0) console.log(` \u2713 Installed /flowbook command to ${cmdCount} ${scope} director${cmdCount > 1 ? "ies" : "y"}`);
165
+ console.log(` Agents: ${agentNames}`);
166
+ } else {
167
+ console.log(` \u2713 Already installed for: ${agentNames}`);
168
+ }
169
+ }
170
+ function installAllProjectSkills() {
171
+ const cwd = process.cwd();
172
+ const skillSrc = getSkillSrc();
173
+ if (!existsSync(skillSrc)) return 0;
174
+ let installed = 0;
175
+ for (const agent of AGENTS) {
176
+ const dir = resolve(cwd, agent.skill.project);
177
+ if (installFile(skillSrc, dir, "SKILL.md")) {
178
+ installed++;
179
+ }
180
+ }
181
+ return installed;
182
+ }
183
+ function printSkillUsage() {
184
+ console.log(`
185
+ flowbook skill \u2014 Install AI agent skill & /flowbook command
186
+
187
+ Usage:
188
+ flowbook skill <agent> Install to project
189
+ flowbook skill <agent> --global Install globally
190
+ flowbook skill <agent> -g Install globally (short)
191
+
192
+ Agents:
193
+ all All supported agents
194
+ claude Claude Code
195
+ codex OpenAI Codex
196
+ copilot VS Code / GitHub Copilot
197
+ antigravity Google Antigravity
198
+ gemini Gemini CLI
199
+ cursor Cursor
200
+ windsurf Windsurf (Codeium)
201
+ amp AmpCode
202
+ opencode OpenCode / oh-my-opencode
203
+
204
+ Examples:
205
+ flowbook skill all Install to all agents (project)
206
+ flowbook skill opencode -g Install globally for OpenCode
207
+ flowbook skill claude --global Install globally for Claude Code
208
+ `);
209
+ }
210
+
211
+ // src/node/init.ts
9
212
  var EXAMPLE_FLOW = `---
10
213
  title: Example Flow
11
214
  category: Getting Started
@@ -25,8 +228,8 @@ flowchart TD
25
228
  `;
26
229
  async function initFlowbook() {
27
230
  const cwd = process.cwd();
28
- const pkgPath = resolve(cwd, "package.json");
29
- if (!existsSync(pkgPath)) {
231
+ const pkgPath = resolve2(cwd, "package.json");
232
+ if (!existsSync2(pkgPath)) {
30
233
  console.error(" No package.json found. Run 'npm init' first.");
31
234
  process.exit(1);
32
235
  }
@@ -57,59 +260,28 @@ async function initFlowbook() {
57
260
  } else {
58
261
  console.log(" \u2713 Scripts already exist in package.json");
59
262
  }
60
- const flowsDir = resolve(cwd, "flows");
61
- const examplePath = resolve(flowsDir, "example.flow.md");
62
- if (!existsSync(examplePath)) {
63
- mkdirSync(flowsDir, { recursive: true });
263
+ const flowsDir = resolve2(cwd, "flows");
264
+ const examplePath = resolve2(flowsDir, "example.flow.md");
265
+ if (!existsSync2(examplePath)) {
266
+ mkdirSync2(flowsDir, { recursive: true });
64
267
  writeFileSync(examplePath, EXAMPLE_FLOW);
65
268
  console.log(" \u2713 Created flows/example.flow.md");
66
269
  } else {
67
270
  console.log(" \u2713 Example flow already exists");
68
271
  }
69
- const gitignorePath = resolve(cwd, ".gitignore");
70
- if (existsSync(gitignorePath)) {
272
+ const gitignorePath = resolve2(cwd, ".gitignore");
273
+ if (existsSync2(gitignorePath)) {
71
274
  const gitignore = readFileSync(gitignorePath, "utf-8");
72
275
  if (!gitignore.includes("flowbook-static")) {
73
276
  writeFileSync(gitignorePath, gitignore.trimEnd() + "\nflowbook-static\n");
74
277
  console.log(" \u2713 Added flowbook-static to .gitignore");
75
278
  }
76
279
  }
77
- const skillSrc = resolve(__dirname, "..", "src", "skills", "flowbook", "SKILL.md");
78
- const skillDirs = [
79
- resolve(cwd, ".claude", "skills", "flowbook"),
80
- // Claude Code
81
- resolve(cwd, ".agents", "skills", "flowbook"),
82
- // OpenAI Codex / cross-tool alias
83
- resolve(cwd, ".github", "skills", "flowbook"),
84
- // VS Code / GitHub Copilot
85
- resolve(cwd, ".agent", "skills", "flowbook"),
86
- // Google Antigravity
87
- resolve(cwd, ".gemini", "skills", "flowbook"),
88
- // Gemini CLI
89
- resolve(cwd, ".cursor", "skills", "flowbook"),
90
- // Cursor
91
- resolve(cwd, ".windsurf", "skills", "flowbook"),
92
- // Windsurf (Codeium)
93
- resolve(cwd, ".amp", "skills", "flowbook"),
94
- // AmpCode
95
- resolve(cwd, ".opencode", "skills", "flowbook")
96
- // OpenCode / oh-my-opencode
97
- ];
98
- if (existsSync(skillSrc)) {
99
- let installed = 0;
100
- for (const dir of skillDirs) {
101
- const dest = resolve(dir, "SKILL.md");
102
- if (!existsSync(dest)) {
103
- mkdirSync(dir, { recursive: true });
104
- copyFileSync(skillSrc, dest);
105
- installed++;
106
- }
107
- }
108
- if (installed > 0) {
109
- console.log(` \u2713 Installed AI agent skill to ${installed} agent directories`);
110
- } else {
111
- console.log(" \u2713 AI agent skills already installed");
112
- }
280
+ const installed = installAllProjectSkills();
281
+ if (installed > 0) {
282
+ console.log(` \u2713 Installed AI agent skill to ${installed} agent directories`);
283
+ } else {
284
+ console.log(" \u2713 AI agent skills already installed");
113
285
  }
114
286
  const run = pm === "yarn" ? "yarn" : `${pm} run`;
115
287
  console.log("");
@@ -119,9 +291,9 @@ async function initFlowbook() {
119
291
  console.log("");
120
292
  }
121
293
  function detectPackageManager(cwd) {
122
- if (existsSync(resolve(cwd, "bun.lockb")) || existsSync(resolve(cwd, "bun.lock"))) return "bun";
123
- if (existsSync(resolve(cwd, "pnpm-lock.yaml"))) return "pnpm";
124
- if (existsSync(resolve(cwd, "yarn.lock"))) return "yarn";
294
+ if (existsSync2(resolve2(cwd, "bun.lockb")) || existsSync2(resolve2(cwd, "bun.lock"))) return "bun";
295
+ if (existsSync2(resolve2(cwd, "pnpm-lock.yaml"))) return "pnpm";
296
+ if (existsSync2(resolve2(cwd, "yarn.lock"))) return "yarn";
125
297
  return "npm";
126
298
  }
127
299
  function getInstallCommand(pm) {
@@ -228,11 +400,11 @@ function flowbookPlugin(options = {}) {
228
400
  }
229
401
 
230
402
  // src/node/server.ts
231
- import { resolve as resolve2, dirname as dirname2 } from "path";
403
+ import { resolve as resolve3, dirname as dirname2 } from "path";
232
404
  import { fileURLToPath as fileURLToPath2 } from "url";
233
405
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
234
406
  function getClientDir() {
235
- return resolve2(__dirname2, "..", "src", "client");
407
+ return resolve3(__dirname2, "..", "src", "client");
236
408
  }
237
409
  function createConfig(options) {
238
410
  const cwd = options.cwd ?? process.cwd();
@@ -253,7 +425,7 @@ function createConfig(options) {
253
425
  port: options.port ?? 6200
254
426
  },
255
427
  build: {
256
- outDir: resolve2(cwd, options.outDir ?? "flowbook-static"),
428
+ outDir: resolve3(cwd, options.outDir ?? "flowbook-static"),
257
429
  emptyOutDir: true
258
430
  }
259
431
  };
@@ -290,6 +462,16 @@ async function main() {
290
462
  await buildStatic({ outDir });
291
463
  break;
292
464
  }
465
+ case "skill": {
466
+ const agent = args[1];
467
+ if (!agent) {
468
+ printSkillUsage();
469
+ break;
470
+ }
471
+ const isGlobal = args.includes("--global") || args.includes("-g");
472
+ installSkills(agent, isGlobal);
473
+ break;
474
+ }
293
475
  default:
294
476
  printUsage();
295
477
  break;
@@ -308,10 +490,13 @@ function printUsage() {
308
490
  flowbook init Set up Flowbook in your project
309
491
  flowbook dev [--port 6200] Start the dev server
310
492
  flowbook build [--out-dir d] Build a static site
493
+ flowbook skill <agent> [-g] Install AI agent skill & command
311
494
 
312
495
  Options:
313
496
  --port <number> Dev server port (default: 6200)
314
497
  --out-dir <path> Build output directory (default: flowbook-static)
498
+
499
+ Run 'flowbook skill' for agent-specific options.
315
500
  `);
316
501
  }
317
502
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowbook",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "flowbook": "./dist/cli.js"
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Analyze codebase and generate Mermaid flowchart documentation using Flowbook
3
+ ---
4
+
5
+ Load the **flowbook** skill and execute the full flowchart generation workflow.
6
+
7
+ When prompted with `/flowbook`, you MUST:
8
+
9
+ 1. Load the `flowbook` skill (SKILL.md) for detailed instructions
10
+ 2. Analyze the codebase for logical flows (API routes, auth, state management, business logic)
11
+ 3. Set up Flowbook if not already initialized (`npx flowbook@latest init`)
12
+ 4. Generate `.flow.md` files with Mermaid diagrams for every significant flow
13
+ 5. Run `npm run flowbook` to verify the build
@@ -0,0 +1,13 @@
1
+ # /flowbook — Flowchart Documentation Generator
2
+
3
+ Analyze codebase and generate Mermaid flowchart documentation using Flowbook.
4
+
5
+ Load the **flowbook** skill and execute the full flowchart generation workflow.
6
+
7
+ When prompted with `/flowbook`, you MUST:
8
+
9
+ 1. Load the `flowbook` skill (SKILL.md) for detailed instructions
10
+ 2. Analyze the codebase for logical flows (API routes, auth, state management, business logic)
11
+ 3. Set up Flowbook if not already initialized (`npx flowbook@latest init`)
12
+ 4. Generate `.flow.md` files with Mermaid diagrams for every significant flow
13
+ 5. Run `npm run flowbook` to verify the build
package/src/node/cli.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { initFlowbook } from "./init";
2
2
  import { startDevServer, buildStatic } from "./server";
3
+ import { installSkills, printSkillUsage } from "./skill";
3
4
 
4
5
  async function main() {
5
6
  const args = process.argv.slice(2);
@@ -22,6 +23,17 @@ async function main() {
22
23
  break;
23
24
  }
24
25
 
26
+ case "skill": {
27
+ const agent = args[1];
28
+ if (!agent) {
29
+ printSkillUsage();
30
+ break;
31
+ }
32
+ const isGlobal = args.includes("--global") || args.includes("-g");
33
+ installSkills(agent, isGlobal);
34
+ break;
35
+ }
36
+
25
37
  default:
26
38
  printUsage();
27
39
  break;
@@ -46,10 +58,13 @@ function printUsage() {
46
58
  flowbook init Set up Flowbook in your project
47
59
  flowbook dev [--port 6200] Start the dev server
48
60
  flowbook build [--out-dir d] Build a static site
61
+ flowbook skill <agent> [-g] Install AI agent skill & command
49
62
 
50
63
  Options:
51
64
  --port <number> Dev server port (default: 6200)
52
65
  --out-dir <path> Build output directory (default: flowbook-static)
66
+
67
+ Run 'flowbook skill' for agent-specific options.
53
68
  `);
54
69
  }
55
70
 
package/src/node/init.ts CHANGED
@@ -1,9 +1,8 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync } from "node:fs";
2
- import { resolve, dirname } from "node:path";
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
3
  import { execSync } from "node:child_process";
4
- import { fileURLToPath } from "node:url";
4
+ import { installAllProjectSkills } from "./skill";
5
5
 
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
6
 
8
7
  const EXAMPLE_FLOW = `---
9
8
  title: Example Flow
@@ -89,34 +88,11 @@ export async function initFlowbook() {
89
88
  }
90
89
 
91
90
  // 5. Install AI agent skill to all supported agent directories
92
- const skillSrc = resolve(__dirname, "..", "src", "skills", "flowbook", "SKILL.md");
93
- const skillDirs = [
94
- resolve(cwd, ".claude", "skills", "flowbook"), // Claude Code
95
- resolve(cwd, ".agents", "skills", "flowbook"), // OpenAI Codex / cross-tool alias
96
- resolve(cwd, ".github", "skills", "flowbook"), // VS Code / GitHub Copilot
97
- resolve(cwd, ".agent", "skills", "flowbook"), // Google Antigravity
98
- resolve(cwd, ".gemini", "skills", "flowbook"), // Gemini CLI
99
- resolve(cwd, ".cursor", "skills", "flowbook"), // Cursor
100
- resolve(cwd, ".windsurf", "skills", "flowbook"), // Windsurf (Codeium)
101
- resolve(cwd, ".amp", "skills", "flowbook"), // AmpCode
102
- resolve(cwd, ".opencode", "skills", "flowbook"), // OpenCode / oh-my-opencode
103
- ];
104
-
105
- if (existsSync(skillSrc)) {
106
- let installed = 0;
107
- for (const dir of skillDirs) {
108
- const dest = resolve(dir, "SKILL.md");
109
- if (!existsSync(dest)) {
110
- mkdirSync(dir, { recursive: true });
111
- copyFileSync(skillSrc, dest);
112
- installed++;
113
- }
114
- }
115
- if (installed > 0) {
116
- console.log(` \u2713 Installed AI agent skill to ${installed} agent directories`);
117
- } else {
118
- console.log(" \u2713 AI agent skills already installed");
119
- }
91
+ const installed = installAllProjectSkills();
92
+ if (installed > 0) {
93
+ console.log(` ✓ Installed AI agent skill to ${installed} agent directories`);
94
+ } else {
95
+ console.log(" AI agent skills already installed");
120
96
  }
121
97
 
122
98
  const run = pm === "yarn" ? "yarn" : `${pm} run`;
@@ -0,0 +1,230 @@
1
+ import { existsSync, mkdirSync, copyFileSync } from "node:fs";
2
+ import { resolve, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { homedir } from "node:os";
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ interface AgentConfig {
9
+ name: string;
10
+ aliases: string[];
11
+ skill: { project: string; global: string };
12
+ command?: { project: string; global: string; format: "frontmatter" | "plain" };
13
+ }
14
+
15
+ const AGENTS: AgentConfig[] = [
16
+ {
17
+ name: "Claude Code",
18
+ aliases: ["claude"],
19
+ skill: {
20
+ project: ".claude/skills/flowbook",
21
+ global: ".claude/skills/flowbook",
22
+ },
23
+ command: {
24
+ project: ".claude/commands",
25
+ global: ".claude/commands",
26
+ format: "frontmatter",
27
+ },
28
+ },
29
+ {
30
+ name: "OpenAI Codex",
31
+ aliases: ["codex"],
32
+ skill: {
33
+ project: ".agents/skills/flowbook",
34
+ global: ".codex/skills/flowbook",
35
+ },
36
+ },
37
+ {
38
+ name: "VS Code / GitHub Copilot",
39
+ aliases: ["copilot", "vscode"],
40
+ skill: {
41
+ project: ".github/skills/flowbook",
42
+ global: ".copilot/skills/flowbook",
43
+ },
44
+ },
45
+ {
46
+ name: "Google Antigravity",
47
+ aliases: ["antigravity"],
48
+ skill: {
49
+ project: ".agent/skills/flowbook",
50
+ global: ".gemini/antigravity/skills/flowbook",
51
+ },
52
+ },
53
+ {
54
+ name: "Gemini CLI",
55
+ aliases: ["gemini"],
56
+ skill: {
57
+ project: ".gemini/skills/flowbook",
58
+ global: ".gemini/skills/flowbook",
59
+ },
60
+ },
61
+ {
62
+ name: "Cursor",
63
+ aliases: ["cursor"],
64
+ skill: {
65
+ project: ".cursor/skills/flowbook",
66
+ global: ".cursor/skills/flowbook",
67
+ },
68
+ command: {
69
+ project: ".cursor/commands",
70
+ global: ".cursor/commands",
71
+ format: "plain",
72
+ },
73
+ },
74
+ {
75
+ name: "Windsurf",
76
+ aliases: ["windsurf"],
77
+ skill: {
78
+ project: ".windsurf/skills/flowbook",
79
+ global: ".codeium/windsurf/skills/flowbook",
80
+ },
81
+ command: {
82
+ project: ".windsurf/workflows",
83
+ global: ".codeium/windsurf/workflows",
84
+ format: "plain",
85
+ },
86
+ },
87
+ {
88
+ name: "AmpCode",
89
+ aliases: ["amp"],
90
+ skill: {
91
+ project: ".amp/skills/flowbook",
92
+ global: ".config/agents/skills/flowbook",
93
+ },
94
+ },
95
+ {
96
+ name: "OpenCode",
97
+ aliases: ["opencode"],
98
+ skill: {
99
+ project: ".opencode/skills/flowbook",
100
+ global: ".config/opencode/skills/flowbook",
101
+ },
102
+ command: {
103
+ project: ".opencode/command",
104
+ global: ".config/opencode/command",
105
+ format: "frontmatter",
106
+ },
107
+ },
108
+ ];
109
+
110
+ function getSkillSrc(): string {
111
+ return resolve(__dirname, "..", "src", "skills", "flowbook", "SKILL.md");
112
+ }
113
+
114
+ function getCommandSrc(format: "frontmatter" | "plain"): string {
115
+ const file = format === "frontmatter" ? "flowbook.md" : "flowbook.plain.md";
116
+ return resolve(__dirname, "..", "src", "commands", file);
117
+ }
118
+
119
+ function resolveAgents(agentArg: string): AgentConfig[] {
120
+ if (agentArg === "all") return AGENTS;
121
+
122
+ const lower = agentArg.toLowerCase();
123
+ const found = AGENTS.filter(
124
+ (a) =>
125
+ a.aliases.includes(lower) ||
126
+ a.name.toLowerCase().includes(lower),
127
+ );
128
+
129
+ if (found.length === 0) {
130
+ console.error(` Unknown agent: "${agentArg}"`);
131
+ console.error(` Available: all, ${AGENTS.flatMap((a) => a.aliases).join(", ")}`);
132
+ process.exit(1);
133
+ }
134
+
135
+ return found;
136
+ }
137
+
138
+ function installFile(src: string, destDir: string, destFilename: string): boolean {
139
+ const dest = resolve(destDir, destFilename);
140
+ if (existsSync(dest)) return false;
141
+ mkdirSync(destDir, { recursive: true });
142
+ copyFileSync(src, dest);
143
+ return true;
144
+ }
145
+
146
+ export function installSkills(agentArg: string, global: boolean): void {
147
+ const agents = resolveAgents(agentArg);
148
+ const base = global ? homedir() : process.cwd();
149
+ const skillSrc = getSkillSrc();
150
+
151
+ if (!existsSync(skillSrc)) {
152
+ console.error(" ✗ Skill source file not found. Reinstall flowbook.");
153
+ process.exit(1);
154
+ }
155
+
156
+ let skillCount = 0;
157
+ let cmdCount = 0;
158
+
159
+ for (const agent of agents) {
160
+ const skillDir = resolve(base, global ? agent.skill.global : agent.skill.project);
161
+ if (installFile(skillSrc, skillDir, "SKILL.md")) {
162
+ skillCount++;
163
+ }
164
+
165
+ if (agent.command) {
166
+ const cmdSrc = getCommandSrc(agent.command.format);
167
+ if (existsSync(cmdSrc)) {
168
+ const cmdDir = resolve(base, global ? agent.command.global : agent.command.project);
169
+ if (installFile(cmdSrc, cmdDir, "flowbook.md")) {
170
+ cmdCount++;
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ const scope = global ? "global" : "project";
177
+ const agentNames = agents.map((a) => a.name).join(", ");
178
+
179
+ if (skillCount > 0 || cmdCount > 0) {
180
+ if (skillCount > 0) console.log(` ✓ Installed skill to ${skillCount} ${scope} director${skillCount > 1 ? "ies" : "y"}`);
181
+ if (cmdCount > 0) console.log(` ✓ Installed /flowbook command to ${cmdCount} ${scope} director${cmdCount > 1 ? "ies" : "y"}`);
182
+ console.log(` Agents: ${agentNames}`);
183
+ } else {
184
+ console.log(` ✓ Already installed for: ${agentNames}`);
185
+ }
186
+ }
187
+
188
+ /** Used by init.ts — installs skills only (no commands) to all agents at project level */
189
+ export function installAllProjectSkills(): number {
190
+ const cwd = process.cwd();
191
+ const skillSrc = getSkillSrc();
192
+ if (!existsSync(skillSrc)) return 0;
193
+
194
+ let installed = 0;
195
+ for (const agent of AGENTS) {
196
+ const dir = resolve(cwd, agent.skill.project);
197
+ if (installFile(skillSrc, dir, "SKILL.md")) {
198
+ installed++;
199
+ }
200
+ }
201
+ return installed;
202
+ }
203
+
204
+ export function printSkillUsage(): void {
205
+ console.log(`
206
+ flowbook skill — Install AI agent skill & /flowbook command
207
+
208
+ Usage:
209
+ flowbook skill <agent> Install to project
210
+ flowbook skill <agent> --global Install globally
211
+ flowbook skill <agent> -g Install globally (short)
212
+
213
+ Agents:
214
+ all All supported agents
215
+ claude Claude Code
216
+ codex OpenAI Codex
217
+ copilot VS Code / GitHub Copilot
218
+ antigravity Google Antigravity
219
+ gemini Gemini CLI
220
+ cursor Cursor
221
+ windsurf Windsurf (Codeium)
222
+ amp AmpCode
223
+ opencode OpenCode / oh-my-opencode
224
+
225
+ Examples:
226
+ flowbook skill all Install to all agents (project)
227
+ flowbook skill opencode -g Install globally for OpenCode
228
+ flowbook skill claude --global Install globally for Claude Code
229
+ `);
230
+ }