@jspg-ai/coding-bb 0.0.1 → 0.0.2-beta.23
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/cbb/bin/cbb.js +6 -0
- package/cbb/bin/cbbm.js +6 -0
- package/cbb/dev-standards/rules/cbb-ai-behavior.md +103 -103
- package/cbb/dev-standards/rules/cbb-priority.md +55 -55
- package/cbb/lib/install/claude-code.js +2 -0
- package/cbb/lib/install/init.js +496 -290
- package/cbb/lib/install/opencode.js +31 -0
- package/cbb/lib/install/qoder.js +2 -0
- package/cbb/lib/install/workspaces.js +53 -13
- package/cbb/lib/openspec/index.js +3 -3
- package/cbb/lib/superpowers/index.js +1 -1
- package/cbb/lib/utils/check-update.js +16 -14
- package/cbb/lib/utils/gitignore.js +2 -1
- package/cbb/lib/utils/output.js +92 -4
- package/cbb/lib/utils/version.js +2 -31
- package/cbb/lib/wiki/cli.js +25 -25
- package/cbb/lib/wiki/index.js +16 -16
- package/cbb/tools/cbb-design-to-wiki/SKILL.md +14 -14
- package/cbb/tools/cbb-wiki-ops/SKILL.md +22 -22
- package/cbb/worktrees/_shared/scripts/find-target-worktree.js +2 -2
- package/cbb/worktrees/_shared/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/_shared/scripts/push-core.js +18 -0
- package/cbb/worktrees/commands/{close.md → worktree-close.md} +8 -8
- package/cbb/worktrees/commands/{init.md → worktree-init.md} +9 -9
- package/cbb/worktrees/commands/{push.md → worktree-push.md} +3 -3
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/SKILL.md +20 -20
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/check-env.js +4 -4
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/check-unarchived.js +1 -1
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/delete-branches.js +3 -3
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/discover-apps.js +6 -6
- package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-close}/scripts/find-target-worktree.js +2 -2
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-close}/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/remove-worktrees.js +4 -4
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/safety-check.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/SKILL.md +81 -107
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/auto-open.js +1 -1
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-env-deep.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-env.js +6 -6
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-repos.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/create-branches.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/create-worktrees.js +8 -7
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-init}/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/install-ai.js +33 -33
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/parse-config.js +7 -7
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/push-branches.js +9 -7
- package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-init}/scripts/push-core.js +18 -0
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/sync-repos.js +10 -12
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/update-gitignore.js +19 -6
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/SKILL.md +16 -16
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/commit-worktrees.js +2 -2
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/find-target-worktree.js +2 -2
- package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-push}/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/push-core.js +18 -0
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/push-worktrees.js +7 -5
- package/config/workspace-config.sample.json +18 -0
- package/config/workspaces.json +11 -6
- package/openspec/.version +6 -6
- package/openspec/commands/apply.md +7 -7
- package/openspec/commands/archive.md +7 -7
- package/openspec/commands/bulk-archive.md +7 -7
- package/openspec/commands/continue.md +7 -7
- package/openspec/commands/explore.md +7 -7
- package/openspec/commands/ff.md +7 -7
- package/openspec/commands/new.md +7 -7
- package/openspec/commands/onboard.md +7 -7
- package/openspec/commands/propose.md +7 -7
- package/openspec/commands/sync.md +7 -7
- package/openspec/commands/update.md +7 -7
- package/openspec/commands/verify.md +7 -7
- package/openspec/skills/openspec-apply-change/SKILL.md +187 -187
- package/openspec/skills/openspec-archive-change/SKILL.md +181 -181
- package/openspec/skills/openspec-bulk-archive-change/SKILL.md +338 -338
- package/openspec/skills/openspec-continue-change/SKILL.md +117 -117
- package/openspec/skills/openspec-explore/SKILL.md +342 -342
- package/openspec/skills/openspec-ff-change/SKILL.md +116 -116
- package/openspec/skills/openspec-new-change/SKILL.md +76 -76
- package/openspec/skills/openspec-onboard/SKILL.md +560 -560
- package/openspec/skills/openspec-propose/SKILL.md +162 -162
- package/openspec/skills/openspec-sync-specs/SKILL.md +261 -261
- package/openspec/skills/openspec-update-change/SKILL.md +90 -90
- package/openspec/skills/openspec-verify-change/SKILL.md +174 -174
- package/package.json +4 -3
- package/superpowers/.version +6 -6
- package/superpowers/skills/brainstorming/SKILL.md +250 -250
- package/superpowers/skills/brainstorming/scripts/helper.js +167 -167
- package/superpowers/skills/brainstorming/scripts/start-server.sh +209 -209
- package/superpowers/skills/brainstorming/scripts/stop-server.sh +120 -120
- package/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +49 -49
- package/superpowers/skills/brainstorming/visual-companion.md +299 -299
- package/superpowers/skills/dispatching-parallel-agents/SKILL.md +167 -167
- package/superpowers/skills/executing-plans/SKILL.md +64 -64
- package/superpowers/skills/finishing-a-development-branch/SKILL.md +225 -225
- package/superpowers/skills/receiving-code-review/SKILL.md +205 -205
- package/superpowers/skills/requesting-code-review/SKILL.md +95 -95
- package/superpowers/skills/requesting-code-review/code-reviewer.md +181 -181
- package/superpowers/skills/subagent-driven-development/SKILL.md +568 -568
- package/superpowers/skills/subagent-driven-development/implementer-prompt.md +154 -154
- package/superpowers/skills/subagent-driven-development/re-review-prompt.md +115 -115
- package/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +207 -207
- package/superpowers/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/superpowers/skills/systematic-debugging/SKILL.md +283 -283
- package/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/superpowers/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/superpowers/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/superpowers/skills/systematic-debugging/find-polluter.sh +72 -72
- package/superpowers/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/superpowers/skills/systematic-debugging/test-academic.md +14 -14
- package/superpowers/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/superpowers/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/superpowers/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/superpowers/skills/test-driven-development/SKILL.md +320 -320
- package/superpowers/skills/test-driven-development/writing-good-tests.md +198 -198
- package/superpowers/skills/using-git-worktrees/SKILL.md +167 -167
- package/superpowers/skills/using-superpowers/SKILL.md +63 -63
- package/superpowers/skills/using-superpowers/references/antigravity-tools.md +23 -23
- package/superpowers/skills/using-superpowers/references/codex-tools.md +108 -108
- package/superpowers/skills/using-superpowers/references/gemini-tools.md +63 -63
- package/superpowers/skills/using-superpowers/references/hermes-tools.md +56 -56
- package/superpowers/skills/using-superpowers/references/pi-tools.md +16 -16
- package/superpowers/skills/verification-before-completion/SKILL.md +120 -120
- package/superpowers/skills/writing-plans/SKILL.md +171 -171
- package/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +49 -49
- package/superpowers/skills/writing-skills/SKILL.md +679 -679
- package/superpowers/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/superpowers/skills/writing-skills/persuasion-principles.md +187 -187
- package/superpowers/skills/writing-skills/render-graphs.js +169 -169
- package/superpowers/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/cbb/worktrees/commands/extend.md +0 -65
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/find-target-worktree.js +0 -96
- package/cbb/worktrees/skills/openspec-extend-worktree/SKILL.md +0 -390
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-env.js +0 -95
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-repos.js +0 -98
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/create-branches-and-worktrees.js +0 -135
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/install-ai.js +0 -150
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/list-available-apps.js +0 -88
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/sync-repos.js +0 -93
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/find-workspace-root.js +0 -67
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/generate-app-options.js +0 -93
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/push-core.js +0 -136
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/silence-popup.js +0 -23
- package/config/config.sample.json +0 -16
- /package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/silence-popup.js +0 -0
- /package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-init}/scripts/silence-popup.js +0 -0
- /package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-push}/scripts/silence-popup.js +0 -0
|
@@ -1,169 +1,169 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Render graphviz diagrams from a skill's SKILL.md to SVG files.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* ./render-graphs.js <skill-directory> # Render each diagram separately
|
|
8
|
-
* ./render-graphs.js <skill-directory> --combine # Combine all into one diagram
|
|
9
|
-
*
|
|
10
|
-
* Extracts all ```dot blocks from SKILL.md and renders to SVG.
|
|
11
|
-
* Useful for helping your human partner visualize the process flows.
|
|
12
|
-
*
|
|
13
|
-
* Requires: graphviz (dot) installed on system
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import * as fs from 'fs';
|
|
17
|
-
import * as path from 'path';
|
|
18
|
-
import { execFileSync } from 'child_process';
|
|
19
|
-
|
|
20
|
-
function extractDotBlocks(markdown) {
|
|
21
|
-
const blocks = [];
|
|
22
|
-
const regex = /```dot\n([\s\S]*?)```/g;
|
|
23
|
-
let match;
|
|
24
|
-
|
|
25
|
-
while ((match = regex.exec(markdown)) !== null) {
|
|
26
|
-
const content = match[1].trim();
|
|
27
|
-
|
|
28
|
-
// Extract digraph name
|
|
29
|
-
const nameMatch = content.match(/digraph\s+(\w+)/);
|
|
30
|
-
const name = nameMatch ? nameMatch[1] : `graph_${blocks.length + 1}`;
|
|
31
|
-
|
|
32
|
-
blocks.push({ name, content });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return blocks;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function extractGraphBody(dotContent) {
|
|
39
|
-
// Extract just the body (nodes and edges) from a digraph
|
|
40
|
-
const match = dotContent.match(/digraph\s+\w+\s*\{([\s\S]*)\}/);
|
|
41
|
-
if (!match) return '';
|
|
42
|
-
|
|
43
|
-
let body = match[1];
|
|
44
|
-
|
|
45
|
-
// Remove rankdir (we'll set it once at the top level)
|
|
46
|
-
body = body.replace(/^\s*rankdir\s*=\s*\w+\s*;?\s*$/gm, '');
|
|
47
|
-
|
|
48
|
-
return body.trim();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function combineGraphs(blocks, skillName) {
|
|
52
|
-
const bodies = blocks.map((block, i) => {
|
|
53
|
-
const body = extractGraphBody(block.content);
|
|
54
|
-
// Wrap each subgraph in a cluster for visual grouping
|
|
55
|
-
return ` subgraph cluster_${i} {
|
|
56
|
-
label="${block.name}";
|
|
57
|
-
${body.split('\n').map(line => ' ' + line).join('\n')}
|
|
58
|
-
}`;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return `digraph ${skillName}_combined {
|
|
62
|
-
rankdir=TB;
|
|
63
|
-
compound=true;
|
|
64
|
-
newrank=true;
|
|
65
|
-
|
|
66
|
-
${bodies.join('\n\n')}
|
|
67
|
-
}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function renderToSvg(dotContent) {
|
|
71
|
-
try {
|
|
72
|
-
return execFileSync('dot', ['-Tsvg'], {
|
|
73
|
-
input: dotContent,
|
|
74
|
-
encoding: 'utf-8',
|
|
75
|
-
maxBuffer: 10 * 1024 * 1024
|
|
76
|
-
});
|
|
77
|
-
} catch (err) {
|
|
78
|
-
console.error('Error running dot:', err.message);
|
|
79
|
-
if (err.stderr) console.error(err.stderr.toString());
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function main() {
|
|
85
|
-
const args = process.argv.slice(2);
|
|
86
|
-
const combine = args.includes('--combine');
|
|
87
|
-
const skillDirArg = args.find(a => !a.startsWith('--'));
|
|
88
|
-
|
|
89
|
-
if (!skillDirArg) {
|
|
90
|
-
console.error('Usage: render-graphs.js <skill-directory> [--combine]');
|
|
91
|
-
console.error('');
|
|
92
|
-
console.error('Options:');
|
|
93
|
-
console.error(' --combine Combine all diagrams into one SVG');
|
|
94
|
-
console.error('');
|
|
95
|
-
console.error('Example:');
|
|
96
|
-
console.error(' ./render-graphs.js ../subagent-driven-development');
|
|
97
|
-
console.error(' ./render-graphs.js ../subagent-driven-development --combine');
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const skillDir = path.resolve(skillDirArg);
|
|
102
|
-
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
103
|
-
const skillName = path.basename(skillDir).replace(/-/g, '_');
|
|
104
|
-
|
|
105
|
-
if (!fs.existsSync(skillFile)) {
|
|
106
|
-
console.error(`Error: ${skillFile} not found`);
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Check if dot is available. Run the binary directly rather than probing
|
|
111
|
-
// with `which`, which is not a command on Windows.
|
|
112
|
-
try {
|
|
113
|
-
execFileSync('dot', ['-V'], { stdio: 'ignore' });
|
|
114
|
-
} catch {
|
|
115
|
-
console.error('Error: graphviz (dot) not found. Install with:');
|
|
116
|
-
console.error(' brew install graphviz # macOS');
|
|
117
|
-
console.error(' apt install graphviz # Linux');
|
|
118
|
-
process.exit(1);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const markdown = fs.readFileSync(skillFile, 'utf-8');
|
|
122
|
-
const blocks = extractDotBlocks(markdown);
|
|
123
|
-
|
|
124
|
-
if (blocks.length === 0) {
|
|
125
|
-
console.log('No ```dot blocks found in', skillFile);
|
|
126
|
-
process.exit(0);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
console.log(`Found ${blocks.length} diagram(s) in ${path.basename(skillDir)}/SKILL.md`);
|
|
130
|
-
|
|
131
|
-
const outputDir = path.join(skillDir, 'diagrams');
|
|
132
|
-
if (!fs.existsSync(outputDir)) {
|
|
133
|
-
fs.mkdirSync(outputDir);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (combine) {
|
|
137
|
-
// Combine all graphs into one
|
|
138
|
-
const combined = combineGraphs(blocks, skillName);
|
|
139
|
-
const svg = renderToSvg(combined);
|
|
140
|
-
if (svg) {
|
|
141
|
-
const outputPath = path.join(outputDir, `${skillName}_combined.svg`);
|
|
142
|
-
fs.writeFileSync(outputPath, svg);
|
|
143
|
-
console.log(` Rendered: ${skillName}_combined.svg`);
|
|
144
|
-
|
|
145
|
-
// Also write the dot source for debugging
|
|
146
|
-
const dotPath = path.join(outputDir, `${skillName}_combined.dot`);
|
|
147
|
-
fs.writeFileSync(dotPath, combined);
|
|
148
|
-
console.log(` Source: ${skillName}_combined.dot`);
|
|
149
|
-
} else {
|
|
150
|
-
console.error(' Failed to render combined diagram');
|
|
151
|
-
}
|
|
152
|
-
} else {
|
|
153
|
-
// Render each separately
|
|
154
|
-
for (const block of blocks) {
|
|
155
|
-
const svg = renderToSvg(block.content);
|
|
156
|
-
if (svg) {
|
|
157
|
-
const outputPath = path.join(outputDir, `${block.name}.svg`);
|
|
158
|
-
fs.writeFileSync(outputPath, svg);
|
|
159
|
-
console.log(` Rendered: ${block.name}.svg`);
|
|
160
|
-
} else {
|
|
161
|
-
console.error(` Failed: ${block.name}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
console.log(`\nOutput: ${outputDir}/`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Render graphviz diagrams from a skill's SKILL.md to SVG files.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* ./render-graphs.js <skill-directory> # Render each diagram separately
|
|
8
|
+
* ./render-graphs.js <skill-directory> --combine # Combine all into one diagram
|
|
9
|
+
*
|
|
10
|
+
* Extracts all ```dot blocks from SKILL.md and renders to SVG.
|
|
11
|
+
* Useful for helping your human partner visualize the process flows.
|
|
12
|
+
*
|
|
13
|
+
* Requires: graphviz (dot) installed on system
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { execFileSync } from 'child_process';
|
|
19
|
+
|
|
20
|
+
function extractDotBlocks(markdown) {
|
|
21
|
+
const blocks = [];
|
|
22
|
+
const regex = /```dot\n([\s\S]*?)```/g;
|
|
23
|
+
let match;
|
|
24
|
+
|
|
25
|
+
while ((match = regex.exec(markdown)) !== null) {
|
|
26
|
+
const content = match[1].trim();
|
|
27
|
+
|
|
28
|
+
// Extract digraph name
|
|
29
|
+
const nameMatch = content.match(/digraph\s+(\w+)/);
|
|
30
|
+
const name = nameMatch ? nameMatch[1] : `graph_${blocks.length + 1}`;
|
|
31
|
+
|
|
32
|
+
blocks.push({ name, content });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return blocks;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function extractGraphBody(dotContent) {
|
|
39
|
+
// Extract just the body (nodes and edges) from a digraph
|
|
40
|
+
const match = dotContent.match(/digraph\s+\w+\s*\{([\s\S]*)\}/);
|
|
41
|
+
if (!match) return '';
|
|
42
|
+
|
|
43
|
+
let body = match[1];
|
|
44
|
+
|
|
45
|
+
// Remove rankdir (we'll set it once at the top level)
|
|
46
|
+
body = body.replace(/^\s*rankdir\s*=\s*\w+\s*;?\s*$/gm, '');
|
|
47
|
+
|
|
48
|
+
return body.trim();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function combineGraphs(blocks, skillName) {
|
|
52
|
+
const bodies = blocks.map((block, i) => {
|
|
53
|
+
const body = extractGraphBody(block.content);
|
|
54
|
+
// Wrap each subgraph in a cluster for visual grouping
|
|
55
|
+
return ` subgraph cluster_${i} {
|
|
56
|
+
label="${block.name}";
|
|
57
|
+
${body.split('\n').map(line => ' ' + line).join('\n')}
|
|
58
|
+
}`;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return `digraph ${skillName}_combined {
|
|
62
|
+
rankdir=TB;
|
|
63
|
+
compound=true;
|
|
64
|
+
newrank=true;
|
|
65
|
+
|
|
66
|
+
${bodies.join('\n\n')}
|
|
67
|
+
}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function renderToSvg(dotContent) {
|
|
71
|
+
try {
|
|
72
|
+
return execFileSync('dot', ['-Tsvg'], {
|
|
73
|
+
input: dotContent,
|
|
74
|
+
encoding: 'utf-8',
|
|
75
|
+
maxBuffer: 10 * 1024 * 1024
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error('Error running dot:', err.message);
|
|
79
|
+
if (err.stderr) console.error(err.stderr.toString());
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function main() {
|
|
85
|
+
const args = process.argv.slice(2);
|
|
86
|
+
const combine = args.includes('--combine');
|
|
87
|
+
const skillDirArg = args.find(a => !a.startsWith('--'));
|
|
88
|
+
|
|
89
|
+
if (!skillDirArg) {
|
|
90
|
+
console.error('Usage: render-graphs.js <skill-directory> [--combine]');
|
|
91
|
+
console.error('');
|
|
92
|
+
console.error('Options:');
|
|
93
|
+
console.error(' --combine Combine all diagrams into one SVG');
|
|
94
|
+
console.error('');
|
|
95
|
+
console.error('Example:');
|
|
96
|
+
console.error(' ./render-graphs.js ../subagent-driven-development');
|
|
97
|
+
console.error(' ./render-graphs.js ../subagent-driven-development --combine');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const skillDir = path.resolve(skillDirArg);
|
|
102
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
103
|
+
const skillName = path.basename(skillDir).replace(/-/g, '_');
|
|
104
|
+
|
|
105
|
+
if (!fs.existsSync(skillFile)) {
|
|
106
|
+
console.error(`Error: ${skillFile} not found`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check if dot is available. Run the binary directly rather than probing
|
|
111
|
+
// with `which`, which is not a command on Windows.
|
|
112
|
+
try {
|
|
113
|
+
execFileSync('dot', ['-V'], { stdio: 'ignore' });
|
|
114
|
+
} catch {
|
|
115
|
+
console.error('Error: graphviz (dot) not found. Install with:');
|
|
116
|
+
console.error(' brew install graphviz # macOS');
|
|
117
|
+
console.error(' apt install graphviz # Linux');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const markdown = fs.readFileSync(skillFile, 'utf-8');
|
|
122
|
+
const blocks = extractDotBlocks(markdown);
|
|
123
|
+
|
|
124
|
+
if (blocks.length === 0) {
|
|
125
|
+
console.log('No ```dot blocks found in', skillFile);
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(`Found ${blocks.length} diagram(s) in ${path.basename(skillDir)}/SKILL.md`);
|
|
130
|
+
|
|
131
|
+
const outputDir = path.join(skillDir, 'diagrams');
|
|
132
|
+
if (!fs.existsSync(outputDir)) {
|
|
133
|
+
fs.mkdirSync(outputDir);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (combine) {
|
|
137
|
+
// Combine all graphs into one
|
|
138
|
+
const combined = combineGraphs(blocks, skillName);
|
|
139
|
+
const svg = renderToSvg(combined);
|
|
140
|
+
if (svg) {
|
|
141
|
+
const outputPath = path.join(outputDir, `${skillName}_combined.svg`);
|
|
142
|
+
fs.writeFileSync(outputPath, svg);
|
|
143
|
+
console.log(` Rendered: ${skillName}_combined.svg`);
|
|
144
|
+
|
|
145
|
+
// Also write the dot source for debugging
|
|
146
|
+
const dotPath = path.join(outputDir, `${skillName}_combined.dot`);
|
|
147
|
+
fs.writeFileSync(dotPath, combined);
|
|
148
|
+
console.log(` Source: ${skillName}_combined.dot`);
|
|
149
|
+
} else {
|
|
150
|
+
console.error(' Failed to render combined diagram');
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
// Render each separately
|
|
154
|
+
for (const block of blocks) {
|
|
155
|
+
const svg = renderToSvg(block.content);
|
|
156
|
+
if (svg) {
|
|
157
|
+
const outputPath = path.join(outputDir, `${block.name}.svg`);
|
|
158
|
+
fs.writeFileSync(outputPath, svg);
|
|
159
|
+
console.log(` Rendered: ${block.name}.svg`);
|
|
160
|
+
} else {
|
|
161
|
+
console.error(` Failed: ${block.name}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log(`\nOutput: ${outputDir}/`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
main();
|