archbyte 0.4.1 → 0.5.0
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 +9 -25
- package/bin/archbyte.js +6 -41
- package/dist/cli/analyze.js +49 -27
- package/dist/cli/license-gate.js +47 -19
- package/dist/cli/run.js +117 -1
- package/dist/cli/setup.d.ts +6 -1
- package/dist/cli/setup.js +35 -16
- package/dist/cli/shared.d.ts +0 -11
- package/dist/cli/shared.js +0 -61
- package/dist/cli/workflow.js +8 -15
- package/dist/server/src/index.js +81 -106
- package/package.json +2 -2
- package/templates/archbyte.yaml +28 -7
- package/ui/dist/assets/index-BXsZyipz.js +72 -0
- package/ui/dist/assets/{index-DDCNauh7.css → index-ow1c3Nxp.css} +1 -1
- package/ui/dist/index.html +2 -2
- package/dist/cli/arch-diff.d.ts +0 -38
- package/dist/cli/arch-diff.js +0 -61
- package/dist/cli/diff.d.ts +0 -10
- package/dist/cli/diff.js +0 -144
- package/dist/cli/patrol.d.ts +0 -18
- package/dist/cli/patrol.js +0 -596
- package/dist/cli/validate.d.ts +0 -53
- package/dist/cli/validate.js +0 -299
- package/ui/dist/assets/index-DO4t5Xu1.js +0 -72
package/dist/cli/workflow.js
CHANGED
|
@@ -8,7 +8,7 @@ const BUILTIN_WORKFLOWS = [
|
|
|
8
8
|
{
|
|
9
9
|
id: "full-analysis",
|
|
10
10
|
name: "Full Analysis Pipeline",
|
|
11
|
-
description: "Complete architecture pipeline: analyze, generate,
|
|
11
|
+
description: "Complete architecture pipeline: analyze, generate, audit, and report",
|
|
12
12
|
steps: [
|
|
13
13
|
{
|
|
14
14
|
id: "generate",
|
|
@@ -17,13 +17,6 @@ const BUILTIN_WORKFLOWS = [
|
|
|
17
17
|
needs: [],
|
|
18
18
|
description: "Generate architecture diagram from analysis JSON",
|
|
19
19
|
},
|
|
20
|
-
{
|
|
21
|
-
id: "validate",
|
|
22
|
-
name: "Validate Architecture",
|
|
23
|
-
command: "archbyte validate",
|
|
24
|
-
needs: ["generate"],
|
|
25
|
-
description: "Run fitness function rules against the architecture",
|
|
26
|
-
},
|
|
27
20
|
{
|
|
28
21
|
id: "stats",
|
|
29
22
|
name: "Architecture Stats",
|
|
@@ -34,23 +27,23 @@ const BUILTIN_WORKFLOWS = [
|
|
|
34
27
|
{
|
|
35
28
|
id: "export",
|
|
36
29
|
name: "Export Mermaid",
|
|
37
|
-
command: "archbyte export --format mermaid",
|
|
30
|
+
command: "archbyte export --format mermaid --output .archbyte/architecture.mmd",
|
|
38
31
|
needs: ["generate"],
|
|
39
|
-
description: "Export architecture as Mermaid diagram",
|
|
32
|
+
description: "Export architecture as Mermaid diagram to .archbyte/architecture.mmd",
|
|
40
33
|
},
|
|
41
34
|
],
|
|
42
35
|
},
|
|
43
36
|
{
|
|
44
37
|
id: "ci-check",
|
|
45
38
|
name: "CI Architecture Check",
|
|
46
|
-
description: "Lightweight CI pipeline:
|
|
39
|
+
description: "Lightweight CI pipeline: audit architecture health",
|
|
47
40
|
steps: [
|
|
48
41
|
{
|
|
49
|
-
id: "
|
|
50
|
-
name: "
|
|
51
|
-
command: "archbyte
|
|
42
|
+
id: "stats",
|
|
43
|
+
name: "Stats",
|
|
44
|
+
command: "archbyte stats --ci",
|
|
52
45
|
needs: [],
|
|
53
|
-
description: "Run
|
|
46
|
+
description: "Run architecture stats in CI mode",
|
|
54
47
|
},
|
|
55
48
|
],
|
|
56
49
|
},
|
package/dist/server/src/index.js
CHANGED
|
@@ -15,13 +15,16 @@ let config;
|
|
|
15
15
|
let sseClients = new Set();
|
|
16
16
|
let diagramWatcher = null;
|
|
17
17
|
let isAnalyzing = false;
|
|
18
|
+
// Source file watcher — detects changes to notify UI for manual re-analysis
|
|
19
|
+
let sourceWatcher = null;
|
|
20
|
+
const pendingSourceChanges = new Map();
|
|
21
|
+
let changeDebounceTimer = null;
|
|
22
|
+
const CHANGE_DEBOUNCE_MS = 2500;
|
|
18
23
|
const sessionChanges = [];
|
|
19
24
|
const MAX_SESSION_CHANGES = 50;
|
|
20
25
|
let currentArchitecture = null;
|
|
21
26
|
// Process tracking for run-from-UI
|
|
22
27
|
const runningWorkflows = new Map();
|
|
23
|
-
let patrolProcess = null;
|
|
24
|
-
let patrolRunning = false;
|
|
25
28
|
let chatProcess = null;
|
|
26
29
|
// Resolve archbyte CLI binary path
|
|
27
30
|
function getArchbyteBin() {
|
|
@@ -331,7 +334,7 @@ function createHttpServer() {
|
|
|
331
334
|
return;
|
|
332
335
|
}
|
|
333
336
|
// API: Audit — unified validation + health endpoint
|
|
334
|
-
if (
|
|
337
|
+
if (url === "/api/audit" && req.method === "GET") {
|
|
335
338
|
const arch = currentArchitecture || (await loadArchitecture());
|
|
336
339
|
if (!arch) {
|
|
337
340
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
@@ -937,51 +940,13 @@ function createHttpServer() {
|
|
|
937
940
|
res.end(content);
|
|
938
941
|
return;
|
|
939
942
|
}
|
|
940
|
-
// API: Patrol history
|
|
941
|
-
if (url === "/api/patrol/latest" && req.method === "GET") {
|
|
942
|
-
const latestPath = path.join(config.workspaceRoot, ".archbyte/patrols/latest.json");
|
|
943
|
-
if (!existsSync(latestPath)) {
|
|
944
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
945
|
-
res.end(JSON.stringify(null));
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
try {
|
|
949
|
-
const content = readFileSync(latestPath, "utf-8");
|
|
950
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
951
|
-
res.end(content);
|
|
952
|
-
}
|
|
953
|
-
catch {
|
|
954
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
955
|
-
res.end(JSON.stringify(null));
|
|
956
|
-
}
|
|
957
|
-
return;
|
|
958
|
-
}
|
|
959
|
-
if (url === "/api/patrol/history" && req.method === "GET") {
|
|
960
|
-
const historyPath = path.join(config.workspaceRoot, ".archbyte/patrols/history.jsonl");
|
|
961
|
-
if (!existsSync(historyPath)) {
|
|
962
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
963
|
-
res.end(JSON.stringify([]));
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
try {
|
|
967
|
-
const content = readFileSync(historyPath, "utf-8");
|
|
968
|
-
const records = content.trim().split("\n").filter(Boolean).slice(-50).map((line) => JSON.parse(line));
|
|
969
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
970
|
-
res.end(JSON.stringify(records));
|
|
971
|
-
}
|
|
972
|
-
catch {
|
|
973
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
974
|
-
res.end(JSON.stringify([]));
|
|
975
|
-
}
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
943
|
// API: Workflow list and status
|
|
979
944
|
if (url === "/api/workflow/list" && req.method === "GET") {
|
|
980
945
|
const workflows = [];
|
|
981
946
|
// Built-in workflows
|
|
982
947
|
const builtins = [
|
|
983
|
-
{ id: "full-analysis", name: "Full Analysis Pipeline", description: "Complete architecture pipeline", steps: ["generate", "
|
|
984
|
-
{ id: "ci-check", name: "CI Architecture Check", description: "Lightweight CI pipeline", steps: ["
|
|
948
|
+
{ id: "full-analysis", name: "Full Analysis Pipeline", description: "Complete architecture pipeline", steps: ["generate", "stats", "export"] },
|
|
949
|
+
{ id: "ci-check", name: "CI Architecture Check", description: "Lightweight CI pipeline", steps: ["stats"] },
|
|
985
950
|
{ id: "drift-check", name: "Architecture Drift Check", description: "Check for architecture drift", steps: ["snapshot", "generate", "diff"] },
|
|
986
951
|
];
|
|
987
952
|
for (const b of builtins) {
|
|
@@ -1197,6 +1162,14 @@ function createHttpServer() {
|
|
|
1197
1162
|
res.end(JSON.stringify(sessionChanges));
|
|
1198
1163
|
return;
|
|
1199
1164
|
}
|
|
1165
|
+
// API: Pending source file changes — hydrates UI on reconnect
|
|
1166
|
+
if (url === "/api/changes/pending" && req.method === "GET") {
|
|
1167
|
+
const files = Array.from(pendingSourceChanges.values()).map((c) => ({ event: c.event, path: c.path }));
|
|
1168
|
+
const count = files.length;
|
|
1169
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1170
|
+
res.end(JSON.stringify({ count, files: files.slice(0, 20), truncated: count > 20 }));
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1200
1173
|
// API: Run workflow
|
|
1201
1174
|
if (url.startsWith("/api/workflow/run/") && req.method === "POST") {
|
|
1202
1175
|
const id = url.split("/").pop();
|
|
@@ -1269,56 +1242,6 @@ function createHttpServer() {
|
|
|
1269
1242
|
});
|
|
1270
1243
|
return;
|
|
1271
1244
|
}
|
|
1272
|
-
// API: Start patrol
|
|
1273
|
-
if (url === "/api/patrol/start" && req.method === "POST") {
|
|
1274
|
-
if (patrolRunning) {
|
|
1275
|
-
res.writeHead(409, { "Content-Type": "application/json" });
|
|
1276
|
-
res.end(JSON.stringify({ error: "Patrol already running" }));
|
|
1277
|
-
return;
|
|
1278
|
-
}
|
|
1279
|
-
let body = "";
|
|
1280
|
-
req.on("data", (chunk) => { body += chunk.toString(); });
|
|
1281
|
-
req.on("end", () => {
|
|
1282
|
-
const { interval } = JSON.parse(body || "{}");
|
|
1283
|
-
const bin = getArchbyteBin();
|
|
1284
|
-
const args = [bin, "patrol"];
|
|
1285
|
-
if (interval)
|
|
1286
|
-
args.push("--interval", String(interval));
|
|
1287
|
-
patrolProcess = spawn(process.execPath, args, {
|
|
1288
|
-
cwd: config.workspaceRoot,
|
|
1289
|
-
stdio: ["ignore", "inherit", "inherit"],
|
|
1290
|
-
env: { ...process.env, FORCE_COLOR: "1" },
|
|
1291
|
-
});
|
|
1292
|
-
patrolRunning = true;
|
|
1293
|
-
broadcastOpsEvent({ type: "patrol:started" });
|
|
1294
|
-
patrolProcess.on("close", () => {
|
|
1295
|
-
patrolProcess = null;
|
|
1296
|
-
patrolRunning = false;
|
|
1297
|
-
broadcastOpsEvent({ type: "patrol:stopped" });
|
|
1298
|
-
});
|
|
1299
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1300
|
-
res.end(JSON.stringify({ ok: true }));
|
|
1301
|
-
});
|
|
1302
|
-
return;
|
|
1303
|
-
}
|
|
1304
|
-
// API: Stop patrol
|
|
1305
|
-
if (url === "/api/patrol/stop" && req.method === "POST") {
|
|
1306
|
-
if (patrolProcess) {
|
|
1307
|
-
patrolProcess.kill("SIGTERM");
|
|
1308
|
-
patrolProcess = null;
|
|
1309
|
-
patrolRunning = false;
|
|
1310
|
-
broadcastOpsEvent({ type: "patrol:stopped" });
|
|
1311
|
-
}
|
|
1312
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1313
|
-
res.end(JSON.stringify({ ok: true }));
|
|
1314
|
-
return;
|
|
1315
|
-
}
|
|
1316
|
-
// API: Patrol running status
|
|
1317
|
-
if (url === "/api/patrol/running" && req.method === "GET") {
|
|
1318
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1319
|
-
res.end(JSON.stringify({ running: patrolRunning }));
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
1245
|
// API: Config — read project config (including provider settings)
|
|
1323
1246
|
if (url === "/api/config" && req.method === "GET") {
|
|
1324
1247
|
const configPath = path.join(config.workspaceRoot, ".archbyte/config.json");
|
|
@@ -1732,12 +1655,22 @@ function diffArchitectures(prev, curr) {
|
|
|
1732
1655
|
.map((e) => ({ source: e.source, target: e.target, label: e.label }));
|
|
1733
1656
|
return { addedNodes, removedNodes, modifiedNodes, addedEdges, removedEdges };
|
|
1734
1657
|
}
|
|
1735
|
-
// Run analyze → generate pipeline (used by /api/analyze
|
|
1658
|
+
// Run analyze → generate pipeline (used by /api/analyze)
|
|
1736
1659
|
function runAnalyzePipeline(mode = "static", fileChanges) {
|
|
1737
1660
|
if (isAnalyzing)
|
|
1738
1661
|
return;
|
|
1739
1662
|
isAnalyzing = true;
|
|
1740
1663
|
const pipelineStart = Date.now();
|
|
1664
|
+
// Capture and clear pending source changes
|
|
1665
|
+
if (!fileChanges) {
|
|
1666
|
+
fileChanges = Array.from(pendingSourceChanges.values()).map((c) => ({ event: c.event, path: c.path }));
|
|
1667
|
+
}
|
|
1668
|
+
pendingSourceChanges.clear();
|
|
1669
|
+
if (changeDebounceTimer) {
|
|
1670
|
+
clearTimeout(changeDebounceTimer);
|
|
1671
|
+
changeDebounceTimer = null;
|
|
1672
|
+
}
|
|
1673
|
+
broadcastOpsEvent({ type: "changes:cleared" });
|
|
1741
1674
|
// Snapshot current architecture for diffing after pipeline completes
|
|
1742
1675
|
const prevArchitecture = currentArchitecture
|
|
1743
1676
|
? { ...currentArchitecture, nodes: [...currentArchitecture.nodes], edges: [...currentArchitecture.edges] }
|
|
@@ -1828,6 +1761,56 @@ function setupWatcher() {
|
|
|
1828
1761
|
broadcastUpdate();
|
|
1829
1762
|
});
|
|
1830
1763
|
}
|
|
1764
|
+
// Broadcast pending source changes to SSE clients
|
|
1765
|
+
function broadcastPendingChanges() {
|
|
1766
|
+
const files = Array.from(pendingSourceChanges.values()).map((c) => c.path);
|
|
1767
|
+
const count = files.length;
|
|
1768
|
+
const truncated = count > 20;
|
|
1769
|
+
broadcastOpsEvent({
|
|
1770
|
+
type: "changes:detected",
|
|
1771
|
+
count,
|
|
1772
|
+
files: files.slice(0, 20),
|
|
1773
|
+
truncated,
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
// Setup source file watcher — watches workspace for code changes
|
|
1777
|
+
function setupSourceWatcher() {
|
|
1778
|
+
sourceWatcher = watch(config.workspaceRoot, {
|
|
1779
|
+
ignoreInitial: true,
|
|
1780
|
+
ignored: [
|
|
1781
|
+
/(^|[/\\])\../, // dotfiles/dirs
|
|
1782
|
+
"**/node_modules/**",
|
|
1783
|
+
"**/dist/**",
|
|
1784
|
+
"**/build/**",
|
|
1785
|
+
"**/out/**",
|
|
1786
|
+
"**/.archbyte/**",
|
|
1787
|
+
"**/coverage/**",
|
|
1788
|
+
"**/*.lock",
|
|
1789
|
+
"**/package-lock.json",
|
|
1790
|
+
"**/__pycache__/**",
|
|
1791
|
+
"**/target/**",
|
|
1792
|
+
"**/vendor/**",
|
|
1793
|
+
],
|
|
1794
|
+
depth: 10,
|
|
1795
|
+
});
|
|
1796
|
+
sourceWatcher.on("all", (event, filePath) => {
|
|
1797
|
+
if (event !== "change" && event !== "add" && event !== "unlink")
|
|
1798
|
+
return;
|
|
1799
|
+
const relativePath = path.relative(config.workspaceRoot, filePath);
|
|
1800
|
+
pendingSourceChanges.set(relativePath, {
|
|
1801
|
+
event,
|
|
1802
|
+
path: relativePath,
|
|
1803
|
+
timestamp: Date.now(),
|
|
1804
|
+
});
|
|
1805
|
+
// Debounce: reset timer on each change, broadcast after settling
|
|
1806
|
+
if (changeDebounceTimer)
|
|
1807
|
+
clearTimeout(changeDebounceTimer);
|
|
1808
|
+
changeDebounceTimer = setTimeout(() => {
|
|
1809
|
+
changeDebounceTimer = null;
|
|
1810
|
+
broadcastPendingChanges();
|
|
1811
|
+
}, CHANGE_DEBOUNCE_MS);
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1831
1814
|
// Graceful shutdown
|
|
1832
1815
|
function setupShutdown() {
|
|
1833
1816
|
const shutdown = async () => {
|
|
@@ -1840,14 +1823,6 @@ function setupShutdown() {
|
|
|
1840
1823
|
catch { }
|
|
1841
1824
|
}
|
|
1842
1825
|
runningWorkflows.clear();
|
|
1843
|
-
if (patrolProcess) {
|
|
1844
|
-
try {
|
|
1845
|
-
patrolProcess.kill("SIGTERM");
|
|
1846
|
-
}
|
|
1847
|
-
catch { }
|
|
1848
|
-
patrolProcess = null;
|
|
1849
|
-
patrolRunning = false;
|
|
1850
|
-
}
|
|
1851
1826
|
if (chatProcess) {
|
|
1852
1827
|
try {
|
|
1853
1828
|
chatProcess.kill("SIGTERM");
|
|
@@ -1862,6 +1837,7 @@ function setupShutdown() {
|
|
|
1862
1837
|
catch { }
|
|
1863
1838
|
}
|
|
1864
1839
|
sseClients.clear();
|
|
1840
|
+
await sourceWatcher?.close();
|
|
1865
1841
|
await diagramWatcher?.close();
|
|
1866
1842
|
httpServer?.close();
|
|
1867
1843
|
process.exit(0);
|
|
@@ -1878,8 +1854,7 @@ function loadLicenseInfo() {
|
|
|
1878
1854
|
tier: "free",
|
|
1879
1855
|
features: {
|
|
1880
1856
|
analyze: true,
|
|
1881
|
-
|
|
1882
|
-
patrol: false,
|
|
1857
|
+
audit: false,
|
|
1883
1858
|
workflows: false,
|
|
1884
1859
|
chat: false,
|
|
1885
1860
|
premiumAgents: false,
|
|
@@ -1931,8 +1906,7 @@ function loadLicenseInfo() {
|
|
|
1931
1906
|
tier: isPremium ? "premium" : "free",
|
|
1932
1907
|
features: {
|
|
1933
1908
|
analyze: true,
|
|
1934
|
-
|
|
1935
|
-
patrol: isPremium,
|
|
1909
|
+
audit: isPremium,
|
|
1936
1910
|
workflows: isPremium,
|
|
1937
1911
|
chat: isPremium,
|
|
1938
1912
|
premiumAgents: isPremium,
|
|
@@ -1956,6 +1930,7 @@ export async function startServer(cfg) {
|
|
|
1956
1930
|
process.exit(1);
|
|
1957
1931
|
}
|
|
1958
1932
|
setupWatcher();
|
|
1933
|
+
setupSourceWatcher();
|
|
1959
1934
|
console.error(`[archbyte] Serving ${config.name}`);
|
|
1960
1935
|
console.error(`[archbyte] Diagram: ${config.diagramPath}`);
|
|
1961
1936
|
// Listen for 'q' keypress to quit gracefully
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archbyte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "ArchByte - See what agents build. As they build it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@anthropic-ai/sdk": "^0.74.0",
|
|
39
|
-
"@google/genai": "^1.
|
|
39
|
+
"@google/genai": "^1.42.0",
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
41
41
|
"chalk": "^5.3.0",
|
|
42
42
|
"chokidar": "^3.5.3",
|
package/templates/archbyte.yaml
CHANGED
|
@@ -102,10 +102,31 @@ rules:
|
|
|
102
102
|
# to: { type: external }
|
|
103
103
|
# level: warn
|
|
104
104
|
|
|
105
|
-
# ──
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
105
|
+
# ── Workflows ──
|
|
106
|
+
# Composable multi-step architecture pipelines.
|
|
107
|
+
# Run built-in workflows:
|
|
108
|
+
# archbyte workflow --list # see available workflows
|
|
109
|
+
# archbyte workflow --run full-analysis
|
|
110
|
+
# archbyte workflow --run ci-check
|
|
111
|
+
# archbyte workflow --run drift-check
|
|
112
|
+
#
|
|
113
|
+
# Create custom workflows in .archbyte/workflows/:
|
|
114
|
+
# archbyte workflow --create "My Pipeline"
|
|
115
|
+
#
|
|
116
|
+
# Custom workflow format (.archbyte/workflows/my-pipeline.yaml):
|
|
117
|
+
# id: my-pipeline
|
|
118
|
+
# name: "My Pipeline"
|
|
119
|
+
# description: "Custom architecture pipeline"
|
|
120
|
+
# steps:
|
|
121
|
+
# - id: generate
|
|
122
|
+
# name: "Generate Diagram"
|
|
123
|
+
# command: "archbyte generate"
|
|
124
|
+
# needs: []
|
|
125
|
+
# - id: validate
|
|
126
|
+
# name: "Validate"
|
|
127
|
+
# command: "archbyte validate"
|
|
128
|
+
# needs: [generate]
|
|
129
|
+
# - id: export
|
|
130
|
+
# name: "Export"
|
|
131
|
+
# command: "archbyte export --format mermaid"
|
|
132
|
+
# needs: [validate]
|