septum 0.1.6 → 0.1.8
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 +38 -37
- package/package.json +6 -5
- package/src/cli/commands/check.ts +34 -122
- package/src/cli/commands/hook.ts +68 -16
- package/src/cli/commands/init.ts +440 -121
- package/src/cli/commands/sync.ts +27 -17
- package/src/cli/commands/topology.ts +89 -47
- package/src/cli/index.ts +7 -68
- package/src/cli/scaffold/harness-config.ts +697 -0
- package/src/cli/templates/hooks.ts +6 -6
- package/src/cli/templates/rules.ts +8 -8
- package/src/cli/templates/skill.ts +11 -18
- package/src/cli/ui/prompts.ts +122 -0
- package/src/core/boundary/evaluator.ts +21 -25
- package/src/core/boundary/staged-auditor.ts +50 -0
- package/src/core/config/loader.ts +21 -24
- package/src/core/database/client.ts +8 -0
- package/src/core/database/repositories/dependency.repository.ts +80 -16
- package/src/core/database/repositories/domain.repository.ts +86 -33
- package/src/core/database/repositories/symbol.repository.ts +147 -65
- package/src/core/database/repository.ts +17 -5
- package/src/core/database/schema.ts +1 -1
- package/src/core/discovery/topology-detector.ts +71 -19
- package/src/core/ingestion/pipeline.ts +274 -126
- package/src/core/parser/extractors/frontend.ts +1 -0
- package/src/core/parser/extractors/go.ts +2 -2
- package/src/core/parser/extractors/laravel-semantic.ts +9 -8
- package/src/core/parser/extractors/mcp-cli-semantic.ts +56 -16
- package/src/core/parser/extractors/nestjs-semantic.ts +3 -2
- package/src/core/parser/extractors/php.ts +2 -2
- package/src/core/parser/extractors/python.ts +2 -2
- package/src/core/parser/extractors/typescript.ts +2 -2
- package/src/core/parser/schema/providers/laravel-migration-provider.ts +4 -3
- package/src/core/parser/schema/providers/prisma-schema-provider.ts +2 -1
- package/src/core/parser/tree-sitter.ts +6 -1
- package/src/core/parser/wasm-assets.ts +17 -0
- package/src/core/parser/wasm-loader.ts +3 -0
- package/src/core/resolver/call-graph-tracer.ts +4 -3
- package/src/core/resolver/module-resolver.ts +1 -10
- package/src/core/resolver/path-resolver.ts +57 -4
- package/src/core/resolver/vertical-slice-tracer.ts +1 -0
- package/src/core/resources/ignore.ts +35 -0
- package/src/core/resources/limits.ts +14 -0
- package/src/core/resources/source-files.ts +62 -0
- package/src/core/resources/work-queue.ts +74 -0
- package/src/core/session/session-manager.ts +67 -38
- package/src/core/telemetry/telemetry.ts +12 -1
- package/src/core/topology/environment-detector.ts +21 -11
- package/src/mcp/schemas.ts +381 -8
- package/src/mcp/server.ts +326 -529
- package/src/mcp/tools/boundary.ts +17 -0
- package/src/mcp/tools/check-boundary.ts +97 -43
- package/src/mcp/tools/domain.ts +40 -0
- package/src/mcp/tools/feature.ts +67 -0
- package/src/mcp/tools/get-domain-catalog.ts +15 -7
- package/src/mcp/tools/get-environment-topology.ts +2 -2
- package/src/mcp/tools/get-feature-context.ts +9 -12
- package/src/mcp/tools/get-symbol-impact.ts +5 -2
- package/src/mcp/tools/get-symbol.ts +2 -1
- package/src/mcp/tools/locate-symbol.ts +28 -5
- package/src/mcp/tools/register-domain.ts +25 -9
- package/src/mcp/tools/register-feature.ts +40 -21
- package/src/mcp/tools/slice.ts +15 -0
- package/src/mcp/tools/symbol.ts +82 -0
- package/src/mcp/workspace-manager.ts +159 -0
- package/src/mcp/workspace-watcher.ts +202 -0
- package/src/types/index.ts +21 -1
- package/src/types/wasm.d.ts +4 -0
- package/src/cli/commands/ingest.ts +0 -29
- package/src/cli/commands/locate.ts +0 -61
- package/src/cli/commands/query.ts +0 -104
- package/src/cli/commands/slice.ts +0 -50
package/src/cli/commands/sync.ts
CHANGED
|
@@ -1,31 +1,41 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
1
2
|
import { ConfigLoader } from "../../core/config/loader.ts";
|
|
2
3
|
import { SeptumDatabase } from "../../core/database/client.ts";
|
|
3
4
|
import { SeptumRepository } from "../../core/database/repository.ts";
|
|
4
5
|
import { IngestionPipeline } from "../../core/ingestion/pipeline.ts";
|
|
6
|
+
import { resolveWorkspaceRoot } from "../../core/resolver/path-resolver.ts";
|
|
5
7
|
|
|
6
8
|
export interface SyncCommandOptions {
|
|
7
9
|
json?: boolean;
|
|
10
|
+
workspaceRoot?: string;
|
|
11
|
+
dbPath?: string;
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
export async function handleSyncCommand(options: SyncCommandOptions = {}): Promise<void> {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
+
const root = options.workspaceRoot || resolveWorkspaceRoot();
|
|
16
|
+
const config = ConfigLoader.load(options.dbPath, root);
|
|
17
|
+
const resolvedDbPath = options.dbPath
|
|
18
|
+
? (path.isAbsolute(options.dbPath) ? options.dbPath : path.join(root, options.dbPath))
|
|
19
|
+
: (path.isAbsolute(config.settings.db_path) ? config.settings.db_path : path.join(root, config.settings.db_path));
|
|
20
|
+
const db = new SeptumDatabase(resolvedDbPath);
|
|
21
|
+
try {
|
|
22
|
+
const repo = new SeptumRepository(db.raw);
|
|
23
|
+
const pipeline = new IngestionPipeline(repo, root);
|
|
15
24
|
|
|
16
|
-
|
|
25
|
+
const metrics = await pipeline.run(config);
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
if (options.json) {
|
|
28
|
+
console.log(JSON.stringify(metrics, null, 2));
|
|
29
|
+
} else {
|
|
30
|
+
console.log(
|
|
31
|
+
`[Septum Sync] Ingested in ${metrics.duration_ms.toFixed(0)}ms: ` +
|
|
32
|
+
`${metrics.files_updated} updated/added, ` +
|
|
33
|
+
`${metrics.files_skipped} unchanged, ` +
|
|
34
|
+
`${metrics.symbols_indexed} symbols, ` +
|
|
35
|
+
`${metrics.dependencies_indexed} deps.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
} finally {
|
|
39
|
+
db.close();
|
|
28
40
|
}
|
|
29
|
-
|
|
30
|
-
db.close();
|
|
31
41
|
}
|
|
@@ -1,62 +1,103 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
1
2
|
import { SeptumDatabase } from "../../core/database/client.ts";
|
|
2
3
|
import { SeptumRepository } from "../../core/database/repository.ts";
|
|
3
4
|
import { EnvironmentDetector } from "../../core/topology/environment-detector.ts";
|
|
4
5
|
import type { EnvironmentLayer, TopologyNode } from "../../core/topology/types.ts";
|
|
5
6
|
|
|
7
|
+
const VALID_LAYERS = new Set<EnvironmentLayer>([
|
|
8
|
+
"edge",
|
|
9
|
+
"gateway",
|
|
10
|
+
"host",
|
|
11
|
+
"runtime",
|
|
12
|
+
"storage",
|
|
13
|
+
"telemetry",
|
|
14
|
+
]);
|
|
15
|
+
|
|
6
16
|
export async function handleTopologyCommand(
|
|
7
17
|
subcommand?: string,
|
|
8
18
|
arg1?: string,
|
|
9
19
|
arg2?: string,
|
|
10
20
|
arg3?: string,
|
|
11
|
-
arg4?: string
|
|
21
|
+
arg4?: string,
|
|
22
|
+
workspaceRoot?: string
|
|
12
23
|
): Promise<void> {
|
|
13
|
-
const
|
|
14
|
-
const db = new SeptumDatabase();
|
|
15
|
-
const repo = new SeptumRepository(db.raw);
|
|
24
|
+
const normalizedSub = subcommand?.trim().toLowerCase();
|
|
16
25
|
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// Fix: Preserve existing constraints instead of overwriting with empty array
|
|
21
|
-
const existing = repo.environment.getLayer(layer);
|
|
22
|
-
repo.environment.saveLayer({
|
|
23
|
-
layer,
|
|
24
|
-
platform,
|
|
25
|
-
canonical_tag: EnvironmentDetector.matchCanonicalTag(platform),
|
|
26
|
-
status: "VERIFIED",
|
|
27
|
-
detected_from: ["CLI Manual Resolution"],
|
|
28
|
-
constraints: existing?.constraints && existing.constraints.length > 0 ? existing.constraints : [
|
|
29
|
-
`Configured manually as '${platform}'.`
|
|
30
|
-
],
|
|
31
|
-
});
|
|
32
|
-
console.log(`[Septum Topology] Layer '${layer}' successfully verified as '${platform}' in SQLite SSOT.\n`);
|
|
33
|
-
} else if (subcommand === "add-node" && arg1 && arg2 && arg3) {
|
|
34
|
-
const nodeId = arg1;
|
|
35
|
-
const layer = arg2.toLowerCase() as EnvironmentLayer;
|
|
36
|
-
const platform = arg3;
|
|
37
|
-
const domainOrIp = arg4;
|
|
26
|
+
if (normalizedSub && normalizedSub !== "resolve" && normalizedSub !== "add-node" && normalizedSub !== "remove-node" && normalizedSub !== "clear") {
|
|
27
|
+
throw new Error(`Unknown topology subcommand: '${subcommand}'. Valid subcommands: resolve, add-node, remove-node, clear.`);
|
|
28
|
+
}
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
layer
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} else if (
|
|
56
|
-
|
|
57
|
-
|
|
30
|
+
if (normalizedSub === "resolve") {
|
|
31
|
+
if (!arg1 || !arg2) {
|
|
32
|
+
throw new Error("Missing required arguments for 'topology resolve'. Usage: septum topology resolve <layer> <platform>");
|
|
33
|
+
}
|
|
34
|
+
const layerCandidate = arg1.trim().toLowerCase() as EnvironmentLayer;
|
|
35
|
+
if (!VALID_LAYERS.has(layerCandidate)) {
|
|
36
|
+
throw new Error(`Invalid environment layer '${arg1}'. Valid layers: ${Array.from(VALID_LAYERS).join(", ")}`);
|
|
37
|
+
}
|
|
38
|
+
} else if (normalizedSub === "add-node") {
|
|
39
|
+
if (!arg1 || !arg2 || !arg3) {
|
|
40
|
+
throw new Error("Missing required arguments for 'topology add-node'. Usage: septum topology add-node <id> <layer> <platform> [domain_or_ip]");
|
|
41
|
+
}
|
|
42
|
+
const layerCandidate = arg2.trim().toLowerCase() as EnvironmentLayer;
|
|
43
|
+
if (!VALID_LAYERS.has(layerCandidate)) {
|
|
44
|
+
throw new Error(`Invalid environment layer '${arg2}'. Valid layers: ${Array.from(VALID_LAYERS).join(", ")}`);
|
|
45
|
+
}
|
|
46
|
+
} else if (normalizedSub === "remove-node") {
|
|
47
|
+
if (!arg1) {
|
|
48
|
+
throw new Error("Missing required argument for 'topology remove-node'. Usage: septum topology remove-node <id>");
|
|
49
|
+
}
|
|
58
50
|
}
|
|
59
51
|
|
|
52
|
+
const cwd = workspaceRoot || process.cwd();
|
|
53
|
+
const dbPath = workspaceRoot ? join(workspaceRoot, ".septum/septum.db") : undefined;
|
|
54
|
+
const db = new SeptumDatabase(dbPath);
|
|
55
|
+
const repo = new SeptumRepository(db.raw);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
if (normalizedSub === "resolve" && arg1 && arg2) {
|
|
59
|
+
const layer = arg1.trim().toLowerCase() as EnvironmentLayer;
|
|
60
|
+
const platform = arg2;
|
|
61
|
+
// Fix: Preserve existing constraints instead of overwriting with empty array
|
|
62
|
+
const existing = repo.environment.getLayer(layer);
|
|
63
|
+
repo.environment.saveLayer({
|
|
64
|
+
layer,
|
|
65
|
+
platform,
|
|
66
|
+
canonical_tag: EnvironmentDetector.matchCanonicalTag(platform),
|
|
67
|
+
status: "VERIFIED",
|
|
68
|
+
detected_from: ["CLI Manual Resolution"],
|
|
69
|
+
constraints: existing?.constraints && existing.constraints.length > 0 ? existing.constraints : [
|
|
70
|
+
`Configured manually as '${platform}'.`
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
console.log(`[Septum Topology] Layer '${layer}' successfully verified as '${platform}' in SQLite SSOT.\n`);
|
|
74
|
+
} else if (normalizedSub === "add-node" && arg1 && arg2 && arg3) {
|
|
75
|
+
const nodeId = arg1;
|
|
76
|
+
const layer = arg2.trim().toLowerCase() as EnvironmentLayer;
|
|
77
|
+
const platform = arg3;
|
|
78
|
+
const domainOrIp = arg4;
|
|
79
|
+
|
|
80
|
+
const node: TopologyNode = {
|
|
81
|
+
id: nodeId,
|
|
82
|
+
layer,
|
|
83
|
+
name: nodeId,
|
|
84
|
+
platform,
|
|
85
|
+
canonical_tag: EnvironmentDetector.matchCanonicalTag(platform),
|
|
86
|
+
domain_or_ip: domainOrIp,
|
|
87
|
+
status: "VERIFIED",
|
|
88
|
+
detected_from: ["CLI Manual Node Resolution"],
|
|
89
|
+
constraints: [`Node added manually with platform '${platform}'.`],
|
|
90
|
+
};
|
|
91
|
+
repo.environment.saveNode(node);
|
|
92
|
+
console.log(`[Septum Topology] Node '${nodeId}' (${layer.toUpperCase()}) added to distributed topology graph.\n`);
|
|
93
|
+
} else if (normalizedSub === "remove-node" && arg1) {
|
|
94
|
+
repo.environment.deleteNode(arg1);
|
|
95
|
+
console.log(`[Septum Topology] Node '${arg1}' removed from distributed topology graph.\n`);
|
|
96
|
+
} else if (normalizedSub === "clear") {
|
|
97
|
+
repo.environment.clearAll();
|
|
98
|
+
console.log("[Septum Topology] Cleared environment topology and nodes in SQLite SSOT.\n");
|
|
99
|
+
}
|
|
100
|
+
|
|
60
101
|
const topology = EnvironmentDetector.detect(cwd, repo);
|
|
61
102
|
|
|
62
103
|
console.log("================================================================================");
|
|
@@ -113,7 +154,8 @@ export async function handleTopologyCommand(
|
|
|
113
154
|
console.log(" septum topology add-node host:vps-worker host \"Ubuntu 24.04 (Dedicated Worker)\" 10.0.0.6");
|
|
114
155
|
}
|
|
115
156
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
157
|
+
console.log("================================================================================\n");
|
|
158
|
+
} finally {
|
|
159
|
+
db.close();
|
|
160
|
+
}
|
|
119
161
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -3,13 +3,9 @@ import { SessionManager } from "../core/session/session-manager.ts";
|
|
|
3
3
|
import { SEPTUM_VERSION } from "../version.ts";
|
|
4
4
|
import { handleCheckCommand } from "./commands/check.ts";
|
|
5
5
|
import { handleHookCommand } from "./commands/hook.ts";
|
|
6
|
-
import { handleIngestCommand } from "./commands/ingest.ts";
|
|
7
6
|
import { handleInitCommand } from "./commands/init.ts";
|
|
8
7
|
import { handleTopologyCommand } from "./commands/topology.ts";
|
|
9
|
-
import { handleLocateCommand } from "./commands/locate.ts";
|
|
10
|
-
import { handleQueryCommand } from "./commands/query.ts";
|
|
11
8
|
import { handleServeCommand } from "./commands/serve.ts";
|
|
12
|
-
import { handleSliceCommand } from "./commands/slice.ts";
|
|
13
9
|
import { handleSyncCommand } from "./commands/sync.ts";
|
|
14
10
|
|
|
15
11
|
export async function runCLI(argv: string[]): Promise<void> {
|
|
@@ -22,13 +18,8 @@ export async function runCLI(argv: string[]): Promise<void> {
|
|
|
22
18
|
version: { type: "boolean", short: "v" },
|
|
23
19
|
force: { type: "boolean", short: "f" },
|
|
24
20
|
json: { type: "boolean" },
|
|
25
|
-
full: { type: "boolean" },
|
|
26
|
-
file: { type: "string" },
|
|
27
21
|
strict: { type: "boolean" },
|
|
28
22
|
staged: { type: "boolean" },
|
|
29
|
-
domain: { type: "string", short: "d" },
|
|
30
|
-
"max-depth": { type: "string" },
|
|
31
|
-
archetype: { type: "string", short: "a" },
|
|
32
23
|
feature: { type: "string" },
|
|
33
24
|
},
|
|
34
25
|
allowPositionals: true,
|
|
@@ -61,55 +52,12 @@ export async function runCLI(argv: string[]): Promise<void> {
|
|
|
61
52
|
break;
|
|
62
53
|
}
|
|
63
54
|
|
|
64
|
-
case "ingest": {
|
|
65
|
-
await handleIngestCommand();
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
55
|
case "sync": {
|
|
70
56
|
const json = Boolean(flags.json);
|
|
71
57
|
await handleSyncCommand({ json });
|
|
72
58
|
break;
|
|
73
59
|
}
|
|
74
60
|
|
|
75
|
-
case "query": {
|
|
76
|
-
const domainName = positionals[1];
|
|
77
|
-
const archetype = typeof flags.archetype === "string" ? flags.archetype : undefined;
|
|
78
|
-
const jsonOutput = Boolean(flags.json);
|
|
79
|
-
const full = Boolean(flags.full);
|
|
80
|
-
const file = typeof flags.file === "string" ? flags.file : undefined;
|
|
81
|
-
await handleQueryCommand(domainName, archetype, { json: jsonOutput, full, file });
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
case "locate": {
|
|
86
|
-
const query = positionals[1];
|
|
87
|
-
if (!query) {
|
|
88
|
-
console.error("Usage: septum locate <symbol-or-error> [--domain <domain>] [--json]");
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
const domain = typeof flags.domain === "string" ? flags.domain : undefined;
|
|
92
|
-
const json = Boolean(flags.json);
|
|
93
|
-
await handleLocateCommand(query, { domain, json });
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
case "slice": {
|
|
98
|
-
const query = positionals[1];
|
|
99
|
-
if (!query) {
|
|
100
|
-
console.error("Usage: septum slice <route-or-intent> [-d|--max-depth <number>] [--json]");
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
103
|
-
const json = Boolean(flags.json);
|
|
104
|
-
const rawDepth = flags["max-depth"] ?? flags.domain;
|
|
105
|
-
const parsedDepth = rawDepth !== undefined ? Number(rawDepth) : 5;
|
|
106
|
-
const maxDepth = !Number.isNaN(parsedDepth)
|
|
107
|
-
? Math.min(Math.max(Math.trunc(parsedDepth), 1), 8)
|
|
108
|
-
: 5;
|
|
109
|
-
await handleSliceCommand(query, { json, maxDepth });
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
61
|
case "check": {
|
|
114
62
|
const strict = Boolean(flags.strict);
|
|
115
63
|
const staged = Boolean(flags.staged);
|
|
@@ -132,7 +80,7 @@ export async function runCLI(argv: string[]): Promise<void> {
|
|
|
132
80
|
case "feature": {
|
|
133
81
|
const sub = positionals[1];
|
|
134
82
|
if (sub === "clear") {
|
|
135
|
-
const cleared = SessionManager.clearActiveSession(process.cwd());
|
|
83
|
+
const cleared = SessionManager.clearActiveSession(process.cwd(), undefined, Boolean(flags.force));
|
|
136
84
|
if (cleared) {
|
|
137
85
|
console.log("✓ Active feature session successfully cleared.");
|
|
138
86
|
} else {
|
|
@@ -184,29 +132,21 @@ USAGE:
|
|
|
184
132
|
septum [flags]
|
|
185
133
|
|
|
186
134
|
COMMANDS:
|
|
187
|
-
|
|
135
|
+
Core Lifecycle & Indexing:
|
|
188
136
|
init Initialize configuration, gitignore, pre-commit hook, and catalog
|
|
189
|
-
Flags: -f, --force
|
|
190
|
-
|
|
191
|
-
sync Incremental fast synchronization of modified files
|
|
137
|
+
Flags: -f, --force (regenerate Septum-owned hooks & harness artifacts; preserves DB, domain policies, and user hooks)
|
|
138
|
+
sync Codebase AST synchronization and catalog update (full or incremental)
|
|
192
139
|
Flags: --json
|
|
193
140
|
|
|
194
|
-
Structural Discovery:
|
|
195
|
-
query [domain] Query structural catalog for a domain or whole project
|
|
196
|
-
Flags: -a, --archetype <name>, --full, --file <filter>, --json
|
|
197
|
-
locate <query> Locate symbol, class, interface, method, or error source
|
|
198
|
-
Flags: -d, --domain <name>, --json
|
|
199
|
-
slice <route|intent> Trace end-to-end vertical execution slice for route/intent
|
|
200
|
-
Flags: -d, --max-depth <number>, --json
|
|
201
|
-
|
|
202
141
|
Boundary & Enforcement:
|
|
203
142
|
check Audit codebase for cross-domain boundary violations
|
|
204
|
-
Flags: --strict, --staged, --feature <name
|
|
143
|
+
Flags: --strict, --staged, --feature <name>, --json
|
|
205
144
|
feature [status|clear] View or release active feature context session lock
|
|
145
|
+
Flags: --force (explicit recovery of another owner's session)
|
|
206
146
|
hook [subhook] Internal pre-tool guardrail hook (e.g. pre-write)
|
|
207
147
|
|
|
208
148
|
Environment & Topology:
|
|
209
|
-
topology [action]
|
|
149
|
+
topology, env [action] Inspect 6-layer environment topology & distributed nodes
|
|
210
150
|
Subcommands: resolve <layer> <platform>, add-node, remove-node, clear
|
|
211
151
|
|
|
212
152
|
Daemon & Protocol:
|
|
@@ -215,6 +155,5 @@ COMMANDS:
|
|
|
215
155
|
GLOBAL FLAGS:
|
|
216
156
|
-h, --help Show this help message
|
|
217
157
|
-v, --version Show current version
|
|
218
|
-
--json Output formatted JSON metrics / results
|
|
219
158
|
`);
|
|
220
159
|
}
|