Skip to content

Commit fedb28e

Browse files
committed
feat(import): add Mermaid flowchart importer for typed architecture IR
- Problem: Archify could not import existing Mermaid flowchart/graph diagrams; users had to re-author topology by hand. - Fix: Add a focused Mermaid flowchart parser (archify/importers/flowchart.mjs) that maps a documented subset of flowchart syntax to typed architecture IR, with auto-layout, stable diagnostics for unsupported/malformed syntax, and a new 'archify import flowchart' CLI command. - Verification: npm test in archify/ — 751 tests, 730 pass, 0 fail, 21 skipped (Chrome-dependent). Full import→validate→render pipeline verified on all valid fixtures. Closes #92
1 parent 9a50605 commit fedb28e

14 files changed

Lines changed: 897 additions & 0 deletions

archify/bin/archify.mjs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const TYPES = new Set(['architecture', 'workflow', 'sequence', 'dataflow', 'life
1414

1515
function usage() {
1616
return `Usage:
17+
archify import flowchart <input.mmd> [output.json] [--json]
1718
archify render <type> <input.json> [output.html] [--quality standard|showcase] [--repo-root path]
1819
archify compare architecture <base.json> <head.json> [output.html] [--receipt path] [--json] [--quality standard|showcase] [--repo-root path]
1920
archify deliver <type> <input.json> [output.html] [--json] [--open] [--quality standard|showcase] [--repo-root path]
@@ -1594,6 +1595,76 @@ function commandValidate(args) {
15941595
if (exitCode !== 0) process.exitCode = exitCode;
15951596
}
15961597

1598+
async function commandImport(args) {
1599+
const [format, ...rest] = args;
1600+
if (!format) fail('Usage: archify import flowchart <input.mmd> [output.json] [--json]');
1601+
if (format !== 'flowchart') fail(`Unsupported import format "${format}". Supported: flowchart`);
1602+
1603+
let json = false;
1604+
let inputPath = null;
1605+
let outputPath = null;
1606+
for (const arg of rest) {
1607+
if (arg === '--json') { json = true; continue; }
1608+
if (arg.startsWith('--')) fail(`Unknown import option "${arg}".`);
1609+
if (inputPath === null) { inputPath = arg; continue; }
1610+
if (outputPath === null) { outputPath = arg; continue; }
1611+
fail(`Unexpected argument "${arg}".`);
1612+
}
1613+
if (!inputPath) fail('Usage: archify import flowchart <input.mmd> [output.json] [--json]');
1614+
1615+
let source;
1616+
try {
1617+
source = fs.readFileSync(inputPath, 'utf8');
1618+
} catch (error) {
1619+
const receipt = {
1620+
schemaVersion: 1,
1621+
command: 'import',
1622+
source: 'mermaid-flowchart',
1623+
ok: false,
1624+
error: 'Input could not be read.',
1625+
diagnostics: [diagnostic({
1626+
code: 'input/read',
1627+
message: `Input could not be read: ${error.message}`,
1628+
subject: { input: inputPath },
1629+
evidence: { reason: error.message },
1630+
supportedFixes: ['provide one readable .mmd input file'],
1631+
})],
1632+
};
1633+
if (json) console.log(JSON.stringify(receipt, null, 2));
1634+
else console.error(formatDiagnostics(receipt.error, receipt.diagnostics));
1635+
process.exit(1);
1636+
}
1637+
1638+
const { importFlowchart } = await import(pathToFileURL(path.join(skillRoot, 'importers', 'flowchart.mjs')).href);
1639+
const result = importFlowchart(source);
1640+
1641+
if (!result.ok) {
1642+
const receipt = {
1643+
schemaVersion: 1,
1644+
command: 'import',
1645+
source: 'mermaid-flowchart',
1646+
ok: false,
1647+
error: result.diagnostics[0].message,
1648+
diagnostics: result.diagnostics,
1649+
};
1650+
if (json) console.log(JSON.stringify(receipt, null, 2));
1651+
else console.error(formatDiagnostics(receipt.error, receipt.diagnostics));
1652+
process.exit(1);
1653+
}
1654+
1655+
const irJson = JSON.stringify(result.ir, null, 2);
1656+
if (outputPath) {
1657+
fs.writeFileSync(outputPath, irJson + '\n');
1658+
if (!json) console.error(`Imported ${result.ir.components.length} components, ${result.ir.connections.length} connections → ${outputPath}`);
1659+
} else if (!json) {
1660+
console.log(irJson);
1661+
}
1662+
1663+
if (json) {
1664+
console.log(JSON.stringify(result.receipt, null, 2));
1665+
}
1666+
}
1667+
15971668
const [command, ...args] = process.argv.slice(2);
15981669

15991670
switch (command) {
@@ -1603,6 +1674,9 @@ switch (command) {
16031674
case 'help':
16041675
console.log(usage());
16051676
break;
1677+
case 'import':
1678+
await commandImport(args);
1679+
break;
16061680
case 'render':
16071681
commandRender(args);
16081682
break;

0 commit comments

Comments
 (0)