claude-code-workflow 6.3.2 → 6.3.5
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/.claude/CLAUDE.md +9 -1
- package/.claude/commands/{clean.md → workflow/clean.md} +5 -5
- package/.claude/commands/workflow/docs/analyze.md +1467 -0
- package/.claude/commands/workflow/docs/copyright.md +1265 -0
- package/.claude/commands/workflow/lite-plan.md +1 -1
- package/.claude/commands/workflow/tools/conflict-resolution.md +76 -240
- package/.claude/commands/workflow/tools/task-generate-agent.md +81 -8
- package/.claude/skills/_shared/mermaid-utils.md +584 -0
- package/.claude/skills/copyright-docs/SKILL.md +132 -0
- package/.claude/skills/copyright-docs/phases/01-metadata-collection.md +78 -0
- package/.claude/skills/copyright-docs/phases/02-deep-analysis.md +454 -0
- package/.claude/skills/copyright-docs/phases/02.5-consolidation.md +192 -0
- package/.claude/skills/copyright-docs/phases/04-document-assembly.md +261 -0
- package/.claude/skills/copyright-docs/phases/05-compliance-refinement.md +192 -0
- package/.claude/skills/copyright-docs/specs/cpcc-requirements.md +121 -0
- package/.claude/skills/copyright-docs/templates/agent-base.md +200 -0
- package/.claude/skills/project-analyze/SKILL.md +162 -0
- package/.claude/skills/project-analyze/phases/01-requirements-discovery.md +79 -0
- package/.claude/skills/project-analyze/phases/02-project-exploration.md +75 -0
- package/.claude/skills/project-analyze/phases/03-deep-analysis.md +640 -0
- package/.claude/skills/project-analyze/phases/03.5-consolidation.md +208 -0
- package/.claude/skills/project-analyze/phases/04-report-generation.md +217 -0
- package/.claude/skills/project-analyze/phases/05-iterative-refinement.md +124 -0
- package/.claude/skills/project-analyze/specs/quality-standards.md +115 -0
- package/.claude/skills/project-analyze/specs/writing-style.md +152 -0
- package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +79 -65
- package/.claude/workflows/cli-tools-usage.md +515 -516
- package/README.md +11 -1
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +7 -1
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts +1 -1
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +116 -14
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +2 -2
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +7 -3
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +31 -17
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor.js +19 -7
- package/ccw/dist/tools/cli-executor.js.map +1 -1
- package/ccw/dist/tools/cli-history-store.d.ts +33 -0
- package/ccw/dist/tools/cli-history-store.d.ts.map +1 -1
- package/ccw/dist/tools/cli-history-store.js +89 -5
- package/ccw/dist/tools/cli-history-store.js.map +1 -1
- package/ccw/dist/tools/smart-search.d.ts +25 -0
- package/ccw/dist/tools/smart-search.d.ts.map +1 -1
- package/ccw/dist/tools/smart-search.js +121 -17
- package/ccw/dist/tools/smart-search.js.map +1 -1
- package/ccw/src/cli.ts +264 -258
- package/ccw/src/commands/cli.ts +1009 -884
- package/ccw/src/core/routes/cli-routes.ts +3 -3
- package/ccw/src/templates/dashboard-js/components/cli-history.js +40 -13
- package/ccw/src/templates/dashboard-js/components/cli-status.js +26 -2
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +5 -0
- package/ccw/src/templates/dashboard-js/views/history.js +19 -4
- package/ccw/src/tools/claude-cli-tools.ts +37 -20
- package/ccw/src/tools/cli-executor.ts +20 -7
- package/ccw/src/tools/cli-history-store.ts +125 -5
- package/ccw/src/tools/smart-search.ts +157 -16
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/config.py +8 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +71 -1
- package/codex-lens/src/codexlens/search/hybrid_search.py +144 -11
- package/codex-lens/src/codexlens/search/ranking.py +540 -274
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +55 -10
- package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/global_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/dir_index.py +1888 -1850
- package/codex-lens/src/codexlens/storage/global_index.py +365 -0
- package/codex-lens/src/codexlens/storage/index_tree.py +83 -10
- package/package.json +2 -2
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
# Mermaid Utilities Library
|
|
2
|
+
|
|
3
|
+
Shared utilities for generating and validating Mermaid diagrams across all analysis skills.
|
|
4
|
+
|
|
5
|
+
## Sanitization Functions
|
|
6
|
+
|
|
7
|
+
### sanitizeId
|
|
8
|
+
|
|
9
|
+
Convert any text to a valid Mermaid node ID.
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
/**
|
|
13
|
+
* Sanitize text to valid Mermaid node ID
|
|
14
|
+
* - Only alphanumeric and underscore allowed
|
|
15
|
+
* - Cannot start with number
|
|
16
|
+
* - Truncates to 50 chars max
|
|
17
|
+
*
|
|
18
|
+
* @param {string} text - Input text
|
|
19
|
+
* @returns {string} - Valid Mermaid ID
|
|
20
|
+
*/
|
|
21
|
+
function sanitizeId(text) {
|
|
22
|
+
if (!text) return '_empty';
|
|
23
|
+
return text
|
|
24
|
+
.replace(/[^a-zA-Z0-9_\u4e00-\u9fa5]/g, '_') // Allow Chinese chars
|
|
25
|
+
.replace(/^[0-9]/, '_$&') // Prefix number with _
|
|
26
|
+
.replace(/_+/g, '_') // Collapse multiple _
|
|
27
|
+
.substring(0, 50); // Limit length
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Examples:
|
|
31
|
+
// sanitizeId("User-Service") → "User_Service"
|
|
32
|
+
// sanitizeId("3rdParty") → "_3rdParty"
|
|
33
|
+
// sanitizeId("用户服务") → "用户服务"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### escapeLabel
|
|
37
|
+
|
|
38
|
+
Escape special characters for Mermaid labels.
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
/**
|
|
42
|
+
* Escape special characters in Mermaid labels
|
|
43
|
+
* Uses HTML entity encoding for problematic chars
|
|
44
|
+
*
|
|
45
|
+
* @param {string} text - Label text
|
|
46
|
+
* @returns {string} - Escaped label
|
|
47
|
+
*/
|
|
48
|
+
function escapeLabel(text) {
|
|
49
|
+
if (!text) return '';
|
|
50
|
+
return text
|
|
51
|
+
.replace(/"/g, "'") // Avoid quote issues
|
|
52
|
+
.replace(/\(/g, '#40;') // (
|
|
53
|
+
.replace(/\)/g, '#41;') // )
|
|
54
|
+
.replace(/\{/g, '#123;') // {
|
|
55
|
+
.replace(/\}/g, '#125;') // }
|
|
56
|
+
.replace(/\[/g, '#91;') // [
|
|
57
|
+
.replace(/\]/g, '#93;') // ]
|
|
58
|
+
.replace(/</g, '#60;') // <
|
|
59
|
+
.replace(/>/g, '#62;') // >
|
|
60
|
+
.replace(/\|/g, '#124;') // |
|
|
61
|
+
.substring(0, 80); // Limit length
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Examples:
|
|
65
|
+
// escapeLabel("Process(data)") → "Process#40;data#41;"
|
|
66
|
+
// escapeLabel("Check {valid?}") → "Check #123;valid?#125;"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### sanitizeType
|
|
70
|
+
|
|
71
|
+
Sanitize type names for class diagrams.
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
/**
|
|
75
|
+
* Sanitize type names for Mermaid classDiagram
|
|
76
|
+
* Removes generics syntax that causes issues
|
|
77
|
+
*
|
|
78
|
+
* @param {string} type - Type name
|
|
79
|
+
* @returns {string} - Sanitized type
|
|
80
|
+
*/
|
|
81
|
+
function sanitizeType(type) {
|
|
82
|
+
if (!type) return 'any';
|
|
83
|
+
return type
|
|
84
|
+
.replace(/<[^>]*>/g, '') // Remove generics <T>
|
|
85
|
+
.replace(/\|/g, ' or ') // Union types
|
|
86
|
+
.replace(/&/g, ' and ') // Intersection types
|
|
87
|
+
.replace(/\[\]/g, 'Array') // Array notation
|
|
88
|
+
.substring(0, 30);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Examples:
|
|
92
|
+
// sanitizeType("Array<string>") → "Array"
|
|
93
|
+
// sanitizeType("string | number") → "string or number"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Diagram Generation Functions
|
|
97
|
+
|
|
98
|
+
### generateFlowchartNode
|
|
99
|
+
|
|
100
|
+
Generate a flowchart node with proper shape.
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
/**
|
|
104
|
+
* Generate flowchart node with shape
|
|
105
|
+
*
|
|
106
|
+
* @param {string} id - Node ID
|
|
107
|
+
* @param {string} label - Display label
|
|
108
|
+
* @param {string} type - Node type: start|end|process|decision|io|subroutine
|
|
109
|
+
* @returns {string} - Mermaid node definition
|
|
110
|
+
*/
|
|
111
|
+
function generateFlowchartNode(id, label, type = 'process') {
|
|
112
|
+
const safeId = sanitizeId(id);
|
|
113
|
+
const safeLabel = escapeLabel(label);
|
|
114
|
+
|
|
115
|
+
const shapes = {
|
|
116
|
+
start: `${safeId}(["${safeLabel}"])`, // Stadium shape
|
|
117
|
+
end: `${safeId}(["${safeLabel}"])`, // Stadium shape
|
|
118
|
+
process: `${safeId}["${safeLabel}"]`, // Rectangle
|
|
119
|
+
decision: `${safeId}{"${safeLabel}"}`, // Diamond
|
|
120
|
+
io: `${safeId}[/"${safeLabel}"/]`, // Parallelogram
|
|
121
|
+
subroutine: `${safeId}[["${safeLabel}"]]`, // Subroutine
|
|
122
|
+
database: `${safeId}[("${safeLabel}")]`, // Cylinder
|
|
123
|
+
manual: `${safeId}[/"${safeLabel}"\\]` // Trapezoid
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return shapes[type] || shapes.process;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### generateFlowchartEdge
|
|
131
|
+
|
|
132
|
+
Generate a flowchart edge with optional label.
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
/**
|
|
136
|
+
* Generate flowchart edge
|
|
137
|
+
*
|
|
138
|
+
* @param {string} from - Source node ID
|
|
139
|
+
* @param {string} to - Target node ID
|
|
140
|
+
* @param {string} label - Edge label (optional)
|
|
141
|
+
* @param {string} style - Edge style: solid|dashed|thick
|
|
142
|
+
* @returns {string} - Mermaid edge definition
|
|
143
|
+
*/
|
|
144
|
+
function generateFlowchartEdge(from, to, label = '', style = 'solid') {
|
|
145
|
+
const safeFrom = sanitizeId(from);
|
|
146
|
+
const safeTo = sanitizeId(to);
|
|
147
|
+
const safeLabel = label ? `|"${escapeLabel(label)}"|` : '';
|
|
148
|
+
|
|
149
|
+
const arrows = {
|
|
150
|
+
solid: '-->',
|
|
151
|
+
dashed: '-.->',
|
|
152
|
+
thick: '==>'
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const arrow = arrows[style] || arrows.solid;
|
|
156
|
+
return ` ${safeFrom} ${arrow}${safeLabel} ${safeTo}`;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### generateAlgorithmFlowchart (Enhanced)
|
|
161
|
+
|
|
162
|
+
Generate algorithm flowchart with branch/loop support.
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
/**
|
|
166
|
+
* Generate algorithm flowchart with decision support
|
|
167
|
+
*
|
|
168
|
+
* @param {Object} algorithm - Algorithm definition
|
|
169
|
+
* - name: Algorithm name
|
|
170
|
+
* - inputs: [{name, type}]
|
|
171
|
+
* - outputs: [{name, type}]
|
|
172
|
+
* - steps: [{id, description, type, next: [id], conditions: [text]}]
|
|
173
|
+
* @returns {string} - Complete Mermaid flowchart
|
|
174
|
+
*/
|
|
175
|
+
function generateAlgorithmFlowchart(algorithm) {
|
|
176
|
+
let mermaid = 'flowchart TD\n';
|
|
177
|
+
|
|
178
|
+
// Start node
|
|
179
|
+
mermaid += ` START(["开始: ${escapeLabel(algorithm.name)}"])\n`;
|
|
180
|
+
|
|
181
|
+
// Input node (if has inputs)
|
|
182
|
+
if (algorithm.inputs?.length > 0) {
|
|
183
|
+
const inputList = algorithm.inputs.map(i => `${i.name}: ${i.type}`).join(', ');
|
|
184
|
+
mermaid += ` INPUT[/"输入: ${escapeLabel(inputList)}"/]\n`;
|
|
185
|
+
mermaid += ` START --> INPUT\n`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Process nodes
|
|
189
|
+
const steps = algorithm.steps || [];
|
|
190
|
+
for (const step of steps) {
|
|
191
|
+
const nodeId = sanitizeId(step.id || `STEP_${step.step_num}`);
|
|
192
|
+
|
|
193
|
+
if (step.type === 'decision') {
|
|
194
|
+
mermaid += ` ${nodeId}{"${escapeLabel(step.description)}"}\n`;
|
|
195
|
+
} else if (step.type === 'io') {
|
|
196
|
+
mermaid += ` ${nodeId}[/"${escapeLabel(step.description)}"/]\n`;
|
|
197
|
+
} else if (step.type === 'loop_start') {
|
|
198
|
+
mermaid += ` ${nodeId}[["循环: ${escapeLabel(step.description)}"]]\n`;
|
|
199
|
+
} else {
|
|
200
|
+
mermaid += ` ${nodeId}["${escapeLabel(step.description)}"]\n`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Output node
|
|
205
|
+
const outputDesc = algorithm.outputs?.map(o => o.name).join(', ') || '结果';
|
|
206
|
+
mermaid += ` OUTPUT[/"输出: ${escapeLabel(outputDesc)}"/]\n`;
|
|
207
|
+
mermaid += ` END_(["结束"])\n`;
|
|
208
|
+
|
|
209
|
+
// Connect first step to input/start
|
|
210
|
+
if (steps.length > 0) {
|
|
211
|
+
const firstStep = sanitizeId(steps[0].id || 'STEP_1');
|
|
212
|
+
if (algorithm.inputs?.length > 0) {
|
|
213
|
+
mermaid += ` INPUT --> ${firstStep}\n`;
|
|
214
|
+
} else {
|
|
215
|
+
mermaid += ` START --> ${firstStep}\n`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Connect steps based on next array
|
|
220
|
+
for (const step of steps) {
|
|
221
|
+
const nodeId = sanitizeId(step.id || `STEP_${step.step_num}`);
|
|
222
|
+
|
|
223
|
+
if (step.next && step.next.length > 0) {
|
|
224
|
+
step.next.forEach((nextId, index) => {
|
|
225
|
+
const safeNextId = sanitizeId(nextId);
|
|
226
|
+
const condition = step.conditions?.[index];
|
|
227
|
+
|
|
228
|
+
if (condition) {
|
|
229
|
+
mermaid += ` ${nodeId} -->|"${escapeLabel(condition)}"| ${safeNextId}\n`;
|
|
230
|
+
} else {
|
|
231
|
+
mermaid += ` ${nodeId} --> ${safeNextId}\n`;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
} else if (!step.type?.includes('end')) {
|
|
235
|
+
// Default: connect to next step or output
|
|
236
|
+
const stepIndex = steps.indexOf(step);
|
|
237
|
+
if (stepIndex < steps.length - 1) {
|
|
238
|
+
const nextStep = sanitizeId(steps[stepIndex + 1].id || `STEP_${stepIndex + 2}`);
|
|
239
|
+
mermaid += ` ${nodeId} --> ${nextStep}\n`;
|
|
240
|
+
} else {
|
|
241
|
+
mermaid += ` ${nodeId} --> OUTPUT\n`;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Connect output to end
|
|
247
|
+
mermaid += ` OUTPUT --> END_\n`;
|
|
248
|
+
|
|
249
|
+
return mermaid;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Diagram Validation
|
|
254
|
+
|
|
255
|
+
### validateMermaidSyntax
|
|
256
|
+
|
|
257
|
+
Comprehensive Mermaid syntax validation.
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
/**
|
|
261
|
+
* Validate Mermaid diagram syntax
|
|
262
|
+
*
|
|
263
|
+
* @param {string} content - Mermaid diagram content
|
|
264
|
+
* @returns {Object} - {valid: boolean, issues: string[]}
|
|
265
|
+
*/
|
|
266
|
+
function validateMermaidSyntax(content) {
|
|
267
|
+
const issues = [];
|
|
268
|
+
|
|
269
|
+
// Check 1: Diagram type declaration
|
|
270
|
+
if (!content.match(/^(graph|flowchart|classDiagram|sequenceDiagram|stateDiagram|erDiagram|gantt|pie|mindmap)/m)) {
|
|
271
|
+
issues.push('Missing diagram type declaration');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check 2: Undefined values
|
|
275
|
+
if (content.includes('undefined') || content.includes('null')) {
|
|
276
|
+
issues.push('Contains undefined/null values');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Check 3: Invalid arrow syntax
|
|
280
|
+
if (content.match(/-->\s*-->/)) {
|
|
281
|
+
issues.push('Double arrow syntax error');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Check 4: Unescaped special characters in labels
|
|
285
|
+
const labelMatches = content.match(/\["[^"]*[(){}[\]<>][^"]*"\]/g);
|
|
286
|
+
if (labelMatches?.some(m => !m.includes('#'))) {
|
|
287
|
+
issues.push('Unescaped special characters in labels');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Check 5: Node ID starts with number
|
|
291
|
+
if (content.match(/\n\s*[0-9][a-zA-Z0-9_]*[\[\({]/)) {
|
|
292
|
+
issues.push('Node ID cannot start with number');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Check 6: Nested subgraph syntax error
|
|
296
|
+
if (content.match(/subgraph\s+\S+\s*\n[^e]*subgraph/)) {
|
|
297
|
+
// This is actually valid, only flag if brackets don't match
|
|
298
|
+
const subgraphCount = (content.match(/subgraph/g) || []).length;
|
|
299
|
+
const endCount = (content.match(/\bend\b/g) || []).length;
|
|
300
|
+
if (subgraphCount > endCount) {
|
|
301
|
+
issues.push('Unbalanced subgraph/end blocks');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Check 7: Invalid arrow type for diagram type
|
|
306
|
+
const diagramType = content.match(/^(graph|flowchart|classDiagram|sequenceDiagram)/m)?.[1];
|
|
307
|
+
if (diagramType === 'classDiagram' && content.includes('-->|')) {
|
|
308
|
+
issues.push('Invalid edge label syntax for classDiagram');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Check 8: Empty node labels
|
|
312
|
+
if (content.match(/\[""\]|\{\}|\(\)/)) {
|
|
313
|
+
issues.push('Empty node labels detected');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Check 9: Reserved keywords as IDs
|
|
317
|
+
const reserved = ['end', 'graph', 'subgraph', 'direction', 'class', 'click'];
|
|
318
|
+
for (const keyword of reserved) {
|
|
319
|
+
const pattern = new RegExp(`\\n\\s*${keyword}\\s*[\\[\\(\\{]`, 'i');
|
|
320
|
+
if (content.match(pattern)) {
|
|
321
|
+
issues.push(`Reserved keyword "${keyword}" used as node ID`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Check 10: Line length (Mermaid has issues with very long lines)
|
|
326
|
+
const lines = content.split('\n');
|
|
327
|
+
for (let i = 0; i < lines.length; i++) {
|
|
328
|
+
if (lines[i].length > 500) {
|
|
329
|
+
issues.push(`Line ${i + 1} exceeds 500 characters`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
valid: issues.length === 0,
|
|
335
|
+
issues
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### validateDiagramDirectory
|
|
341
|
+
|
|
342
|
+
Validate all diagrams in a directory.
|
|
343
|
+
|
|
344
|
+
```javascript
|
|
345
|
+
/**
|
|
346
|
+
* Validate all Mermaid diagrams in directory
|
|
347
|
+
*
|
|
348
|
+
* @param {string} diagramDir - Path to diagrams directory
|
|
349
|
+
* @returns {Object[]} - Array of {file, valid, issues}
|
|
350
|
+
*/
|
|
351
|
+
function validateDiagramDirectory(diagramDir) {
|
|
352
|
+
const files = Glob(`${diagramDir}/*.mmd`);
|
|
353
|
+
const results = [];
|
|
354
|
+
|
|
355
|
+
for (const file of files) {
|
|
356
|
+
const content = Read(file);
|
|
357
|
+
const validation = validateMermaidSyntax(content);
|
|
358
|
+
|
|
359
|
+
results.push({
|
|
360
|
+
file: file.split('/').pop(),
|
|
361
|
+
path: file,
|
|
362
|
+
valid: validation.valid,
|
|
363
|
+
issues: validation.issues,
|
|
364
|
+
lines: content.split('\n').length
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return results;
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Class Diagram Utilities
|
|
373
|
+
|
|
374
|
+
### generateClassDiagram
|
|
375
|
+
|
|
376
|
+
Generate class diagram with relationships.
|
|
377
|
+
|
|
378
|
+
```javascript
|
|
379
|
+
/**
|
|
380
|
+
* Generate class diagram from analysis data
|
|
381
|
+
*
|
|
382
|
+
* @param {Object} analysis - Data structure analysis
|
|
383
|
+
* - entities: [{name, type, properties, methods}]
|
|
384
|
+
* - relationships: [{from, to, type, label}]
|
|
385
|
+
* @param {Object} options - Generation options
|
|
386
|
+
* - maxClasses: Max classes to include (default: 15)
|
|
387
|
+
* - maxProperties: Max properties per class (default: 8)
|
|
388
|
+
* - maxMethods: Max methods per class (default: 6)
|
|
389
|
+
* @returns {string} - Mermaid classDiagram
|
|
390
|
+
*/
|
|
391
|
+
function generateClassDiagram(analysis, options = {}) {
|
|
392
|
+
const maxClasses = options.maxClasses || 15;
|
|
393
|
+
const maxProperties = options.maxProperties || 8;
|
|
394
|
+
const maxMethods = options.maxMethods || 6;
|
|
395
|
+
|
|
396
|
+
let mermaid = 'classDiagram\n';
|
|
397
|
+
|
|
398
|
+
const entities = (analysis.entities || []).slice(0, maxClasses);
|
|
399
|
+
|
|
400
|
+
// Generate classes
|
|
401
|
+
for (const entity of entities) {
|
|
402
|
+
const className = sanitizeId(entity.name);
|
|
403
|
+
mermaid += ` class ${className} {\n`;
|
|
404
|
+
|
|
405
|
+
// Properties
|
|
406
|
+
for (const prop of (entity.properties || []).slice(0, maxProperties)) {
|
|
407
|
+
const vis = {public: '+', private: '-', protected: '#'}[prop.visibility] || '+';
|
|
408
|
+
const type = sanitizeType(prop.type);
|
|
409
|
+
mermaid += ` ${vis}${type} ${prop.name}\n`;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Methods
|
|
413
|
+
for (const method of (entity.methods || []).slice(0, maxMethods)) {
|
|
414
|
+
const vis = {public: '+', private: '-', protected: '#'}[method.visibility] || '+';
|
|
415
|
+
const params = (method.params || []).map(p => p.name).join(', ');
|
|
416
|
+
const returnType = sanitizeType(method.returnType || 'void');
|
|
417
|
+
mermaid += ` ${vis}${method.name}(${params}) ${returnType}\n`;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
mermaid += ' }\n';
|
|
421
|
+
|
|
422
|
+
// Add stereotype if applicable
|
|
423
|
+
if (entity.type === 'interface') {
|
|
424
|
+
mermaid += ` <<interface>> ${className}\n`;
|
|
425
|
+
} else if (entity.type === 'abstract') {
|
|
426
|
+
mermaid += ` <<abstract>> ${className}\n`;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Generate relationships
|
|
431
|
+
const arrows = {
|
|
432
|
+
inheritance: '--|>',
|
|
433
|
+
implementation: '..|>',
|
|
434
|
+
composition: '*--',
|
|
435
|
+
aggregation: 'o--',
|
|
436
|
+
association: '-->',
|
|
437
|
+
dependency: '..>'
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
for (const rel of (analysis.relationships || [])) {
|
|
441
|
+
const from = sanitizeId(rel.from);
|
|
442
|
+
const to = sanitizeId(rel.to);
|
|
443
|
+
const arrow = arrows[rel.type] || '-->';
|
|
444
|
+
const label = rel.label ? ` : ${escapeLabel(rel.label)}` : '';
|
|
445
|
+
|
|
446
|
+
// Only include if both entities exist
|
|
447
|
+
if (entities.some(e => sanitizeId(e.name) === from) &&
|
|
448
|
+
entities.some(e => sanitizeId(e.name) === to)) {
|
|
449
|
+
mermaid += ` ${from} ${arrow} ${to}${label}\n`;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return mermaid;
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Sequence Diagram Utilities
|
|
458
|
+
|
|
459
|
+
### generateSequenceDiagram
|
|
460
|
+
|
|
461
|
+
Generate sequence diagram from scenario.
|
|
462
|
+
|
|
463
|
+
```javascript
|
|
464
|
+
/**
|
|
465
|
+
* Generate sequence diagram from scenario
|
|
466
|
+
*
|
|
467
|
+
* @param {Object} scenario - Sequence scenario
|
|
468
|
+
* - name: Scenario name
|
|
469
|
+
* - actors: [{id, name, type}]
|
|
470
|
+
* - messages: [{from, to, description, type}]
|
|
471
|
+
* - blocks: [{type, condition, messages}]
|
|
472
|
+
* @returns {string} - Mermaid sequenceDiagram
|
|
473
|
+
*/
|
|
474
|
+
function generateSequenceDiagram(scenario) {
|
|
475
|
+
let mermaid = 'sequenceDiagram\n';
|
|
476
|
+
|
|
477
|
+
// Title
|
|
478
|
+
if (scenario.name) {
|
|
479
|
+
mermaid += ` title ${escapeLabel(scenario.name)}\n`;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Participants
|
|
483
|
+
for (const actor of scenario.actors || []) {
|
|
484
|
+
const actorType = actor.type === 'external' ? 'actor' : 'participant';
|
|
485
|
+
mermaid += ` ${actorType} ${sanitizeId(actor.id)} as ${escapeLabel(actor.name)}\n`;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
mermaid += '\n';
|
|
489
|
+
|
|
490
|
+
// Messages
|
|
491
|
+
for (const msg of scenario.messages || []) {
|
|
492
|
+
const from = sanitizeId(msg.from);
|
|
493
|
+
const to = sanitizeId(msg.to);
|
|
494
|
+
const desc = escapeLabel(msg.description);
|
|
495
|
+
|
|
496
|
+
let arrow;
|
|
497
|
+
switch (msg.type) {
|
|
498
|
+
case 'async': arrow = '->>'; break;
|
|
499
|
+
case 'response': arrow = '-->>'; break;
|
|
500
|
+
case 'create': arrow = '->>+'; break;
|
|
501
|
+
case 'destroy': arrow = '->>-'; break;
|
|
502
|
+
case 'self': arrow = '->>'; break;
|
|
503
|
+
default: arrow = '->>';
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
mermaid += ` ${from}${arrow}${to}: ${desc}\n`;
|
|
507
|
+
|
|
508
|
+
// Activation
|
|
509
|
+
if (msg.activate) {
|
|
510
|
+
mermaid += ` activate ${to}\n`;
|
|
511
|
+
}
|
|
512
|
+
if (msg.deactivate) {
|
|
513
|
+
mermaid += ` deactivate ${from}\n`;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Notes
|
|
517
|
+
if (msg.note) {
|
|
518
|
+
mermaid += ` Note over ${to}: ${escapeLabel(msg.note)}\n`;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Blocks (loops, alt, opt)
|
|
523
|
+
for (const block of scenario.blocks || []) {
|
|
524
|
+
switch (block.type) {
|
|
525
|
+
case 'loop':
|
|
526
|
+
mermaid += ` loop ${escapeLabel(block.condition)}\n`;
|
|
527
|
+
break;
|
|
528
|
+
case 'alt':
|
|
529
|
+
mermaid += ` alt ${escapeLabel(block.condition)}\n`;
|
|
530
|
+
break;
|
|
531
|
+
case 'opt':
|
|
532
|
+
mermaid += ` opt ${escapeLabel(block.condition)}\n`;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
for (const m of block.messages || []) {
|
|
537
|
+
mermaid += ` ${sanitizeId(m.from)}->>${sanitizeId(m.to)}: ${escapeLabel(m.description)}\n`;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
mermaid += ' end\n';
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return mermaid;
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## Usage Examples
|
|
548
|
+
|
|
549
|
+
### Example 1: Algorithm with Branches
|
|
550
|
+
|
|
551
|
+
```javascript
|
|
552
|
+
const algorithm = {
|
|
553
|
+
name: "用户认证流程",
|
|
554
|
+
inputs: [{name: "credentials", type: "Object"}],
|
|
555
|
+
outputs: [{name: "token", type: "JWT"}],
|
|
556
|
+
steps: [
|
|
557
|
+
{id: "validate", description: "验证输入格式", type: "process"},
|
|
558
|
+
{id: "check_user", description: "用户是否存在?", type: "decision",
|
|
559
|
+
next: ["verify_pwd", "error_user"], conditions: ["是", "否"]},
|
|
560
|
+
{id: "verify_pwd", description: "验证密码", type: "process"},
|
|
561
|
+
{id: "pwd_ok", description: "密码正确?", type: "decision",
|
|
562
|
+
next: ["gen_token", "error_pwd"], conditions: ["是", "否"]},
|
|
563
|
+
{id: "gen_token", description: "生成 JWT Token", type: "process"},
|
|
564
|
+
{id: "error_user", description: "返回用户不存在", type: "io"},
|
|
565
|
+
{id: "error_pwd", description: "返回密码错误", type: "io"}
|
|
566
|
+
]
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const flowchart = generateAlgorithmFlowchart(algorithm);
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### Example 2: Validate Before Output
|
|
573
|
+
|
|
574
|
+
```javascript
|
|
575
|
+
const diagram = generateClassDiagram(analysis);
|
|
576
|
+
const validation = validateMermaidSyntax(diagram);
|
|
577
|
+
|
|
578
|
+
if (!validation.valid) {
|
|
579
|
+
console.log("Diagram has issues:", validation.issues);
|
|
580
|
+
// Fix issues or regenerate
|
|
581
|
+
} else {
|
|
582
|
+
Write(`${outputDir}/class-diagram.mmd`, diagram);
|
|
583
|
+
}
|
|
584
|
+
```
|