archbyte 0.7.1 → 0.7.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 +21 -82
- package/bin/archbyte.js +27 -1
- package/dist/cli/analyze.js +13 -4
- package/dist/cli/auth.d.ts +26 -0
- package/dist/cli/auth.js +85 -34
- package/dist/cli/config.js +20 -22
- package/dist/cli/export.d.ts +10 -0
- package/dist/cli/export.js +34 -27
- package/dist/cli/license-gate.js +10 -21
- package/dist/cli/output.d.ts +57 -0
- package/dist/cli/output.js +111 -0
- package/dist/cli/shared.d.ts +6 -1
- package/dist/cli/shared.js +20 -10
- package/dist/cli/stats.d.ts +36 -0
- package/dist/cli/stats.js +146 -90
- package/dist/cli/ui.js +27 -14
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +253 -0
- package/package.json +4 -2
package/dist/cli/export.js
CHANGED
|
@@ -1,61 +1,70 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { fileURLToPath } from "url";
|
|
3
3
|
import { resolveArchitecturePath, loadArchitectureFile } from "./shared.js";
|
|
4
|
+
import { isJsonMode, outputSuccess, ArchByteError, EXIT } from "./output.js";
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* Generate export content as structured data.
|
|
7
|
+
* Pure function — no console output, no process.exit().
|
|
6
8
|
*/
|
|
7
|
-
export async function
|
|
9
|
+
export async function generateExport(options) {
|
|
8
10
|
const diagramPath = resolveArchitecturePath(options);
|
|
9
11
|
const arch = loadArchitectureFile(diagramPath);
|
|
10
12
|
const format = options.format || "mermaid";
|
|
11
13
|
const SUPPORTED_FORMATS = ["mermaid", "markdown", "json", "plantuml", "dot", "html"];
|
|
12
14
|
if (!SUPPORTED_FORMATS.includes(format)) {
|
|
13
|
-
|
|
14
|
-
console.error(chalk.red(`Unknown format: "${format}". Supported: ${formatList}`));
|
|
15
|
-
process.exit(1);
|
|
15
|
+
throw new ArchByteError("EXPORT_FAILED", `Unknown format: "${format}". Supported: ${SUPPORTED_FORMATS.join(", ")}`, EXIT.EXPORT_FAILED);
|
|
16
16
|
}
|
|
17
|
-
// HTML export requires Pro tier (skip in dev/local builds)
|
|
18
17
|
if (format === "html" && !process.env.ARCHBYTE_DEV) {
|
|
19
18
|
const { loadCredentials } = await import("./auth.js");
|
|
20
19
|
const creds = loadCredentials();
|
|
21
20
|
if (!creds || creds.tier !== "premium") {
|
|
22
|
-
|
|
23
|
-
console.error(chalk.red("HTML export requires a Pro subscription."));
|
|
24
|
-
console.error(chalk.gray("Upgrade at https://heartbyte.io/archbyte"));
|
|
25
|
-
console.error();
|
|
26
|
-
process.exit(1);
|
|
21
|
+
throw new ArchByteError("LICENSE_DENIED", "HTML export requires a Pro subscription. Upgrade at https://heartbyte.io/archbyte", EXIT.LICENSE_DENIED);
|
|
27
22
|
}
|
|
28
23
|
}
|
|
29
|
-
let
|
|
24
|
+
let content;
|
|
30
25
|
switch (format) {
|
|
31
26
|
case "mermaid":
|
|
32
|
-
|
|
27
|
+
content = exportMermaid(arch);
|
|
33
28
|
break;
|
|
34
29
|
case "json":
|
|
35
|
-
|
|
30
|
+
content = exportJson(arch);
|
|
36
31
|
break;
|
|
37
32
|
case "plantuml":
|
|
38
|
-
|
|
33
|
+
content = exportPlantUML(arch);
|
|
39
34
|
break;
|
|
40
35
|
case "dot":
|
|
41
|
-
|
|
36
|
+
content = exportDot(arch);
|
|
42
37
|
break;
|
|
43
38
|
case "html":
|
|
44
|
-
|
|
39
|
+
content = await exportHtml(arch);
|
|
45
40
|
break;
|
|
46
41
|
default:
|
|
47
|
-
|
|
42
|
+
content = exportMarkdown(arch);
|
|
48
43
|
break;
|
|
49
44
|
}
|
|
45
|
+
let outputPath;
|
|
50
46
|
if (options.output) {
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
const fsMod = await import("fs");
|
|
48
|
+
const pathMod = await import("path");
|
|
49
|
+
outputPath = pathMod.resolve(process.cwd(), options.output);
|
|
50
|
+
fsMod.writeFileSync(outputPath, content, "utf-8");
|
|
51
|
+
}
|
|
52
|
+
return { format, content, outputPath };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Export architecture diagram to Mermaid, Markdown, or JSON format.
|
|
56
|
+
*/
|
|
57
|
+
export async function handleExport(options) {
|
|
58
|
+
const result = await generateExport(options);
|
|
59
|
+
if (isJsonMode()) {
|
|
60
|
+
outputSuccess(result);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (result.outputPath) {
|
|
64
|
+
console.error(chalk.green(`✓ Exported to ${result.outputPath}`));
|
|
56
65
|
}
|
|
57
66
|
else {
|
|
58
|
-
console.log(
|
|
67
|
+
console.log(result.content);
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
70
|
/**
|
|
@@ -335,9 +344,7 @@ async function exportHtml(arch) {
|
|
|
335
344
|
const __dirname = pathMod.dirname(__filename);
|
|
336
345
|
const uiDist = pathMod.resolve(__dirname, "../../ui/dist");
|
|
337
346
|
if (!fs.existsSync(uiDist)) {
|
|
338
|
-
|
|
339
|
-
console.error(chalk.gray("Run: npm run build:ui"));
|
|
340
|
-
process.exit(1);
|
|
347
|
+
throw new ArchByteError("EXPORT_FAILED", "UI build not found. Run: npm run build:ui", EXIT.EXPORT_FAILED);
|
|
341
348
|
}
|
|
342
349
|
// Read CSS and JS assets
|
|
343
350
|
const assetsDir = pathMod.join(uiDist, "assets");
|
package/dist/cli/license-gate.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { loadCredentials, cacheVerifiedTier, resetOfflineActions, checkOfflineAction } from "./auth.js";
|
|
3
3
|
import { API_BASE, NETWORK_TIMEOUT_MS } from "./constants.js";
|
|
4
4
|
import { confirm } from "./ui.js";
|
|
5
|
+
import { ArchByteError, EXIT } from "./output.js";
|
|
5
6
|
/**
|
|
6
7
|
* Pre-flight license check. Must be called before scan/analyze/generate.
|
|
7
8
|
*
|
|
@@ -26,13 +27,12 @@ export async function requireLicense(action) {
|
|
|
26
27
|
console.log();
|
|
27
28
|
const shouldLogin = await confirm("Sign in now?");
|
|
28
29
|
if (!shouldLogin)
|
|
29
|
-
|
|
30
|
+
throw new ArchByteError("AUTH_REQUIRED", "Login required", EXIT.AUTH_REQUIRED);
|
|
30
31
|
const { handleLogin } = await import("./auth.js");
|
|
31
32
|
await handleLogin();
|
|
32
33
|
creds = loadCredentials();
|
|
33
34
|
if (!creds) {
|
|
34
|
-
|
|
35
|
-
process.exit(1);
|
|
35
|
+
throw new ArchByteError("AUTH_REQUIRED", "Login failed. Please try again with `archbyte login`.", EXIT.AUTH_REQUIRED);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
// Token expired locally — offer re-login
|
|
@@ -42,18 +42,16 @@ export async function requireLicense(action) {
|
|
|
42
42
|
console.log(chalk.yellow("Session expired."));
|
|
43
43
|
const shouldRelogin = await confirm("Sign in again?");
|
|
44
44
|
if (!shouldRelogin)
|
|
45
|
-
|
|
45
|
+
throw new ArchByteError("AUTH_EXPIRED", "Session expired", EXIT.AUTH_EXPIRED);
|
|
46
46
|
const { handleLogin } = await import("./auth.js");
|
|
47
47
|
await handleLogin();
|
|
48
48
|
creds = loadCredentials();
|
|
49
49
|
if (!creds) {
|
|
50
|
-
|
|
51
|
-
process.exit(1);
|
|
50
|
+
throw new ArchByteError("AUTH_REQUIRED", "Login failed. Please try again with `archbyte login`.", EXIT.AUTH_REQUIRED);
|
|
52
51
|
}
|
|
53
52
|
const freshExpiry = new Date(creds.expiresAt);
|
|
54
53
|
if (isNaN(freshExpiry.getTime()) || freshExpiry < new Date()) {
|
|
55
|
-
|
|
56
|
-
process.exit(1);
|
|
54
|
+
throw new ArchByteError("AUTH_EXPIRED", "Session still expired. Please try `archbyte login`.", EXIT.AUTH_EXPIRED);
|
|
57
55
|
}
|
|
58
56
|
}
|
|
59
57
|
// Check usage with server
|
|
@@ -72,12 +70,12 @@ export async function requireLicense(action) {
|
|
|
72
70
|
console.log(chalk.yellow("Session invalid."));
|
|
73
71
|
const shouldRelogin = await confirm("Sign in again?");
|
|
74
72
|
if (!shouldRelogin)
|
|
75
|
-
|
|
73
|
+
throw new ArchByteError("AUTH_INVALID", "Session invalid", EXIT.AUTH_INVALID);
|
|
76
74
|
const { handleLogin } = await import("./auth.js");
|
|
77
75
|
await handleLogin();
|
|
78
76
|
creds = loadCredentials();
|
|
79
77
|
if (!creds)
|
|
80
|
-
|
|
78
|
+
throw new ArchByteError("AUTH_REQUIRED", "Login required", EXIT.AUTH_REQUIRED);
|
|
81
79
|
// Re-check usage with fresh credentials
|
|
82
80
|
return requireLicense(action);
|
|
83
81
|
}
|
|
@@ -91,11 +89,7 @@ export async function requireLicense(action) {
|
|
|
91
89
|
cacheVerifiedTier(tier, creds.email);
|
|
92
90
|
resetOfflineActions();
|
|
93
91
|
if (!data.allowed) {
|
|
94
|
-
|
|
95
|
-
console.error(chalk.red.bold("Scan not allowed."));
|
|
96
|
-
console.error(chalk.white(data.message ?? "Check your account status."));
|
|
97
|
-
console.error();
|
|
98
|
-
process.exit(1);
|
|
92
|
+
throw new ArchByteError("LICENSE_DENIED", data.message ?? "Scan not allowed. Check your account status.", EXIT.LICENSE_DENIED);
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
95
|
catch (err) {
|
|
@@ -114,12 +108,7 @@ export async function requireLicense(action) {
|
|
|
114
108
|
function handleOfflineFallback(reason) {
|
|
115
109
|
const { allowed, reason: blockReason } = checkOfflineAction();
|
|
116
110
|
if (!allowed) {
|
|
117
|
-
|
|
118
|
-
console.error(chalk.red(`License server unreachable (${reason}).`));
|
|
119
|
-
console.error(chalk.red(blockReason ?? "Offline actions not permitted."));
|
|
120
|
-
console.error();
|
|
121
|
-
console.error(chalk.gray("Check your internet connection and try again."));
|
|
122
|
-
process.exit(1);
|
|
111
|
+
throw new ArchByteError("NETWORK_ERROR", `License server unreachable (${reason}). ${blockReason ?? "Offline actions not permitted."}`, EXIT.NETWORK_ERROR);
|
|
123
112
|
}
|
|
124
113
|
// Premium user within offline grace window
|
|
125
114
|
console.warn(chalk.yellow(`⚠ License server unreachable (${reason}). Using offline grace period.`));
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured output infrastructure for agent-ready CLI.
|
|
3
|
+
*
|
|
4
|
+
* Provides JSON envelope format, domain-specific exit codes,
|
|
5
|
+
* and output mode state (--json, --quiet) for all commands.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EXIT: {
|
|
8
|
+
readonly SUCCESS: 0;
|
|
9
|
+
readonly GENERAL_ERROR: 1;
|
|
10
|
+
readonly AUTH_REQUIRED: 2;
|
|
11
|
+
readonly AUTH_EXPIRED: 3;
|
|
12
|
+
readonly AUTH_INVALID: 4;
|
|
13
|
+
readonly LICENSE_DENIED: 5;
|
|
14
|
+
readonly CONFIG_MISSING: 6;
|
|
15
|
+
readonly CONFIG_INVALID: 7;
|
|
16
|
+
readonly ANALYSIS_FAILED: 10;
|
|
17
|
+
readonly EXPORT_FAILED: 12;
|
|
18
|
+
readonly FILE_NOT_FOUND: 20;
|
|
19
|
+
readonly NETWORK_ERROR: 30;
|
|
20
|
+
readonly PROVIDER_ERROR: 31;
|
|
21
|
+
};
|
|
22
|
+
export type ExitCode = (typeof EXIT)[keyof typeof EXIT];
|
|
23
|
+
export declare class ArchByteError extends Error {
|
|
24
|
+
readonly code: string;
|
|
25
|
+
readonly exitCode: ExitCode;
|
|
26
|
+
constructor(code: string, message: string, exitCode?: ExitCode);
|
|
27
|
+
}
|
|
28
|
+
export declare function setOutputMode(opts: {
|
|
29
|
+
json?: boolean;
|
|
30
|
+
quiet?: boolean;
|
|
31
|
+
command?: string;
|
|
32
|
+
version?: string;
|
|
33
|
+
}): void;
|
|
34
|
+
export declare function isJsonMode(): boolean;
|
|
35
|
+
export declare function isQuietMode(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Output a success response. In JSON mode, prints the envelope to stdout.
|
|
38
|
+
* Returns the data so callers can chain.
|
|
39
|
+
*/
|
|
40
|
+
export declare function outputSuccess(data: unknown): void;
|
|
41
|
+
/**
|
|
42
|
+
* Output an error response and exit. In JSON mode, prints the envelope to stdout.
|
|
43
|
+
* In human mode, prints to stderr and exits.
|
|
44
|
+
*/
|
|
45
|
+
export declare function outputError(code: string, message: string, exitCode?: ExitCode): never;
|
|
46
|
+
/**
|
|
47
|
+
* Log a message to stderr, respecting quiet and JSON modes.
|
|
48
|
+
* In JSON mode: suppressed (only envelope goes to stdout).
|
|
49
|
+
* In quiet mode: suppressed.
|
|
50
|
+
* Otherwise: prints to stderr.
|
|
51
|
+
*/
|
|
52
|
+
export declare function log(message: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Handle an ArchByteError (or any error) and exit appropriately.
|
|
55
|
+
* Used as the top-level error handler in the CLI.
|
|
56
|
+
*/
|
|
57
|
+
export declare function handleError(err: unknown): never;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured output infrastructure for agent-ready CLI.
|
|
3
|
+
*
|
|
4
|
+
* Provides JSON envelope format, domain-specific exit codes,
|
|
5
|
+
* and output mode state (--json, --quiet) for all commands.
|
|
6
|
+
*/
|
|
7
|
+
// ─── Exit Codes ───
|
|
8
|
+
export const EXIT = {
|
|
9
|
+
SUCCESS: 0,
|
|
10
|
+
GENERAL_ERROR: 1,
|
|
11
|
+
AUTH_REQUIRED: 2,
|
|
12
|
+
AUTH_EXPIRED: 3,
|
|
13
|
+
AUTH_INVALID: 4,
|
|
14
|
+
LICENSE_DENIED: 5,
|
|
15
|
+
CONFIG_MISSING: 6,
|
|
16
|
+
CONFIG_INVALID: 7,
|
|
17
|
+
ANALYSIS_FAILED: 10,
|
|
18
|
+
EXPORT_FAILED: 12,
|
|
19
|
+
FILE_NOT_FOUND: 20,
|
|
20
|
+
NETWORK_ERROR: 30,
|
|
21
|
+
PROVIDER_ERROR: 31,
|
|
22
|
+
};
|
|
23
|
+
// ─── ArchByteError ───
|
|
24
|
+
export class ArchByteError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
exitCode;
|
|
27
|
+
constructor(code, message, exitCode = EXIT.GENERAL_ERROR) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "ArchByteError";
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.exitCode = exitCode;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// ─── Output Mode State ───
|
|
35
|
+
let _jsonMode = false;
|
|
36
|
+
let _quietMode = false;
|
|
37
|
+
let _currentCommand = "";
|
|
38
|
+
let _version = "";
|
|
39
|
+
export function setOutputMode(opts) {
|
|
40
|
+
if (opts.json != null)
|
|
41
|
+
_jsonMode = opts.json;
|
|
42
|
+
if (opts.quiet != null)
|
|
43
|
+
_quietMode = opts.quiet;
|
|
44
|
+
if (opts.command != null)
|
|
45
|
+
_currentCommand = opts.command;
|
|
46
|
+
if (opts.version != null)
|
|
47
|
+
_version = opts.version;
|
|
48
|
+
}
|
|
49
|
+
export function isJsonMode() {
|
|
50
|
+
return _jsonMode;
|
|
51
|
+
}
|
|
52
|
+
export function isQuietMode() {
|
|
53
|
+
return _quietMode;
|
|
54
|
+
}
|
|
55
|
+
function buildMeta() {
|
|
56
|
+
return {
|
|
57
|
+
command: _currentCommand,
|
|
58
|
+
version: _version,
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Output a success response. In JSON mode, prints the envelope to stdout.
|
|
64
|
+
* Returns the data so callers can chain.
|
|
65
|
+
*/
|
|
66
|
+
export function outputSuccess(data) {
|
|
67
|
+
if (_jsonMode) {
|
|
68
|
+
const envelope = { ok: true, data, meta: buildMeta() };
|
|
69
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Output an error response and exit. In JSON mode, prints the envelope to stdout.
|
|
74
|
+
* In human mode, prints to stderr and exits.
|
|
75
|
+
*/
|
|
76
|
+
export function outputError(code, message, exitCode = EXIT.GENERAL_ERROR) {
|
|
77
|
+
if (_jsonMode) {
|
|
78
|
+
const envelope = {
|
|
79
|
+
ok: false,
|
|
80
|
+
error: { code, message },
|
|
81
|
+
meta: buildMeta(),
|
|
82
|
+
};
|
|
83
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
87
|
+
}
|
|
88
|
+
process.exit(exitCode);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Log a message to stderr, respecting quiet and JSON modes.
|
|
92
|
+
* In JSON mode: suppressed (only envelope goes to stdout).
|
|
93
|
+
* In quiet mode: suppressed.
|
|
94
|
+
* Otherwise: prints to stderr.
|
|
95
|
+
*/
|
|
96
|
+
export function log(message) {
|
|
97
|
+
if (_jsonMode || _quietMode)
|
|
98
|
+
return;
|
|
99
|
+
process.stderr.write(message + "\n");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Handle an ArchByteError (or any error) and exit appropriately.
|
|
103
|
+
* Used as the top-level error handler in the CLI.
|
|
104
|
+
*/
|
|
105
|
+
export function handleError(err) {
|
|
106
|
+
if (err instanceof ArchByteError) {
|
|
107
|
+
outputError(err.code, err.message, err.exitCode);
|
|
108
|
+
}
|
|
109
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
110
|
+
outputError("GENERAL_ERROR", message, EXIT.GENERAL_ERROR);
|
|
111
|
+
}
|
package/dist/cli/shared.d.ts
CHANGED
|
@@ -36,7 +36,12 @@ export interface CustomRule {
|
|
|
36
36
|
export declare function resolveArchitecturePath(options: DiagramOptions): string;
|
|
37
37
|
/**
|
|
38
38
|
* Load and parse the architecture JSON file.
|
|
39
|
-
*
|
|
39
|
+
* Returns null if the file is missing or invalid — no process.exit().
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadArchitectureFileSafe(filePath: string): Architecture | null;
|
|
42
|
+
/**
|
|
43
|
+
* Load and parse the architecture JSON file.
|
|
44
|
+
* Throws ArchByteError if the file is missing or invalid.
|
|
40
45
|
*/
|
|
41
46
|
export declare function loadArchitectureFile(filePath: string): Architecture;
|
|
42
47
|
/**
|
package/dist/cli/shared.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
2
|
import * as fs from "fs";
|
|
3
|
-
import
|
|
3
|
+
import { ArchByteError, EXIT } from "./output.js";
|
|
4
4
|
/**
|
|
5
5
|
* Resolve the path to the architecture JSON file from CLI options or defaults.
|
|
6
6
|
*/
|
|
@@ -12,22 +12,32 @@ export function resolveArchitecturePath(options) {
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Load and parse the architecture JSON file.
|
|
15
|
-
*
|
|
15
|
+
* Returns null if the file is missing or invalid — no process.exit().
|
|
16
16
|
*/
|
|
17
|
-
export function
|
|
18
|
-
if (!fs.existsSync(filePath))
|
|
19
|
-
|
|
20
|
-
console.error(chalk.gray("Run archbyte generate first, or provide --diagram <path>"));
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
17
|
+
export function loadArchitectureFileSafe(filePath) {
|
|
18
|
+
if (!fs.existsSync(filePath))
|
|
19
|
+
return null;
|
|
23
20
|
try {
|
|
24
21
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
25
22
|
return JSON.parse(content);
|
|
26
23
|
}
|
|
27
24
|
catch {
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load and parse the architecture JSON file.
|
|
30
|
+
* Throws ArchByteError if the file is missing or invalid.
|
|
31
|
+
*/
|
|
32
|
+
export function loadArchitectureFile(filePath) {
|
|
33
|
+
const arch = loadArchitectureFileSafe(filePath);
|
|
34
|
+
if (!arch) {
|
|
35
|
+
if (!fs.existsSync(filePath)) {
|
|
36
|
+
throw new ArchByteError("FILE_NOT_FOUND", `Architecture file not found: ${filePath}. Run archbyte generate first, or provide --diagram <path>`, EXIT.FILE_NOT_FOUND);
|
|
37
|
+
}
|
|
38
|
+
throw new ArchByteError("FILE_NOT_FOUND", `Failed to parse architecture file: ${filePath}`, EXIT.FILE_NOT_FOUND);
|
|
30
39
|
}
|
|
40
|
+
return arch;
|
|
31
41
|
}
|
|
32
42
|
/**
|
|
33
43
|
* Load rules configuration from archbyte.yaml.
|
package/dist/cli/stats.d.ts
CHANGED
|
@@ -2,6 +2,42 @@ interface StatsOptions {
|
|
|
2
2
|
diagram?: string;
|
|
3
3
|
config?: string;
|
|
4
4
|
}
|
|
5
|
+
interface AnalysisMetadata {
|
|
6
|
+
analyzedAt?: string;
|
|
7
|
+
durationMs?: number;
|
|
8
|
+
filesScanned?: number;
|
|
9
|
+
mode?: string;
|
|
10
|
+
tokenUsage?: {
|
|
11
|
+
input: number;
|
|
12
|
+
output: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
interface HealthIndicator {
|
|
16
|
+
rule: string;
|
|
17
|
+
status: "pass" | "warn";
|
|
18
|
+
message: string;
|
|
19
|
+
details?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export interface StatsData {
|
|
22
|
+
summary: {
|
|
23
|
+
components: number;
|
|
24
|
+
databases: number;
|
|
25
|
+
externals: number;
|
|
26
|
+
connections: number;
|
|
27
|
+
totalNodes: number;
|
|
28
|
+
};
|
|
29
|
+
layers: Record<string, {
|
|
30
|
+
count: number;
|
|
31
|
+
percent: number;
|
|
32
|
+
}>;
|
|
33
|
+
health: HealthIndicator[];
|
|
34
|
+
scan?: AnalysisMetadata;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Compute architecture stats as a structured object.
|
|
38
|
+
* Pure data function — no console output.
|
|
39
|
+
*/
|
|
40
|
+
export declare function computeStats(options: StatsOptions): StatsData;
|
|
5
41
|
/**
|
|
6
42
|
* Print an architecture health dashboard to the terminal.
|
|
7
43
|
*/
|