- 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>
78 lines
2.1 KiB
TypeScript
78 lines
2.1 KiB
TypeScript
/**
|
|
* 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,
|
|
};
|
|
}
|