flowbook 0.2.1 → 0.2.3

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
@@ -36,7 +36,6 @@ flowbook skill <agent> [-g] Install AI agent skill & /flowbook command
36
36
  ### `flowbook init`
37
37
 
38
38
  - Adds `"flowbook"` and `"build-flowbook"` scripts to your `package.json`
39
- - Creates `flows/example.flow.md` as a starter template
40
39
 
41
40
  ### `flowbook dev`
42
41
 
package/dist/cli.js CHANGED
@@ -1,26 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/node/init.ts
4
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
4
+ import { readFileSync, writeFileSync, existsSync } from "fs";
5
5
  import { resolve } from "path";
6
6
  import { execSync } from "child_process";
7
- var EXAMPLE_FLOW = `---
8
- title: Example Flow
9
- category: Getting Started
10
- tags: [example]
11
- order: 1
12
- description: An example flowchart to get you started
13
- ---
14
-
15
- \`\`\`mermaid
16
- flowchart TD
17
- A[Start] --> B{Decision}
18
- B -->|Yes| C[Action A]
19
- B -->|No| D[Action B]
20
- C --> E[End]
21
- D --> E
22
- \`\`\`
23
- `;
24
7
  async function initFlowbook() {
25
8
  const cwd = process.cwd();
26
9
  const pkgPath = resolve(cwd, "package.json");
@@ -55,15 +38,6 @@ async function initFlowbook() {
55
38
  } else {
56
39
  console.log(" \u2713 Scripts already exist in package.json");
57
40
  }
58
- const flowsDir = resolve(cwd, "flows");
59
- const examplePath = resolve(flowsDir, "example.flow.md");
60
- if (!existsSync(examplePath)) {
61
- mkdirSync(flowsDir, { recursive: true });
62
- writeFileSync(examplePath, EXAMPLE_FLOW);
63
- console.log(" \u2713 Created flows/example.flow.md");
64
- } else {
65
- console.log(" \u2713 Example flow already exists");
66
- }
67
41
  const gitignorePath = resolve(cwd, ".gitignore");
68
42
  if (existsSync(gitignorePath)) {
69
43
  const gitignore = readFileSync(gitignorePath, "utf-8");
@@ -235,7 +209,7 @@ async function buildStatic(options) {
235
209
  }
236
210
 
237
211
  // src/node/skill.ts
238
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, copyFileSync } from "fs";
212
+ import { existsSync as existsSync2, mkdirSync, copyFileSync } from "fs";
239
213
  import { resolve as resolve3, dirname as dirname2 } from "path";
240
214
  import { fileURLToPath as fileURLToPath2 } from "url";
241
215
  import { homedir } from "os";
@@ -357,7 +331,7 @@ function resolveAgents(agentArg) {
357
331
  function installFile(src, destDir, destFilename) {
358
332
  const dest = resolve3(destDir, destFilename);
359
333
  if (existsSync2(dest)) return false;
360
- mkdirSync2(destDir, { recursive: true });
334
+ mkdirSync(destDir, { recursive: true });
361
335
  copyFileSync(src, dest);
362
336
  return true;
363
337
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowbook",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "flowbook": "./dist/cli.js"
package/src/node/init.ts CHANGED
@@ -1,26 +1,7 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
1
+ import { readFileSync, writeFileSync, existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { execSync } from "node:child_process";
4
4
 
5
-
6
- const EXAMPLE_FLOW = `---
7
- title: Example Flow
8
- category: Getting Started
9
- tags: [example]
10
- order: 1
11
- description: An example flowchart to get you started
12
- ---
13
-
14
- \`\`\`mermaid
15
- flowchart TD
16
- A[Start] --> B{Decision}
17
- B -->|Yes| C[Action A]
18
- B -->|No| D[Action B]
19
- C --> E[End]
20
- D --> E
21
- \`\`\`
22
- `;
23
-
24
5
  export async function initFlowbook() {
25
6
  const cwd = process.cwd();
26
7
  const pkgPath = resolve(cwd, "package.json");
@@ -64,18 +45,6 @@ export async function initFlowbook() {
64
45
  console.log(" ✓ Scripts already exist in package.json");
65
46
  }
66
47
 
67
- // 3. Create example flow file
68
- const flowsDir = resolve(cwd, "flows");
69
- const examplePath = resolve(flowsDir, "example.flow.md");
70
-
71
- if (!existsSync(examplePath)) {
72
- mkdirSync(flowsDir, { recursive: true });
73
- writeFileSync(examplePath, EXAMPLE_FLOW);
74
- console.log(" ✓ Created flows/example.flow.md");
75
- } else {
76
- console.log(" ✓ Example flow already exists");
77
- }
78
-
79
48
  // 4. Add flowbook-static to .gitignore
80
49
  const gitignorePath = resolve(cwd, ".gitignore");
81
50
  if (existsSync(gitignorePath)) {
@@ -275,6 +275,37 @@ flowchart TD
275
275
  I[\Output\] %% Reverse parallelogram: response
276
276
  ```
277
277
 
278
+ #### Label Quoting Rules (MANDATORY)
279
+
280
+ Node labels containing special characters **MUST** be wrapped in double quotes to prevent Mermaid parse errors.
281
+
282
+ **Characters that REQUIRE quoting:**
283
+
284
+ | Character | Why it breaks | Unquoted (BROKEN) | Quoted (CORRECT) |
285
+ |-----------|--------------|-------------------|------------------|
286
+ | `()` | Conflicts with `([...])` stadium and `(...)` rounded shapes | `A([Agent run() Start])` | `A(["Agent run() Start"])` |
287
+ | `{}` | Conflicts with `{...}` diamond shape | `B{tokio::select!{}}` | `B{"tokio::select!{}"}` |
288
+ | `[]` | Conflicts with `[...]` rectangle shape | `C[arr[0] value]` | `C["arr[0] value"]` |
289
+ | `::` | Interpreted as Mermaid class/namespace syntax | `D[std::io::Error]` | `D["std::io::Error"]` |
290
+ | `#` | Interpreted as Unicode escape or comment | `E[Issue #42]` | `E["Issue #42"]` |
291
+ | `&` | Interpreted as HTML entity start | `F[A & B]` | `F["A & B"]` |
292
+
293
+ **Rule: When in doubt, quote it.** Quoting a label that doesn't need it causes no harm. Unquoted special characters WILL break rendering.
294
+
295
+ **Examples of correct quoting by node shape:**
296
+
297
+ ```mermaid
298
+ flowchart TD
299
+ A(["fn main() entry"]) %% Stadium with parens
300
+ B["process_data(input)"] %% Rectangle with parens
301
+ C{"is_valid(x)?"} %% Diamond with parens
302
+ D[["handle_error(err)"]] %% Subroutine with parens
303
+ E{{"validate(req)"}} %% Hexagon with parens
304
+ F["Config::new()"] %% Rectangle with double colon
305
+ ```
306
+
307
+ **NEVER generate unquoted labels containing `()`, `{}`, `[]`, `::`, `#`, or `&`.**
308
+
278
309
  #### Edge Labels
279
310
 
280
311
  ```mermaid
@@ -369,7 +400,7 @@ description: POST /api/auth/login — validates credentials and returns JWT toke
369
400
  ```mermaid
370
401
  flowchart TD
371
402
  A([POST /api/auth/login]) --> B[/Parse Request Body/]
372
- B --> C{{Validate Email & Password}}
403
+ B --> C{{"Validate Email & Password"}}
373
404
  C -->|Invalid| D[\400 Bad Request/]
374
405
  C -->|Valid| E[(Find User by Email)]
375
406
  E -->|Not Found| F[\401 Unauthorized/]
@@ -402,6 +433,16 @@ For each generated `.flow.md` file:
402
433
  1. Verify YAML frontmatter is valid (title, category present)
403
434
  2. Verify mermaid code block is properly fenced (``` mermaid ```)
404
435
  3. Verify mermaid syntax has no obvious errors (matched brackets, valid node IDs)
436
+ 4. **Special Character Validation (CRITICAL)**: Scan ALL node labels for unquoted special characters:
437
+ - `()` inside any node shape → MUST be quoted: `A(["label()"])` not `A([label()])`
438
+ - `{}` inside any node shape → MUST be quoted: `A{"label{}"}` not `A{label{}}`
439
+ - `[]` inside any node shape → MUST be quoted: `A["label[]"]` not `A[label[]]`
440
+ - `::` anywhere in labels → MUST be quoted: `A["std::io"]` not `A[std::io]`
441
+ - `#` anywhere in labels → MUST be quoted: `A["Issue #1"]` not `A[Issue #1]`
442
+ - `&` anywhere in labels → MUST be quoted: `A["A & B"]` not `A[A & B]`
443
+ - If ANY unquoted special characters are found, fix them BEFORE proceeding to build
444
+ 5. Verify all node IDs are unique within each diagram
445
+ 6. Verify subgraph labels don't contain special characters
405
446
 
406
447
  ### 5.2 Build Verification
407
448
 
@@ -483,6 +524,12 @@ Build: ✅ / ❌
483
524
  ### Mermaid syntax errors
484
525
  - **Brackets**: Every `[`, `{`, `(` must be closed
485
526
  - **Special characters in labels**: Wrap in double quotes: `A["User's Input"]`
527
+ - **Parentheses in labels** (MOST COMMON): `A([run() Start])` → Parse error. Fix: `A(["run() Start"])`
528
+ - **Double colons in labels**: `A[std::io::Error]` → Interpreted as class syntax. Fix: `A["std::io::Error"]`
529
+ - **Curly braces in labels**: `B{select!{}}` → Conflicts with diamond shape. Fix: `B{"select!{}"}`
530
+ - **Square brackets in labels**: `C[arr[0]]` → Conflicts with rectangle shape. Fix: `C["arr[0]"]`
531
+ - **Hash in labels**: `D[Issue #42]` → Unicode escape. Fix: `D["Issue #42"]`
532
+ - **Ampersand in labels**: `E[A & B]` → HTML entity. Fix: `E["A & B"]`
486
533
  - **Arrow syntax**: Use `-->` for solid, `-.->` for dotted, `==>` for thick
487
534
  - **Node ID reuse**: Each node ID must be unique per diagram. Reuse ID to reference same node.
488
535
  - **Subgraph naming**: Subgraph labels cannot contain special characters