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/README.md
CHANGED
|
@@ -8,40 +8,24 @@ AI-powered architecture visualization for your codebase. Analyzes code, generate
|
|
|
8
8
|
# Install
|
|
9
9
|
curl -fsSL https://archbyte.heartbyte.io/install.sh | bash
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
archbyte
|
|
13
|
-
|
|
14
|
-
# Configure your AI provider (BYOK)
|
|
15
|
-
archbyte init
|
|
16
|
-
|
|
17
|
-
# Using Claude Code / Codex? Install the MCP server too:
|
|
18
|
-
archbyte mcp install
|
|
11
|
+
# Run (auto-configures on first use)
|
|
12
|
+
archbyte run
|
|
19
13
|
```
|
|
20
14
|
|
|
21
|
-
Your API keys stay on your machine
|
|
15
|
+
On first run, ArchByte walks you through sign-in and provider setup interactively. Your API keys stay on your machine — ArchByte never stores or transmits your provider credentials.
|
|
22
16
|
|
|
23
|
-
###
|
|
17
|
+
### Manual setup
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
Prefer to configure each step separately:
|
|
26
20
|
|
|
27
21
|
```bash
|
|
28
|
-
archbyte
|
|
29
|
-
#
|
|
30
|
-
claude mcp add archbyte --transport stdio -- npx -y archbyte@latest mcp
|
|
22
|
+
archbyte login # Sign in or create a free account
|
|
23
|
+
archbyte init # Configure your AI provider (BYOK)
|
|
31
24
|
```
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
"Analyze the architecture of this project"
|
|
37
|
-
"Export the architecture as a Mermaid diagram"
|
|
38
|
-
"Show me the architecture stats"
|
|
39
|
-
"Read the current architecture diagram"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Available MCP tools: `archbyte_analyze`, `archbyte_generate`, `archbyte_validate` (Pro), `archbyte_export`, `archbyte_stats`, `archbyte_diff`, `archbyte_read_diagram`.
|
|
26
|
+
### Setup with Claude Code
|
|
43
27
|
|
|
44
|
-
|
|
28
|
+
Slash commands — installed globally so they work in any project:
|
|
45
29
|
|
|
46
30
|
```bash
|
|
47
31
|
mkdir -p ~/.claude/commands
|
package/bin/archbyte.js
CHANGED
|
@@ -11,10 +11,7 @@ import { Command } from 'commander';
|
|
|
11
11
|
import { handleServe } from '../dist/cli/serve.js';
|
|
12
12
|
import { handleGenerate } from '../dist/cli/generate.js';
|
|
13
13
|
import { handleStats } from '../dist/cli/stats.js';
|
|
14
|
-
import { handleValidate } from '../dist/cli/validate.js';
|
|
15
14
|
import { handleExport } from '../dist/cli/export.js';
|
|
16
|
-
import { handleDiff } from '../dist/cli/diff.js';
|
|
17
|
-
import { handlePatrol } from '../dist/cli/patrol.js';
|
|
18
15
|
import { handleWorkflow } from '../dist/cli/workflow.js';
|
|
19
16
|
import { handleAnalyze } from '../dist/cli/analyze.js';
|
|
20
17
|
import { handleConfig } from '../dist/cli/config.js';
|
|
@@ -36,9 +33,13 @@ program
|
|
|
36
33
|
.version(PKG_VERSION, '-v, --version', 'Show version number')
|
|
37
34
|
.addHelpText('after', `
|
|
38
35
|
Quick start:
|
|
36
|
+
$ archbyte run Analyze + open diagram (auto-configures on first run)
|
|
37
|
+
|
|
38
|
+
Or step by step:
|
|
39
39
|
$ archbyte login Sign in or create a free account
|
|
40
40
|
$ archbyte init Configure your model provider
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
Run from your project root, or pass --dir <path> to specify the directory.
|
|
42
43
|
|
|
43
44
|
https://archbyte.heartbyte.io
|
|
44
45
|
`);
|
|
@@ -93,7 +94,7 @@ program
|
|
|
93
94
|
.option('--force', 'Force full re-scan (skip incremental detection)')
|
|
94
95
|
.option('--dry-run', 'Preview without running')
|
|
95
96
|
.action(async (options) => {
|
|
96
|
-
|
|
97
|
+
// handleRun manages login + setup + requireLicense internally
|
|
97
98
|
await handleRun(options);
|
|
98
99
|
});
|
|
99
100
|
|
|
@@ -145,27 +146,6 @@ program
|
|
|
145
146
|
await handleStats(options);
|
|
146
147
|
});
|
|
147
148
|
|
|
148
|
-
program
|
|
149
|
-
.command('validate')
|
|
150
|
-
.description('Run architecture fitness function rules')
|
|
151
|
-
.option('-d, --diagram <path>', 'Path to architecture JSON (default: .archbyte/architecture.json)')
|
|
152
|
-
.option('-c, --config <path>', 'Path to archbyte.yaml config')
|
|
153
|
-
.option('--ci', 'Machine-readable JSON output for CI pipelines')
|
|
154
|
-
.action(async (options) => {
|
|
155
|
-
await requireLicense('analyze');
|
|
156
|
-
await handleValidate(options);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
program
|
|
160
|
-
.command('diff')
|
|
161
|
-
.description('Compare architecture snapshots and detect drift')
|
|
162
|
-
.requiredOption('-b, --baseline <path>', 'Path to baseline architecture JSON')
|
|
163
|
-
.option('-c, --current <path>', 'Path to current architecture JSON (default: .archbyte/architecture.json)')
|
|
164
|
-
.option('--config <path>', 'Path to archbyte.yaml config')
|
|
165
|
-
.action(async (options) => {
|
|
166
|
-
await handleDiff(options);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
149
|
program
|
|
170
150
|
.command('export')
|
|
171
151
|
.description('Export architecture to various formats')
|
|
@@ -178,21 +158,6 @@ program
|
|
|
178
158
|
|
|
179
159
|
// — Advanced (Pro) —
|
|
180
160
|
|
|
181
|
-
program
|
|
182
|
-
.command('patrol')
|
|
183
|
-
.description('Run continuous architecture health monitoring (Gastown-inspired patrol loop)')
|
|
184
|
-
.option('-d, --diagram <path>', 'Path to architecture JSON (default: .archbyte/architecture.json)')
|
|
185
|
-
.option('-c, --config <path>', 'Path to archbyte.yaml config')
|
|
186
|
-
.option('-i, --interval <duration>', 'Patrol interval: 30s, 5m, 1h (default: 5m)')
|
|
187
|
-
.option('--on-violation <action>', 'Action on new violations: log, json (default: log)')
|
|
188
|
-
.option('--once', 'Run a single patrol cycle then exit')
|
|
189
|
-
.option('-w, --watch', 'Watch source files for changes instead of polling on interval')
|
|
190
|
-
.option('--history', 'Show patrol history dashboard')
|
|
191
|
-
.action(async (options) => {
|
|
192
|
-
await requireLicense('analyze');
|
|
193
|
-
await handlePatrol(options);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
161
|
program
|
|
197
162
|
.command('workflow')
|
|
198
163
|
.description('Run composable architecture workflows (MEOW-inspired pipeline system)')
|
package/dist/cli/analyze.js
CHANGED
|
@@ -6,7 +6,7 @@ import { resolveConfig } from "./config.js";
|
|
|
6
6
|
import { recordUsage } from "./license-gate.js";
|
|
7
7
|
import { staticResultToSpec, writeSpec, writeMetadata, loadSpec, loadMetadata } from "./yaml-io.js";
|
|
8
8
|
import { getChangedFiles, mapFilesToComponents, shouldRunAgents, isGitAvailable, categorizeChanges, computeNeighbors, getCommitCount } from "./incremental.js";
|
|
9
|
-
import { progressBar } from "./ui.js";
|
|
9
|
+
import { progressBar, confirm } from "./ui.js";
|
|
10
10
|
export async function handleAnalyze(options) {
|
|
11
11
|
const rootDir = options.dir ? path.resolve(options.dir) : process.cwd();
|
|
12
12
|
const isStaticOnly = options.static || options.skipLlm;
|
|
@@ -48,12 +48,18 @@ export async function handleAnalyze(options) {
|
|
|
48
48
|
progress.update(0, `Static analysis: ${msg}`);
|
|
49
49
|
});
|
|
50
50
|
progress.update(1, "Building analysis...");
|
|
51
|
-
const
|
|
51
|
+
const freshAnalysis = buildAnalysisFromStatic(result, rootDir);
|
|
52
52
|
const duration = Date.now() - startTime;
|
|
53
|
+
// Merge into existing analysis if it was produced by an agentic run,
|
|
54
|
+
// preserving LLM-generated components/connections while refreshing
|
|
55
|
+
// static data (environments, metadata, project info).
|
|
56
|
+
const existingAnalysis = loadExistingAnalysis(rootDir);
|
|
57
|
+
const wasAgentic = existingAnalysis && existingAnalysis.metadata?.mode !== "static";
|
|
58
|
+
const analysis = wasAgentic ? mergeStaticIntoExisting(existingAnalysis, freshAnalysis) : freshAnalysis;
|
|
53
59
|
// Stamp scan metadata on analysis.json (backward compat)
|
|
54
60
|
const ameta = analysis.metadata;
|
|
55
61
|
ameta.durationMs = duration;
|
|
56
|
-
ameta.mode = "static";
|
|
62
|
+
ameta.mode = wasAgentic ? "static-refresh" : "static";
|
|
57
63
|
writeAnalysis(rootDir, analysis);
|
|
58
64
|
// Dual-write: archbyte.yaml + metadata.json
|
|
59
65
|
const existingSpec = loadSpec(rootDir);
|
|
@@ -79,28 +85,20 @@ export async function handleAnalyze(options) {
|
|
|
79
85
|
config.apiKey = options.apiKey;
|
|
80
86
|
}
|
|
81
87
|
if (!config) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
chalk.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
chalk.bold("Or run without a model:"),
|
|
97
|
-
chalk.gray(" archbyte analyze --static"),
|
|
98
|
-
"",
|
|
99
|
-
chalk.bold("Supported providers:"),
|
|
100
|
-
chalk.gray(" anthropic, openai, google, claude-sdk"),
|
|
101
|
-
].join("\n");
|
|
102
|
-
console.error(msg);
|
|
103
|
-
throw new Error("No model provider configured");
|
|
88
|
+
console.log(chalk.yellow("No model provider configured."));
|
|
89
|
+
console.log();
|
|
90
|
+
const shouldSetup = await confirm("Set up your AI provider now?");
|
|
91
|
+
if (shouldSetup) {
|
|
92
|
+
const { handleSetup } = await import("./setup.js");
|
|
93
|
+
await handleSetup();
|
|
94
|
+
config = resolveConfig();
|
|
95
|
+
}
|
|
96
|
+
if (!config) {
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(chalk.gray("Running static-only analysis (no AI)."));
|
|
99
|
+
console.log();
|
|
100
|
+
return handleAnalyze({ ...options, static: true });
|
|
101
|
+
}
|
|
104
102
|
}
|
|
105
103
|
const providerLabel = config.provider === "claude-sdk" ? "Claude Code (SDK)" : config.provider;
|
|
106
104
|
console.log(chalk.gray(`Provider: ${chalk.white(providerLabel)}`));
|
|
@@ -413,10 +411,34 @@ function printSummary(analysis, durationMs, mode, options) {
|
|
|
413
411
|
if (!options?.skipServeHint) {
|
|
414
412
|
console.log(` ${chalk.cyan("archbyte serve")} Open the interactive diagram`);
|
|
415
413
|
}
|
|
416
|
-
console.log(` ${chalk.cyan("archbyte validate")} Check architecture fitness rules ${chalk.yellow("[Pro]")}`);
|
|
417
|
-
console.log(` ${chalk.cyan("archbyte patrol")} Continuous architecture monitoring ${chalk.yellow("[Pro]")}`);
|
|
418
414
|
console.log();
|
|
419
415
|
}
|
|
416
|
+
// ─── Analysis merge helpers ───
|
|
417
|
+
function loadExistingAnalysis(rootDir) {
|
|
418
|
+
const p = path.join(rootDir, ".archbyte", "analysis.json");
|
|
419
|
+
try {
|
|
420
|
+
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Merge fresh static scan into an existing agentic analysis.
|
|
428
|
+
* Preserves: components, connections, flows, databases, externalServices (LLM-generated).
|
|
429
|
+
* Updates: project info, environments, metadata, c4/infra if present.
|
|
430
|
+
*/
|
|
431
|
+
function mergeStaticIntoExisting(existing, fresh) {
|
|
432
|
+
return {
|
|
433
|
+
...existing,
|
|
434
|
+
project: fresh.project,
|
|
435
|
+
environments: fresh.environments,
|
|
436
|
+
metadata: {
|
|
437
|
+
...(existing.metadata ?? {}),
|
|
438
|
+
...(fresh.metadata ?? {}),
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
}
|
|
420
442
|
// ─── Analysis converters ───
|
|
421
443
|
/**
|
|
422
444
|
* Convert a StaticAnalysisResult (from pipeline or static-only) into the
|
package/dist/cli/license-gate.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
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
|
+
import { confirm } from "./ui.js";
|
|
4
5
|
/**
|
|
5
6
|
* Pre-flight license check. Must be called before scan/analyze/generate.
|
|
6
7
|
*
|
|
@@ -16,25 +17,44 @@ import { API_BASE, NETWORK_TIMEOUT_MS } from "./constants.js";
|
|
|
16
17
|
* the 1-hour cache window. Exceeding limits blocks the action.
|
|
17
18
|
*/
|
|
18
19
|
export async function requireLicense(action) {
|
|
19
|
-
|
|
20
|
-
// Not logged in
|
|
20
|
+
let creds = loadCredentials();
|
|
21
|
+
// Not logged in — offer interactive login
|
|
21
22
|
if (!creds) {
|
|
22
|
-
console.
|
|
23
|
-
console.
|
|
24
|
-
console.
|
|
25
|
-
console.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
console.log();
|
|
24
|
+
console.log(chalk.yellow("Not signed in."));
|
|
25
|
+
console.log(chalk.gray("Free tier includes unlimited scans. No credit card required."));
|
|
26
|
+
console.log();
|
|
27
|
+
const shouldLogin = await confirm("Sign in now?");
|
|
28
|
+
if (!shouldLogin)
|
|
29
|
+
process.exit(1);
|
|
30
|
+
const { handleLogin } = await import("./auth.js");
|
|
31
|
+
await handleLogin();
|
|
32
|
+
creds = loadCredentials();
|
|
33
|
+
if (!creds) {
|
|
34
|
+
console.error(chalk.red("Login failed. Please try again with `archbyte login`."));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
|
-
// Token expired locally
|
|
38
|
+
// Token expired locally — offer re-login
|
|
32
39
|
const expiry = new Date(creds.expiresAt);
|
|
33
40
|
if (isNaN(expiry.getTime()) || expiry < new Date()) {
|
|
34
|
-
console.
|
|
35
|
-
console.
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
console.log();
|
|
42
|
+
console.log(chalk.yellow("Session expired."));
|
|
43
|
+
const shouldRelogin = await confirm("Sign in again?");
|
|
44
|
+
if (!shouldRelogin)
|
|
45
|
+
process.exit(1);
|
|
46
|
+
const { handleLogin } = await import("./auth.js");
|
|
47
|
+
await handleLogin();
|
|
48
|
+
creds = loadCredentials();
|
|
49
|
+
if (!creds) {
|
|
50
|
+
console.error(chalk.red("Login failed. Please try again with `archbyte login`."));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const freshExpiry = new Date(creds.expiresAt);
|
|
54
|
+
if (isNaN(freshExpiry.getTime()) || freshExpiry < new Date()) {
|
|
55
|
+
console.error(chalk.red("Session still expired. Please try `archbyte login`."));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
38
58
|
}
|
|
39
59
|
// Check usage with server
|
|
40
60
|
try {
|
|
@@ -48,10 +68,18 @@ export async function requireLicense(action) {
|
|
|
48
68
|
signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
|
|
49
69
|
});
|
|
50
70
|
if (res.status === 401) {
|
|
51
|
-
console.
|
|
52
|
-
console.
|
|
53
|
-
|
|
54
|
-
|
|
71
|
+
console.log();
|
|
72
|
+
console.log(chalk.yellow("Session invalid."));
|
|
73
|
+
const shouldRelogin = await confirm("Sign in again?");
|
|
74
|
+
if (!shouldRelogin)
|
|
75
|
+
process.exit(1);
|
|
76
|
+
const { handleLogin } = await import("./auth.js");
|
|
77
|
+
await handleLogin();
|
|
78
|
+
creds = loadCredentials();
|
|
79
|
+
if (!creds)
|
|
80
|
+
process.exit(1);
|
|
81
|
+
// Re-check usage with fresh credentials
|
|
82
|
+
return requireLicense(action);
|
|
55
83
|
}
|
|
56
84
|
if (!res.ok) {
|
|
57
85
|
// Server error — enforce offline limits instead of allowing freely
|
package/dist/cli/run.js
CHANGED
|
@@ -1,8 +1,124 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
1
4
|
import { handleAnalyze } from "./analyze.js";
|
|
2
5
|
import { handleServe } from "./serve.js";
|
|
3
|
-
import { DEFAULT_PORT } from "./constants.js";
|
|
6
|
+
import { DEFAULT_PORT, CONFIG_PATH } from "./constants.js";
|
|
7
|
+
import { loadCredentials } from "./auth.js";
|
|
8
|
+
import { resolveConfig } from "./config.js";
|
|
9
|
+
import { requireLicense } from "./license-gate.js";
|
|
10
|
+
import { isTTY } from "./utils.js";
|
|
11
|
+
/**
|
|
12
|
+
* Check if the user has explicitly configured a provider via `archbyte init`.
|
|
13
|
+
* This reads the raw config file — unlike resolveConfig() which auto-detects
|
|
14
|
+
* Claude Code on PATH, env vars, etc. We want to trigger setup only when
|
|
15
|
+
* the user hasn't gone through init yet.
|
|
16
|
+
*/
|
|
17
|
+
function hasExplicitConfig() {
|
|
18
|
+
try {
|
|
19
|
+
if (!fs.existsSync(CONFIG_PATH))
|
|
20
|
+
return false;
|
|
21
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
22
|
+
return !!config.provider;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
4
28
|
export async function handleRun(options) {
|
|
5
29
|
const port = options.port || DEFAULT_PORT;
|
|
30
|
+
const isStaticOnly = options.static || options.skipLlm;
|
|
31
|
+
// ─── First-run onboarding: login + provider setup ───
|
|
32
|
+
const creds = loadCredentials();
|
|
33
|
+
const needsLogin = !creds;
|
|
34
|
+
const needsSetup = !hasExplicitConfig() && !isStaticOnly && !options.provider;
|
|
35
|
+
if (needsLogin || needsSetup) {
|
|
36
|
+
const dim = chalk.gray;
|
|
37
|
+
const sep = dim(" ───");
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(chalk.bold.cyan(" Welcome to ArchByte"));
|
|
40
|
+
console.log(dim(" Visual observability for agentic development."));
|
|
41
|
+
console.log(dim(" Understand what your agents are building, in real time."));
|
|
42
|
+
console.log();
|
|
43
|
+
console.log(sep);
|
|
44
|
+
console.log();
|
|
45
|
+
console.log(dim(" Docs ") + chalk.cyan("https://archbyte.heartbyte.io/setup.html"));
|
|
46
|
+
console.log(dim(" Website ") + chalk.cyan("https://archbyte.heartbyte.io"));
|
|
47
|
+
console.log();
|
|
48
|
+
console.log(sep);
|
|
49
|
+
console.log();
|
|
50
|
+
console.log(dim(" Let's get you set up. This takes about 30 seconds."));
|
|
51
|
+
console.log();
|
|
52
|
+
// Step 1: Login
|
|
53
|
+
if (needsLogin) {
|
|
54
|
+
const { handleLogin } = await import("./auth.js");
|
|
55
|
+
await handleLogin();
|
|
56
|
+
if (!loadCredentials()) {
|
|
57
|
+
console.error(chalk.red("Login required. Run `archbyte login` to try again."));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Step 2: Provider setup
|
|
62
|
+
if (needsSetup) {
|
|
63
|
+
const { handleSetup } = await import("./setup.js");
|
|
64
|
+
await handleSetup({ calledFromRun: true });
|
|
65
|
+
}
|
|
66
|
+
// Show config summary and pause before scanning
|
|
67
|
+
const resolvedConfig = resolveConfig();
|
|
68
|
+
const resolvedCreds = loadCredentials();
|
|
69
|
+
const rootDir = options.dir ? path.resolve(options.dir) : process.cwd();
|
|
70
|
+
console.log(sep);
|
|
71
|
+
console.log();
|
|
72
|
+
console.log(" " + chalk.bold("Ready to scan"));
|
|
73
|
+
console.log();
|
|
74
|
+
if (resolvedCreds) {
|
|
75
|
+
console.log(dim(" Account ") + chalk.white(resolvedCreds.email));
|
|
76
|
+
}
|
|
77
|
+
if (resolvedConfig) {
|
|
78
|
+
const providerLabel = resolvedConfig.provider === "claude-sdk"
|
|
79
|
+
? "Claude Code (SDK)"
|
|
80
|
+
: resolvedConfig.provider;
|
|
81
|
+
console.log(dim(" Provider ") + chalk.white(providerLabel));
|
|
82
|
+
if (resolvedConfig.model) {
|
|
83
|
+
console.log(dim(" Model ") + chalk.white(resolvedConfig.model));
|
|
84
|
+
}
|
|
85
|
+
if (resolvedConfig.provider !== "claude-sdk") {
|
|
86
|
+
console.log(dim(" API key ") + (resolvedConfig.apiKey ? chalk.green("configured") : chalk.yellow("not set")));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
console.log(dim(" Project ") + chalk.white(path.basename(rootDir)));
|
|
90
|
+
console.log(dim(" Directory ") + chalk.white(rootDir));
|
|
91
|
+
console.log();
|
|
92
|
+
// Pause — let the user absorb the config before scanning
|
|
93
|
+
if (isTTY()) {
|
|
94
|
+
await new Promise((resolve) => {
|
|
95
|
+
process.stdout.write(dim(" Press Enter to start analysis (q to quit)..."));
|
|
96
|
+
const stdin = process.stdin;
|
|
97
|
+
stdin.setRawMode(true);
|
|
98
|
+
stdin.resume();
|
|
99
|
+
stdin.setEncoding("utf8");
|
|
100
|
+
const onData = (data) => {
|
|
101
|
+
if (data === "\r" || data === "\n" || data === " ") {
|
|
102
|
+
stdin.setRawMode(false);
|
|
103
|
+
stdin.pause();
|
|
104
|
+
stdin.removeListener("data", onData);
|
|
105
|
+
process.stdout.write("\n\n");
|
|
106
|
+
resolve();
|
|
107
|
+
}
|
|
108
|
+
else if (data === "q" || data === "Q" || data === "\u0003" || data === "\u001b") {
|
|
109
|
+
stdin.setRawMode(false);
|
|
110
|
+
stdin.pause();
|
|
111
|
+
stdin.removeListener("data", onData);
|
|
112
|
+
process.stdout.write("\n");
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
stdin.on("data", onData);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// License/usage check (creds guaranteed to exist after onboarding)
|
|
121
|
+
await requireLicense("analyze");
|
|
6
122
|
// 1. Analyze (includes auto-generate)
|
|
7
123
|
await handleAnalyze({
|
|
8
124
|
verbose: options.verbose,
|
package/dist/cli/setup.d.ts
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
interface SetupOptions {
|
|
2
|
+
/** When true, skip "archbyte run" in next steps (called from `archbyte run`) */
|
|
3
|
+
calledFromRun?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function handleSetup(opts?: SetupOptions): Promise<void>;
|
|
6
|
+
export {};
|
package/dist/cli/setup.js
CHANGED
|
@@ -207,7 +207,8 @@ async function validateProviderSilent(providerName, apiKey, model) {
|
|
|
207
207
|
function getProfiles(config) {
|
|
208
208
|
return config.profiles ?? {};
|
|
209
209
|
}
|
|
210
|
-
export async function handleSetup() {
|
|
210
|
+
export async function handleSetup(opts) {
|
|
211
|
+
const calledFromRun = opts?.calledFromRun ?? false;
|
|
211
212
|
console.log();
|
|
212
213
|
console.log(chalk.bold.cyan("ArchByte Setup"));
|
|
213
214
|
console.log(chalk.gray("Configure your model provider and API key.\n"));
|
|
@@ -300,12 +301,20 @@ export async function handleSetup() {
|
|
|
300
301
|
console.log();
|
|
301
302
|
console.log(sep);
|
|
302
303
|
console.log();
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
304
|
+
if (calledFromRun) {
|
|
305
|
+
console.log(dim(" Continuing to scan your codebase..."));
|
|
306
|
+
console.log();
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
console.log(" " + chalk.bold("Next steps"));
|
|
310
|
+
console.log();
|
|
311
|
+
console.log(" " + chalk.cyan("archbyte run") + " Analyze your codebase");
|
|
312
|
+
console.log(" " + chalk.cyan("archbyte status") + " Check account and usage");
|
|
313
|
+
console.log(" " + chalk.cyan("archbyte --help") + " See all commands");
|
|
314
|
+
console.log();
|
|
315
|
+
console.log(dim(" Run from your project root, or use --dir <path> to specify the directory."));
|
|
316
|
+
console.log();
|
|
317
|
+
}
|
|
309
318
|
return;
|
|
310
319
|
}
|
|
311
320
|
if (choice === "codex") {
|
|
@@ -588,18 +597,28 @@ export async function handleSetup() {
|
|
|
588
597
|
console.log();
|
|
589
598
|
console.log(sep);
|
|
590
599
|
console.log();
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
600
|
+
if (calledFromRun) {
|
|
601
|
+
if (result === false) {
|
|
602
|
+
console.log(chalk.yellow(" Warning: credentials unverified. Saving anyway."));
|
|
603
|
+
console.log();
|
|
604
|
+
}
|
|
605
|
+
console.log(dim(" Continuing to scan your codebase..."));
|
|
606
|
+
console.log();
|
|
595
607
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
608
|
+
else {
|
|
609
|
+
console.log(" " + chalk.bold("Next steps"));
|
|
610
|
+
console.log();
|
|
611
|
+
console.log(" " + chalk.cyan("archbyte run") + " Analyze your codebase");
|
|
612
|
+
console.log(" " + chalk.cyan("archbyte status") + " Check account and usage");
|
|
613
|
+
console.log(" " + chalk.cyan("archbyte --help") + " See all commands");
|
|
614
|
+
console.log();
|
|
615
|
+
console.log(dim(" Run from your project root, or use --dir <path> to specify the directory."));
|
|
616
|
+
if (result === false) {
|
|
617
|
+
console.log();
|
|
618
|
+
console.log(chalk.yellow(" Warning: credentials unverified. Saving anyway."));
|
|
619
|
+
}
|
|
599
620
|
console.log();
|
|
600
|
-
console.log(chalk.yellow(" Warning: credentials unverified. Saving anyway."));
|
|
601
621
|
}
|
|
602
|
-
console.log();
|
|
603
622
|
}
|
|
604
623
|
function writeArchbyteReadme(archbyteDir) {
|
|
605
624
|
const readmePath = path.join(archbyteDir, "README.md");
|
package/dist/cli/shared.d.ts
CHANGED
|
@@ -64,16 +64,5 @@ export declare function parseRulesFromYaml(content: string): RuleConfig;
|
|
|
64
64
|
* level: error
|
|
65
65
|
*/
|
|
66
66
|
export declare function parseCustomRulesFromYaml(content: string): CustomRule[];
|
|
67
|
-
/**
|
|
68
|
-
* Parse the patrol.ignore list from archbyte.yaml.
|
|
69
|
-
* Returns user-defined glob patterns for watch mode to ignore.
|
|
70
|
-
*
|
|
71
|
-
* patrol:
|
|
72
|
-
* ignore:
|
|
73
|
-
* - "docs/"
|
|
74
|
-
* - "*.md"
|
|
75
|
-
* - "build/"
|
|
76
|
-
*/
|
|
77
|
-
export declare function loadPatrolIgnore(configPath?: string): string[];
|
|
78
67
|
export declare function getRuleLevel(config: RuleConfig, rule: keyof RuleConfig, defaultLevel: RuleLevel): RuleLevel;
|
|
79
68
|
export declare function getThreshold(config: RuleConfig, rule: "max-connections", defaultVal: number): number;
|
package/dist/cli/shared.js
CHANGED
|
@@ -259,67 +259,6 @@ export function parseCustomRulesFromYaml(content) {
|
|
|
259
259
|
flushItem();
|
|
260
260
|
return rules;
|
|
261
261
|
}
|
|
262
|
-
/**
|
|
263
|
-
* Parse the patrol.ignore list from archbyte.yaml.
|
|
264
|
-
* Returns user-defined glob patterns for watch mode to ignore.
|
|
265
|
-
*
|
|
266
|
-
* patrol:
|
|
267
|
-
* ignore:
|
|
268
|
-
* - "docs/"
|
|
269
|
-
* - "*.md"
|
|
270
|
-
* - "build/"
|
|
271
|
-
*/
|
|
272
|
-
export function loadPatrolIgnore(configPath) {
|
|
273
|
-
const rootDir = process.cwd();
|
|
274
|
-
const yamlPath = configPath
|
|
275
|
-
? path.resolve(rootDir, configPath)
|
|
276
|
-
: path.join(rootDir, ".archbyte", "archbyte.yaml");
|
|
277
|
-
if (!fs.existsSync(yamlPath))
|
|
278
|
-
return [];
|
|
279
|
-
try {
|
|
280
|
-
const lines = fs.readFileSync(yamlPath, "utf-8").split("\n");
|
|
281
|
-
const patterns = [];
|
|
282
|
-
let inPatrol = false;
|
|
283
|
-
let inIgnore = false;
|
|
284
|
-
for (const line of lines) {
|
|
285
|
-
const trimmed = line.trimEnd();
|
|
286
|
-
if (/^patrol:\s*$/.test(trimmed)) {
|
|
287
|
-
inPatrol = true;
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
// Another top-level section ends patrol
|
|
291
|
-
if (inPatrol && /^\S/.test(trimmed) && !trimmed.startsWith("#")) {
|
|
292
|
-
inPatrol = false;
|
|
293
|
-
inIgnore = false;
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
if (!inPatrol)
|
|
297
|
-
continue;
|
|
298
|
-
if (trimmed === "" || trimmed.trim().startsWith("#"))
|
|
299
|
-
continue;
|
|
300
|
-
if (/^ {2}ignore:\s*$/.test(trimmed)) {
|
|
301
|
-
inIgnore = true;
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
// Another patrol sub-key ends ignore
|
|
305
|
-
if (inIgnore && /^ {2}\S/.test(trimmed) && !/^ {2}ignore:/.test(trimmed)) {
|
|
306
|
-
inIgnore = false;
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
if (!inIgnore)
|
|
310
|
-
continue;
|
|
311
|
-
// List item: " - pattern"
|
|
312
|
-
const itemMatch = trimmed.match(/^ {4}-\s+"?([^"]+)"?\s*$/);
|
|
313
|
-
if (itemMatch) {
|
|
314
|
-
patterns.push(itemMatch[1].trim());
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return patterns;
|
|
318
|
-
}
|
|
319
|
-
catch {
|
|
320
|
-
return [];
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
262
|
export function getRuleLevel(config, rule, defaultLevel) {
|
|
324
263
|
const entry = config[rule];
|
|
325
264
|
if (!entry)
|