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:
@@ -0,0 +1,74 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const skillRoot = path.resolve(__dirname, '..');
|
||||
const repoRoot = path.resolve(skillRoot, '..');
|
||||
const cursorCommand = 'npx -y skills add tt-a1i/archify --skill archify --agent cursor --global --copy --yes';
|
||||
|
||||
test('Cursor onboarding stays explicit, bilingual, and backed by the same Skill', () => {
|
||||
const english = fs.readFileSync(path.join(repoRoot, 'README.md'), 'utf8');
|
||||
const englishMirror = fs.readFileSync(path.join(repoRoot, 'README_EN.md'), 'utf8');
|
||||
const chinese = fs.readFileSync(path.join(repoRoot, 'README_ZH.md'), 'utf8');
|
||||
const start = fs.readFileSync(path.join(repoRoot, 'docs', 'start.html'), 'utf8');
|
||||
const landing = fs.readFileSync(path.join(repoRoot, 'docs', 'index.html'), 'utf8');
|
||||
|
||||
assert.equal(english, englishMirror, 'English README mirrors must stay synchronized');
|
||||
assert.match(english, /Cursor, Claude Code, Codex CLI, and OpenCode/);
|
||||
assert.match(chinese, /Cursor、Claude Code、Codex CLI 和 OpenCode/);
|
||||
for (const surface of [english, chinese, landing]) assert.ok(surface.includes(cursorCommand));
|
||||
for (const surface of [english, chinese, start, landing]) {
|
||||
assert.doesNotMatch(surface, /skills use[^\n<]*--agent cursor/);
|
||||
assert.doesNotMatch(surface, /~\/\.cursor\/skills\/archify/);
|
||||
assert.doesNotMatch(surface, /all Cursor models|every Cursor model/i);
|
||||
}
|
||||
|
||||
assert.match(start, /data-agent="cursor">Cursor<\/button>/);
|
||||
assert.match(start, /data-agent="codex">Codex<\/button>/);
|
||||
assert.match(start, /data-agent="claude-code">Claude Code<\/button>/);
|
||||
assert.match(start, /data-agent="opencode">OpenCode<\/button>/);
|
||||
assert.match(start, /KNOWN_AGENTS\.has\(requestedAgent\)/);
|
||||
assert.match(start, /same Skill/);
|
||||
assert.match(start, /同一份 Skill/);
|
||||
assert.doesNotMatch(start, /vendor-specific (?:renderer|schema|skill)/i);
|
||||
});
|
||||
|
||||
test('the zero-dependency archive works from the canonical Cursor-visible agent path', () => {
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'archify-cursor-package-'));
|
||||
const agentSkills = path.join(tmp, '.agents', 'skills');
|
||||
try {
|
||||
fs.mkdirSync(agentSkills, { recursive: true });
|
||||
execFileSync('unzip', ['-q', path.join(repoRoot, 'archify.zip'), '-d', agentSkills]);
|
||||
const installed = path.join(agentSkills, 'archify');
|
||||
const cli = path.join(installed, 'bin', 'archify.mjs');
|
||||
const doctor = execFileSync(process.execPath, [cli, 'doctor'], { encoding: 'utf8' });
|
||||
assert.match(doctor, /Archify is ready\./);
|
||||
|
||||
const fixtures = {
|
||||
architecture: 'web-app.architecture.json',
|
||||
workflow: 'agent-tool-call.workflow.json',
|
||||
sequence: 'cache-miss-request.sequence.json',
|
||||
dataflow: 'product-analytics.dataflow.json',
|
||||
lifecycle: 'agent-run.lifecycle.json',
|
||||
};
|
||||
for (const [type, fixture] of Object.entries(fixtures)) {
|
||||
const output = execFileSync(process.execPath, [
|
||||
cli,
|
||||
'validate',
|
||||
type,
|
||||
path.join(installed, 'examples', fixture),
|
||||
'--json',
|
||||
], { encoding: 'utf8' });
|
||||
const receipt = JSON.parse(output);
|
||||
assert.equal(receipt.ok, true, `${type}: installed package validation failed`);
|
||||
assert.equal(receipt.type, type);
|
||||
}
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user