flowbook 0.2.2 → 0.2.4
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/dist/cli.js +17 -17
- package/package.json +1 -1
- package/src/node/skill.ts +22 -23
- package/src/skills/flowbook/SKILL.md +48 -1
package/dist/cli.js
CHANGED
|
@@ -330,10 +330,10 @@ function resolveAgents(agentArg) {
|
|
|
330
330
|
}
|
|
331
331
|
function installFile(src, destDir, destFilename) {
|
|
332
332
|
const dest = resolve3(destDir, destFilename);
|
|
333
|
-
|
|
333
|
+
const existed = existsSync2(dest);
|
|
334
334
|
mkdirSync(destDir, { recursive: true });
|
|
335
335
|
copyFileSync(src, dest);
|
|
336
|
-
return
|
|
336
|
+
return { action: existed ? "updated" : "installed" };
|
|
337
337
|
}
|
|
338
338
|
function installSkills(agentArg, global) {
|
|
339
339
|
const agents = resolveAgents(agentArg);
|
|
@@ -343,32 +343,32 @@ function installSkills(agentArg, global) {
|
|
|
343
343
|
console.error(" \u2717 Skill source file not found. Reinstall flowbook.");
|
|
344
344
|
process.exit(1);
|
|
345
345
|
}
|
|
346
|
-
let
|
|
347
|
-
let
|
|
346
|
+
let installedSkills = 0;
|
|
347
|
+
let updatedSkills = 0;
|
|
348
|
+
let installedCmds = 0;
|
|
349
|
+
let updatedCmds = 0;
|
|
348
350
|
for (const agent of agents) {
|
|
349
351
|
const skillDir = resolve3(base, global ? agent.skill.global : agent.skill.project);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
352
|
+
const skillResult = installFile(skillSrc, skillDir, "SKILL.md");
|
|
353
|
+
if (skillResult.action === "installed") installedSkills++;
|
|
354
|
+
else if (skillResult.action === "updated") updatedSkills++;
|
|
353
355
|
if (agent.command) {
|
|
354
356
|
const cmdSrc = getCommandSrc(agent.command.format);
|
|
355
357
|
if (existsSync2(cmdSrc)) {
|
|
356
358
|
const cmdDir = resolve3(base, global ? agent.command.global : agent.command.project);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
359
|
+
const cmdResult = installFile(cmdSrc, cmdDir, "flowbook.md");
|
|
360
|
+
if (cmdResult.action === "installed") installedCmds++;
|
|
361
|
+
else if (cmdResult.action === "updated") updatedCmds++;
|
|
360
362
|
}
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
const scope = global ? "global" : "project";
|
|
364
366
|
const agentNames = agents.map((a) => a.name).join(", ");
|
|
365
|
-
if (
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
console.log(` \u2713 Already installed for: ${agentNames}`);
|
|
371
|
-
}
|
|
367
|
+
if (installedSkills > 0) console.log(` \u2713 Installed skill to ${installedSkills} ${scope} director${installedSkills > 1 ? "ies" : "y"}`);
|
|
368
|
+
if (updatedSkills > 0) console.log(` \u2713 Updated skill in ${updatedSkills} ${scope} director${updatedSkills > 1 ? "ies" : "y"}`);
|
|
369
|
+
if (installedCmds > 0) console.log(` \u2713 Installed /flowbook command to ${installedCmds} ${scope} director${installedCmds > 1 ? "ies" : "y"}`);
|
|
370
|
+
if (updatedCmds > 0) console.log(` \u2713 Updated /flowbook command in ${updatedCmds} ${scope} director${updatedCmds > 1 ? "ies" : "y"}`);
|
|
371
|
+
console.log(` Agents: ${agentNames}`);
|
|
372
372
|
}
|
|
373
373
|
function printSkillUsage() {
|
|
374
374
|
console.log(`
|
package/package.json
CHANGED
package/src/node/skill.ts
CHANGED
|
@@ -135,12 +135,12 @@ function resolveAgents(agentArg: string): AgentConfig[] {
|
|
|
135
135
|
return found;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
function installFile(src: string, destDir: string, destFilename: string):
|
|
138
|
+
function installFile(src: string, destDir: string, destFilename: string): { action: "installed" | "updated" | "skipped" } {
|
|
139
139
|
const dest = resolve(destDir, destFilename);
|
|
140
|
-
|
|
140
|
+
const existed = existsSync(dest);
|
|
141
141
|
mkdirSync(destDir, { recursive: true });
|
|
142
142
|
copyFileSync(src, dest);
|
|
143
|
-
return
|
|
143
|
+
return { action: existed ? "updated" : "installed" };
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
export function installSkills(agentArg: string, global: boolean): void {
|
|
@@ -153,22 +153,24 @@ export function installSkills(agentArg: string, global: boolean): void {
|
|
|
153
153
|
process.exit(1);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
let
|
|
157
|
-
let
|
|
156
|
+
let installedSkills = 0;
|
|
157
|
+
let updatedSkills = 0;
|
|
158
|
+
let installedCmds = 0;
|
|
159
|
+
let updatedCmds = 0;
|
|
158
160
|
|
|
159
161
|
for (const agent of agents) {
|
|
160
162
|
const skillDir = resolve(base, global ? agent.skill.global : agent.skill.project);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
const skillResult = installFile(skillSrc, skillDir, "SKILL.md");
|
|
164
|
+
if (skillResult.action === "installed") installedSkills++;
|
|
165
|
+
else if (skillResult.action === "updated") updatedSkills++;
|
|
164
166
|
|
|
165
167
|
if (agent.command) {
|
|
166
168
|
const cmdSrc = getCommandSrc(agent.command.format);
|
|
167
169
|
if (existsSync(cmdSrc)) {
|
|
168
170
|
const cmdDir = resolve(base, global ? agent.command.global : agent.command.project);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
const cmdResult = installFile(cmdSrc, cmdDir, "flowbook.md");
|
|
172
|
+
if (cmdResult.action === "installed") installedCmds++;
|
|
173
|
+
else if (cmdResult.action === "updated") updatedCmds++;
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
}
|
|
@@ -176,13 +178,11 @@ export function installSkills(agentArg: string, global: boolean): void {
|
|
|
176
178
|
const scope = global ? "global" : "project";
|
|
177
179
|
const agentNames = agents.map((a) => a.name).join(", ");
|
|
178
180
|
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
console.log(` ✓ Already installed for: ${agentNames}`);
|
|
185
|
-
}
|
|
181
|
+
if (installedSkills > 0) console.log(` ✓ Installed skill to ${installedSkills} ${scope} director${installedSkills > 1 ? "ies" : "y"}`);
|
|
182
|
+
if (updatedSkills > 0) console.log(` ✓ Updated skill in ${updatedSkills} ${scope} director${updatedSkills > 1 ? "ies" : "y"}`);
|
|
183
|
+
if (installedCmds > 0) console.log(` ✓ Installed /flowbook command to ${installedCmds} ${scope} director${installedCmds > 1 ? "ies" : "y"}`);
|
|
184
|
+
if (updatedCmds > 0) console.log(` ✓ Updated /flowbook command in ${updatedCmds} ${scope} director${updatedCmds > 1 ? "ies" : "y"}`);
|
|
185
|
+
console.log(` Agents: ${agentNames}`);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/** Used by init.ts — installs skills only (no commands) to all agents at project level */
|
|
@@ -191,14 +191,13 @@ export function installAllProjectSkills(): number {
|
|
|
191
191
|
const skillSrc = getSkillSrc();
|
|
192
192
|
if (!existsSync(skillSrc)) return 0;
|
|
193
193
|
|
|
194
|
-
let
|
|
194
|
+
let count = 0;
|
|
195
195
|
for (const agent of AGENTS) {
|
|
196
196
|
const dir = resolve(cwd, agent.skill.project);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
197
|
+
const result = installFile(skillSrc, dir, "SKILL.md");
|
|
198
|
+
if (result.action !== "skipped") count++;
|
|
200
199
|
}
|
|
201
|
-
return
|
|
200
|
+
return count;
|
|
202
201
|
}
|
|
203
202
|
|
|
204
203
|
export function printSkillUsage(): void {
|
|
@@ -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
|