feat: introduce archify skill for generating architecture diagrams

- Added a new Archify skill, enabling users to create polished architecture, workflow, sequence, data-flow, and lifecycle diagrams.
- Implemented comprehensive functionality including rendering, validation, and delivery of diagrams in various formats.
- Integrated a user-friendly command-line interface for generating and previewing diagrams.
- Developed supporting files including package.json, LICENSE, and SKILL.md for documentation and licensing.
- Added unit tests to ensure reliability and functionality of the new skill.

These changes enhance the application by providing a structured approach to visualizing system architecture and workflows, improving user experience and data representation.
This commit is contained in:
shancheas
2026-08-31 11:31:29 +07:00
parent 050fafd731
commit 166e0d40ac
221 changed files with 191228 additions and 1 deletions
@@ -0,0 +1,84 @@
import { existsSync, readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import assert from 'node:assert/strict';
const here = path.dirname(fileURLToPath(import.meta.url));
const skillRoot = path.join(here, '..');
const skill = readFileSync(path.join(skillRoot, 'SKILL.md'), 'utf8');
const authoringContract = readFileSync(path.join(skillRoot, 'references', 'authoring-contract.md'), 'utf8');
const frontmatter = skill.match(/^---\n([\s\S]*?)\n---/);
test('skill description is portable across 1024-character runtimes and remains searchable', () => {
assert.ok(frontmatter, 'SKILL.md must start with YAML frontmatter');
const description = frontmatter[1].match(/^description:\s*(.+)$/m)?.[1]?.trim();
assert.ok(description, 'frontmatter must include a one-line description');
assert.ok(description.length <= 1024, `description is ${description.length} characters; maximum is 1024`);
assert.ok(Buffer.byteLength(description, 'utf8') <= 1024, 'description must also fit a 1024-byte runtime limit');
for (const trigger of ['architecture', 'workflow', 'sequence', 'data-flow', 'lifecycle', 'Mermaid']) {
assert.match(description, new RegExp(`\\b${trigger}\\b`, 'i'), `description must retain the ${trigger} trigger`);
}
assert.match(description, /standalone HTML/i);
assert.match(description, /Use when/i);
});
test('literal packaged-skill path references resolve inside the installed skill root', () => {
const references = [...skill.matchAll(/`((?:assets|bin|examples|recipes|references|renderers|schemas|scripts)\/[^`\s]+)`/g)]
.map((match) => match[1])
.filter((reference) => !/[<>{}*\[\]]/.test(reference));
assert.ok(references.length > 0, 'expected literal packaged-skill references');
for (const reference of new Set(references)) {
assert.equal(existsSync(path.join(skillRoot, reference)), true, `SKILL.md references missing packaged path ${reference}`);
}
});
test('main skill stays a bounded authoring router with progressive references', () => {
const lines = skill.trimEnd().split('\n');
assert.ok(lines.length <= 160, `SKILL.md is ${lines.length} lines; keep the entrypoint at 160 or fewer`);
for (const reference of [
'references/authoring-contract.md',
'references/viewer-runtime.md',
'references/delivery-contract.md',
]) {
assert.match(skill, new RegExp(reference.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
assert.equal(existsSync(path.join(skillRoot, reference)), true, `${reference} must ship with the skill`);
}
});
test('update awareness is notification-only and never replaces the requested workflow', () => {
assert.match(skill, /`scripts\/check-update\.mjs`/);
assert.match(skill, /`silent`[\s\S]*without mentioning/i);
assert.match(skill, /`update_available`[\s\S]*compact notice/i);
assert.match(skill, /information, not permission/i);
assert.match(skill, /`severity` is `security`[\s\S]*security update[\s\S]*emphasis only, never user autonomy/i);
assert.match(skill, /continue the user's original task/i);
assert.match(skill, /installed version unchanged/i);
assert.doesNotMatch(skill, /npx skills update|gh skill update/i);
});
test('language behavior stays within the bounded locale contract', () => {
assert.match(skill, /one primary authored language/);
assert.match(skill, /explicit user choice; otherwise follow the request or conversation's dominant language/);
assert.match(skill, /`meta\.locale` controls only renderer-owned Viewer UI/);
assert.match(skill, /use `"en"` or `"zh-CN"`/);
assert.match(skill, /For every other language, omit `meta\.locale`/);
assert.match(skill, /fixed Viewer UI and `<html lang>` fall back to English/);
assert.match(skill, /renderer never translates authored content/i);
assert.match(skill, /product names.*code identifiers.*protocols.*API paths.*environment names/);
assert.match(authoringContract, /`meta\.locale` controls only renderer-owned reader surfaces/);
assert.match(authoringContract, /outside `en` and `zh-CN`/);
assert.match(authoringContract, /artifact is\s+not fully localized/);
assert.match(authoringContract, /Do not silently substitute\s+`zh-CN` for another language or Chinese locale/);
assert.match(authoringContract, /It never translates authored content/);
assert.match(authoringContract, /Renderer-owned default legend labels follow `meta\.locale`/);
assert.match(authoringContract, /The fallback\s+applies only to renderer-owned surfaces/);
});
test('skill keeps the title hierarchy compact by default', () => {
assert.match(skill, /Omit `meta\.subtitle` by default/);
assert.match(skill, /Never invent a subtitle that restates the title, nodes, or cards/);
assert.match(authoringContract, /omitted or blank subtitle must not leave an empty visual row/);
});