@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 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/) or [BPMN](https://www.bpmn.org/) diagrams into documentation: a [docsify](https://docsify.js.org/)
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 .bpmn)');
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
+ };