Restructure to skill-manager conventions
- Move the TypeScript project under scripts/ (src, tsconfig, package.json, pnpm lockfile/workspace, canonical .gitignore); drop the npm package-lock - Add scripts/Taskfile.yml aggregator plus .scripts modules (loggers, base, cli) with build, run, and validate tasks - Move the six SKILL-*.md docs into references/ with kebab names and extract the connector-routing sections from SKILL.md into references/routing-best-practices.md (SKILL.md 666 -> ~310 lines) - Add license/metadata/compatibility frontmatter, an Available scripts section, and update all CLI paths in README and references skill-manager validate: 13/13 passed, 0 warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Action: page-labels-validation
|
||||
*
|
||||
* Validates shape labels on a single page and reports three categories of issues:
|
||||
* 1. empty_label — shape has no label (or whitespace only)
|
||||
* 2. duplicate_label — two or more shapes share the same label
|
||||
* 3. long_label — label exceeds MAX_LABEL_LENGTH characters
|
||||
*
|
||||
* Edges are excluded (connectors often have no label by design).
|
||||
*/
|
||||
|
||||
import { parseDiagram } from "../../services/drawio-parser/parser.js";
|
||||
|
||||
const MAX_LABEL_LENGTH = 80;
|
||||
|
||||
export function run(filePath: string): Record<string, unknown> {
|
||||
const { shapes } = parseDiagram(filePath);
|
||||
|
||||
const issues: Record<string, unknown>[] = [];
|
||||
|
||||
// Track label → [ids] for duplicate detection
|
||||
const labelIndex = new Map<string, string[]>();
|
||||
|
||||
for (const [id, shape] of shapes) {
|
||||
const label = shape.label?.trim() ?? "";
|
||||
|
||||
// 1. Empty label
|
||||
if (label === "") {
|
||||
issues.push({ type: "empty_label", shapeId: id });
|
||||
continue; // skip further checks for unlabelled shapes
|
||||
}
|
||||
|
||||
// 2. Long label
|
||||
if (label.length > MAX_LABEL_LENGTH) {
|
||||
issues.push({
|
||||
type: "long_label",
|
||||
shapeId: id,
|
||||
label,
|
||||
length: label.length,
|
||||
maxAllowed: MAX_LABEL_LENGTH,
|
||||
});
|
||||
}
|
||||
|
||||
// Accumulate for duplicate check
|
||||
if (!labelIndex.has(label)) labelIndex.set(label, []);
|
||||
labelIndex.get(label)!.push(id);
|
||||
}
|
||||
|
||||
// 3. Duplicate labels
|
||||
for (const [label, ids] of labelIndex) {
|
||||
if (ids.length > 1) {
|
||||
issues.push({
|
||||
type: "duplicate_label",
|
||||
label,
|
||||
shapeIds: ids,
|
||||
count: ids.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const empty = issues.filter((i) => i["type"] === "empty_label").length;
|
||||
const duplicate = issues.filter((i) => i["type"] === "duplicate_label").length;
|
||||
const long = issues.filter((i) => i["type"] === "long_label").length;
|
||||
|
||||
return {
|
||||
action: "page-labels-validation",
|
||||
file: filePath,
|
||||
config: { maxLabelLength: MAX_LABEL_LENGTH },
|
||||
summary: {
|
||||
emptyLabels: empty,
|
||||
duplicateLabels: duplicate,
|
||||
longLabels: long,
|
||||
totalIssues: issues.length,
|
||||
},
|
||||
issues,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user