@alfacapital-tech/c4builder 0.9.0-rc.1 → 0.9.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +4 -2
- package/dist/cli/agent.js +21 -0
- package/dist/cli/commands/check.js +9 -6
- package/dist/cli/commands/feedback.js +45 -0
- package/dist/cli/commands/site.comments.js +318 -77
- package/dist/cli/commands/site.i18n.js +88 -0
- package/dist/cli/commands/site.js +152 -8
- package/dist/cli/dispatch.js +15 -4
- package/dist/config/defaults.js +2 -1
- package/dist/config/schema.js +2 -0
- package/dist/core/compose/markdown.js +3 -2
- package/dist/core/feedback.js +59 -0
- package/dist/core/render/bpmnrenderer.js +3 -28
- package/dist/core/render/diagrams.js +22 -10
- package/dist/core/render/mermaidrenderer.js +495 -0
- package/dist/core/render/textmetrics.js +36 -0
- package/dist/core/scan/fences.js +2 -2
- package/dist/core/scan/tree.js +14 -9
- package/dist/plugins/openspec/render.js +1 -1
- package/package.json +2 -1
- package/template/AGENTS.md +16 -4
- package/template/src/6 Mermaid Example/flow.mmd +6 -0
- package/template/src/6 Mermaid Example/mermaid.md +16 -0
package/README.MD
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**Architecture as code that an agent writes, checks and reads.**
|
|
6
6
|
|
|
7
7
|
C4-Builder is a CLI that builds a folder of markdown files and [PlantUML](https://plantuml.com/),
|
|
8
|
-
[D2](https://d2lang.com/)
|
|
8
|
+
[D2](https://d2lang.com/), [BPMN](https://www.bpmn.org/) or [Mermaid](https://mermaid.js.org/) diagrams into documentation: a [docsify](https://docsify.js.org/)
|
|
9
9
|
site, a markdown file per folder, or a single file. Folders follow the
|
|
10
10
|
[C4 model](https://c4model.com/) (system → container → component), and git keeps the history of
|
|
11
11
|
architecture decisions next to the code.
|
|
@@ -19,7 +19,7 @@ than a hundred diagrams, so we continue it as a maintained fork, adapted to work
|
|
|
19
19
|
- **Deterministic offline rendering.** The PlantUML jar, the diagram font and the engines ship
|
|
20
20
|
with the package; layout is computed by PlantUML's built-in Smetana engine, no Graphviz. The
|
|
21
21
|
same sources produce the same SVG on a laptop, in CI and in Docker, with no network access.
|
|
22
|
-
- **`c4builder check`** validates individual `.puml`/`.iuml`/`.d2`/`.bpmn` files with the build's own
|
|
22
|
+
- **`c4builder check`** validates individual `.puml`/`.iuml`/`.d2`/`.bpmn`/`.mmd` files with the build's own
|
|
23
23
|
engine and returns an exit code and the error line — fast feedback for agents, pre-commit
|
|
24
24
|
hooks and CI.
|
|
25
25
|
- **Plugins.** `openspec` publishes an [OpenSpec](https://github.com/Fission-AI/OpenSpec) store
|
|
@@ -29,6 +29,8 @@ than a hundred diagrams, so we continue it as a maintained fork, adapted to work
|
|
|
29
29
|
without a system Java, c4builder downloads a private JRE.
|
|
30
30
|
- **BPMN** processes written as BPMN 2.0 XML without coordinates: the build validates the model
|
|
31
31
|
and lays it out with horizontal pools and lanes.
|
|
32
|
+
- **Mermaid** `.mmd` files and ```` ```mermaid ```` blocks are rendered at build time by real mermaid,
|
|
33
|
+
without a browser: images in every output instead of client-side rendering.
|
|
32
34
|
- **Maintained.** TypeScript, golden rendering snapshots, CI on every pull request, releases to
|
|
33
35
|
npm and GHCR. The PDF output of the original was removed.
|
|
34
36
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const AGENT_VARS = [
|
|
2
|
+
['CLAUDECODE', 'claude-code'],
|
|
3
|
+
['CODEX_THREAD_ID', 'codex'],
|
|
4
|
+
['GEMINI_CLI', 'gemini-cli'],
|
|
5
|
+
['OPENCODE', 'opencode'],
|
|
6
|
+
['COPILOT_CLI', 'copilot-cli']
|
|
7
|
+
];
|
|
8
|
+
const fromEnv = (env) => {
|
|
9
|
+
const own = env.C4BUILDER_AGENT;
|
|
10
|
+
if (own)
|
|
11
|
+
return { on: own !== '0', name: own, explicit: true };
|
|
12
|
+
const known = AGENT_VARS.find(([key]) => env[key]);
|
|
13
|
+
const name = known ? known[1] : env.AI_AGENT;
|
|
14
|
+
return { on: !!name, name: name ?? '', explicit: false };
|
|
15
|
+
};
|
|
16
|
+
export const detectAgent = (env, flags) => {
|
|
17
|
+
const found = fromEnv(env);
|
|
18
|
+
if (flags.agent === undefined)
|
|
19
|
+
return found;
|
|
20
|
+
return { on: flags.agent, name: found.on ? found.name : 'agent', explicit: true };
|
|
21
|
+
};
|
|
@@ -7,8 +7,9 @@ import { resolveJava } from "../../core/render/jre.js";
|
|
|
7
7
|
import { renderDiagram } from "../../core/render/diagrams.js";
|
|
8
8
|
import { renderD2, teardownD2 } from "../../core/render/d2renderer.js";
|
|
9
9
|
import { BpmnError, renderBpmn, teardownBpmn } from "../../core/render/bpmnrenderer.js";
|
|
10
|
+
import { MermaidError, renderMermaid, teardownMermaid } from "../../core/render/mermaidrenderer.js";
|
|
10
11
|
// `c4builder check <file...>` — валидация отдельных диаграмм ТЕМ ЖЕ движком, что и
|
|
11
|
-
// сборка (вендорный PlantUML jar + системная/managed java, бандл D2, конвейер BPMN), без проекта и
|
|
12
|
+
// сборка (вендорный PlantUML jar + системная/managed java, бандл D2, конвейер BPMN, mermaid), без проекта и
|
|
12
13
|
// `.c4builder`. Рендер идёт в память и выбрасывается: проверяем ровно то, что упало бы
|
|
13
14
|
// в сборке (системный `plantuml` может отличаться версией). Код выхода 0 — все файлы ок,
|
|
14
15
|
// 1 — есть ошибка (движка, неизвестного расширения или чтения файла).
|
|
@@ -64,11 +65,12 @@ const CHECKERS = {
|
|
|
64
65
|
'.puml': checkPlantUml,
|
|
65
66
|
'.iuml': checkPlantUml,
|
|
66
67
|
'.d2': (abs) => renderD2(abs),
|
|
67
|
-
'.bpmn': async (abs) => renderBpmn(await readFile(abs, 'utf8'), path.relative(process.cwd(), abs))
|
|
68
|
+
'.bpmn': async (abs) => renderBpmn(await readFile(abs, 'utf8'), path.relative(process.cwd(), abs)),
|
|
69
|
+
'.mmd': async (abs) => renderMermaid(await readFile(abs, 'utf8'), path.relative(process.cwd(), abs))
|
|
68
70
|
};
|
|
69
71
|
export default async (files) => {
|
|
70
72
|
if (files.length === 0) {
|
|
71
|
-
console.log(chalk.red('usage: c4builder check <file...> (.puml | .iuml | .d2 | .bpmn)'));
|
|
73
|
+
console.log(chalk.red('usage: c4builder check <file...> (.puml | .iuml | .d2 | .bpmn | .mmd)'));
|
|
72
74
|
process.exit(1);
|
|
73
75
|
}
|
|
74
76
|
let failed = 0;
|
|
@@ -78,20 +80,21 @@ export default async (files) => {
|
|
|
78
80
|
const check = CHECKERS[path.extname(abs).toLowerCase()];
|
|
79
81
|
try {
|
|
80
82
|
if (!check)
|
|
81
|
-
throw new Error('unsupported extension (expected .puml, .iuml, .d2 or .
|
|
83
|
+
throw new Error('unsupported extension (expected .puml, .iuml, .d2, .bpmn or .mmd)');
|
|
82
84
|
await check(abs);
|
|
83
85
|
console.log(chalk.green(`✓ ${file}`));
|
|
84
86
|
}
|
|
85
87
|
catch (e) {
|
|
86
88
|
failed++;
|
|
87
|
-
// BPMN: по строке на нарушение — агент правит их по id
|
|
88
|
-
const problems = e instanceof BpmnError ? e.problems : [e.message];
|
|
89
|
+
// BPMN: по строке на нарушение — агент правит их по id элемента; mermaid — `line N: …`.
|
|
90
|
+
const problems = e instanceof BpmnError || e instanceof MermaidError ? e.problems : [e.message];
|
|
89
91
|
for (const p of problems)
|
|
90
92
|
console.error(chalk.red(`✗ ${file}: ${p}`));
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
95
|
await teardownD2();
|
|
94
96
|
await teardownBpmn();
|
|
97
|
+
await teardownMermaid();
|
|
95
98
|
if (failed)
|
|
96
99
|
process.exit(1);
|
|
97
100
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { parseConfig } from "../../config/options.js";
|
|
4
|
+
import { FEEDBACK_HEADER, FEEDBACK_PATH } from "./site.comments.js";
|
|
5
|
+
// `c4builder feedback` (change site-agent-feedback, design D9): забирает пачку замечаний у `site -w`.
|
|
6
|
+
// Конфиг читается как JSON без Configstore — тот создал бы `.c4builder` в любом каталоге. Ошибки —
|
|
7
|
+
// через exitCode, а не process.exit: напечатанная в pipe пачка должна успеть уйти целиком.
|
|
8
|
+
export default async (configPath, opts) => {
|
|
9
|
+
let raw;
|
|
10
|
+
try {
|
|
11
|
+
raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
12
|
+
}
|
|
13
|
+
catch { }
|
|
14
|
+
const config = parseConfig(raw && typeof raw === 'object' ? raw : {});
|
|
15
|
+
const port = opts.port ?? (config.ok ? config.value : config.salvaged).webPort;
|
|
16
|
+
const shown = `http://localhost:${port}`;
|
|
17
|
+
const fail = (message) => {
|
|
18
|
+
console.error(chalk.red(message));
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
};
|
|
21
|
+
// 127.0.0.1, а не localhost: сервер слушает только IPv4, а `localhost` → `::1` на части машин
|
|
22
|
+
// подвешивает соединение.
|
|
23
|
+
const url = `http://127.0.0.1:${port}${FEEDBACK_PATH}/next${opts.wait ? '' : '?wait=0'}`;
|
|
24
|
+
for (;;) {
|
|
25
|
+
let res;
|
|
26
|
+
try {
|
|
27
|
+
res = await fetch(url, { headers: { [FEEDBACK_HEADER]: '1' } });
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return fail(`no c4builder site -w on ${shown} — start it first`);
|
|
31
|
+
}
|
|
32
|
+
if (res.status === 200) {
|
|
33
|
+
process.stdout.write(await res.text());
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (res.status === 204) {
|
|
37
|
+
if (opts.wait)
|
|
38
|
+
continue;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (res.status === 404)
|
|
42
|
+
return fail(`agent feedback is disabled on ${shown} — the server runs without -w or with --no-agent`);
|
|
43
|
+
return fail(`unexpected response ${res.status} from ${shown}`);
|
|
44
|
+
}
|
|
45
|
+
};
|