@shrkcrft/cli 0.1.0-alpha.1 → 0.1.0-alpha.10
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 +1 -1
- package/dist/commands/api-diff.command.d.ts +11 -0
- package/dist/commands/api-diff.command.d.ts.map +1 -0
- package/dist/commands/api-diff.command.js +116 -0
- package/dist/commands/arch.command.d.ts +9 -0
- package/dist/commands/arch.command.d.ts.map +1 -0
- package/dist/commands/arch.command.js +186 -0
- package/dist/commands/boundaries.command.d.ts.map +1 -1
- package/dist/commands/boundaries.command.js +0 -12
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +20 -30
- package/dist/commands/code-intel.command.d.ts +18 -0
- package/dist/commands/code-intel.command.d.ts.map +1 -0
- package/dist/commands/code-intel.command.js +146 -0
- package/dist/commands/command-catalog.d.ts +7 -3
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +201 -47
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +4 -4
- package/dist/commands/completion.command.d.ts +10 -0
- package/dist/commands/completion.command.d.ts.map +1 -0
- package/dist/commands/completion.command.js +121 -0
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +5 -22
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +89 -0
- package/dist/commands/diff-check.command.d.ts +30 -0
- package/dist/commands/diff-check.command.d.ts.map +1 -0
- package/dist/commands/diff-check.command.js +210 -0
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +42 -9
- package/dist/commands/export.command.d.ts.map +1 -1
- package/dist/commands/export.command.js +76 -3
- package/dist/commands/framework.command.d.ts +12 -0
- package/dist/commands/framework.command.d.ts.map +1 -0
- package/dist/commands/framework.command.js +180 -0
- package/dist/commands/gate.command.d.ts +15 -0
- package/dist/commands/gate.command.d.ts.map +1 -0
- package/dist/commands/gate.command.js +296 -0
- package/dist/commands/graph-code-subverbs.d.ts +11 -0
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -0
- package/dist/commands/graph-code-subverbs.js +818 -0
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +22 -0
- package/dist/commands/help.command.d.ts +4 -3
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +77 -21
- package/dist/commands/helper.command.js +1 -1
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +170 -1
- package/dist/commands/import.command.d.ts.map +1 -1
- package/dist/commands/import.command.js +121 -5
- package/dist/commands/init.command.d.ts.map +1 -1
- package/dist/commands/init.command.js +184 -16
- package/dist/commands/mcp.command.d.ts.map +1 -1
- package/dist/commands/mcp.command.js +2 -131
- package/dist/commands/migrate.command.d.ts +13 -0
- package/dist/commands/migrate.command.d.ts.map +1 -0
- package/dist/commands/migrate.command.js +152 -0
- package/dist/commands/onboard.command.d.ts.map +1 -1
- package/dist/commands/onboard.command.js +3 -15
- package/dist/commands/packs-new.d.ts +1 -1
- package/dist/commands/packs-new.d.ts.map +1 -1
- package/dist/commands/packs-new.js +5 -36
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +3 -17
- package/dist/commands/plan-context.command.d.ts +11 -0
- package/dist/commands/plan-context.command.d.ts.map +1 -0
- package/dist/commands/plan-context.command.js +77 -0
- package/dist/commands/profiles.command.js +4 -4
- package/dist/commands/release.command.js +13 -13
- package/dist/commands/review.command.d.ts.map +1 -1
- package/dist/commands/review.command.js +2 -28
- package/dist/commands/rule-graph-subverbs.d.ts +3 -0
- package/dist/commands/rule-graph-subverbs.d.ts.map +1 -0
- package/dist/commands/rule-graph-subverbs.js +132 -0
- package/dist/commands/search-structural.command.d.ts +18 -0
- package/dist/commands/search-structural.command.d.ts.map +1 -0
- package/dist/commands/search-structural.command.js +376 -0
- package/dist/commands/search.command.js +1 -1
- package/dist/commands/task-context.command.js +0 -16
- package/dist/commands/task.command.d.ts.map +1 -1
- package/dist/commands/task.command.js +8 -2
- package/dist/dashboard/code-intelligence-data.d.ts +33 -0
- package/dist/dashboard/code-intelligence-data.d.ts.map +1 -0
- package/dist/dashboard/code-intelligence-data.js +307 -0
- package/dist/dashboard/dashboard-api-server.d.ts.map +1 -1
- package/dist/dashboard/dashboard-api-server.js +137 -1
- package/dist/export/claude-commands-export.d.ts +60 -0
- package/dist/export/claude-commands-export.d.ts.map +1 -0
- package/dist/export/claude-commands-export.js +276 -0
- package/dist/export/export-formats.d.ts +1 -1
- package/dist/export/export-formats.d.ts.map +1 -1
- package/dist/export/export-formats.js +139 -12
- package/dist/init/init-templates.d.ts.map +1 -1
- package/dist/init/init-templates.js +133 -113
- package/dist/init/paths-advisory.d.ts +20 -0
- package/dist/init/paths-advisory.d.ts.map +1 -0
- package/dist/init/paths-advisory.js +88 -0
- package/dist/main.d.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +137 -46
- package/dist/output/failure-hints.d.ts +1 -9
- package/dist/output/failure-hints.d.ts.map +1 -1
- package/dist/output/failure-hints.js +2 -8
- package/dist/output/watch-loop.d.ts +9 -1
- package/dist/output/watch-loop.d.ts.map +1 -1
- package/dist/output/watch-loop.js +13 -3
- package/dist/schemas/json-schemas.d.ts +36 -36
- package/dist/schemas/json-schemas.js +36 -36
- package/dist/surface/about.d.ts.map +1 -1
- package/dist/surface/about.js +37 -15
- package/dist/surface/no-args-landing.d.ts.map +1 -1
- package/dist/surface/no-args-landing.js +9 -13
- package/dist/surface/surface-config-writer.d.ts.map +1 -1
- package/dist/surface/surface-config-writer.js +23 -11
- package/package.json +36 -25
- package/dist/commands/plugin.command.d.ts +0 -11
- package/dist/commands/plugin.command.d.ts.map +0 -1
- package/dist/commands/plugin.command.js +0 -394
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { buildRegistryWithPacks, defaultRegistry, FrameworkQueryApi, runExtractors, } from '@shrkcrft/framework-scanners';
|
|
2
|
+
import { inspectSharkcraft } from '@shrkcrft/inspector';
|
|
3
|
+
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
4
|
+
import { asJson, header, kv } from "../output/format-output.js";
|
|
5
|
+
/**
|
|
6
|
+
* `shrk framework` — run / inspect the framework-aware extractors.
|
|
7
|
+
*
|
|
8
|
+
* Sub-verbs:
|
|
9
|
+
* - shrk framework index run extractors over the project
|
|
10
|
+
* - shrk framework status report store health
|
|
11
|
+
* - shrk framework list [filters] list entities (--framework, --subtype, --file)
|
|
12
|
+
* - shrk framework routes NestJS route table (method, path, handler, file)
|
|
13
|
+
*/
|
|
14
|
+
export const frameworkCommand = {
|
|
15
|
+
name: 'framework',
|
|
16
|
+
description: 'Framework-aware extractors: NestJS (controllers/modules/providers/routes), React (components/hook usages). Output: shrk framework list / routes / status / index.',
|
|
17
|
+
usage: 'shrk framework index [--only nestjs,react] [--json] | shrk framework status [--json] | shrk framework list [--framework <name>] [--subtype <s>] [--file <path>] [--limit N] [--json] | shrk framework routes [--json]',
|
|
18
|
+
async run(args) {
|
|
19
|
+
const sub = args.positional[0];
|
|
20
|
+
if (sub === 'index')
|
|
21
|
+
return runIndex(args);
|
|
22
|
+
if (sub === 'status')
|
|
23
|
+
return runStatus(args);
|
|
24
|
+
if (sub === 'list')
|
|
25
|
+
return runList(args);
|
|
26
|
+
if (sub === 'routes')
|
|
27
|
+
return runRoutes(args);
|
|
28
|
+
process.stderr.write(this.usage + '\n');
|
|
29
|
+
return 2;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
async function runIndex(args) {
|
|
33
|
+
const cwd = resolveCwd(args);
|
|
34
|
+
const wantJson = flagBool(args, 'json');
|
|
35
|
+
const only = flagString(args, 'only');
|
|
36
|
+
const noPacks = flagBool(args, 'no-packs');
|
|
37
|
+
try {
|
|
38
|
+
// Build the registry: built-ins + optional pack-contributed extractors.
|
|
39
|
+
const registry = defaultRegistry();
|
|
40
|
+
let packPackages = [];
|
|
41
|
+
let packDiagnostics = [];
|
|
42
|
+
if (!noPacks) {
|
|
43
|
+
try {
|
|
44
|
+
const inspection = await inspectSharkcraft({ cwd });
|
|
45
|
+
const merged = await buildRegistryWithPacks(registry, inspection.packs);
|
|
46
|
+
packPackages = merged.packs;
|
|
47
|
+
packDiagnostics = merged.diagnostics;
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
// Pack loading is best-effort. Surface the error as a diagnostic
|
|
51
|
+
// but don't block the index — built-in extractors still run.
|
|
52
|
+
packDiagnostics = [`pack discovery failed: ${e.message}`];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const r = runExtractors({
|
|
56
|
+
projectRoot: cwd,
|
|
57
|
+
registry,
|
|
58
|
+
...(only ? { only: only.split(',').map((s) => s.trim()).filter(Boolean) } : {}),
|
|
59
|
+
});
|
|
60
|
+
const diagnostics = [...packDiagnostics, ...r.diagnostics];
|
|
61
|
+
if (wantJson) {
|
|
62
|
+
process.stdout.write(asJson({
|
|
63
|
+
ok: true,
|
|
64
|
+
manifest: r.manifest,
|
|
65
|
+
filesScanned: r.filesScanned,
|
|
66
|
+
durationMs: r.durationMs,
|
|
67
|
+
diagnostics,
|
|
68
|
+
packExtractors: packPackages,
|
|
69
|
+
}) + '\n');
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
process.stdout.write(header('Framework index'));
|
|
73
|
+
process.stdout.write(kv('schema', r.manifest.schema) + '\n');
|
|
74
|
+
process.stdout.write(kv('frameworks', r.manifest.frameworks.join(', ')) + '\n');
|
|
75
|
+
process.stdout.write(kv('files scanned', String(r.filesScanned)) + '\n');
|
|
76
|
+
for (const [framework, count] of Object.entries(r.manifest.countsByFramework)) {
|
|
77
|
+
process.stdout.write(kv(`entities (${framework})`, String(count)) + '\n');
|
|
78
|
+
}
|
|
79
|
+
if (packPackages.length > 0) {
|
|
80
|
+
process.stdout.write(kv('pack extractors', packPackages.join(', ')) + '\n');
|
|
81
|
+
}
|
|
82
|
+
process.stdout.write(kv('duration', `${r.durationMs}ms`) + '\n');
|
|
83
|
+
for (const d of diagnostics.slice(0, 5))
|
|
84
|
+
process.stdout.write(`! ${d}\n`);
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
const msg = e.message;
|
|
89
|
+
if (wantJson) {
|
|
90
|
+
process.stdout.write(asJson({ ok: false, error: msg, nextCommand: 'shrk graph index' }) + '\n');
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
process.stderr.write(msg + '\n');
|
|
94
|
+
}
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function runStatus(args) {
|
|
99
|
+
const cwd = resolveCwd(args);
|
|
100
|
+
const wantJson = flagBool(args, 'json');
|
|
101
|
+
const missing = FrameworkQueryApi.missingDescription(cwd);
|
|
102
|
+
if (missing) {
|
|
103
|
+
if (wantJson) {
|
|
104
|
+
process.stdout.write(asJson({ ok: false, state: 'missing', message: missing, nextCommand: 'shrk framework index' }) + '\n');
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
process.stderr.write(missing + '\n');
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
const api = FrameworkQueryApi.fromStore(cwd);
|
|
111
|
+
const m = api.manifest();
|
|
112
|
+
if (wantJson) {
|
|
113
|
+
process.stdout.write(asJson({ ok: true, manifest: m }) + '\n');
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
process.stdout.write(header('Framework status'));
|
|
117
|
+
process.stdout.write(kv('schema', m.schema) + '\n');
|
|
118
|
+
process.stdout.write(kv('frameworks', m.frameworks.join(', ')) + '\n');
|
|
119
|
+
for (const [k, v] of Object.entries(m.countsBySubtype)) {
|
|
120
|
+
process.stdout.write(kv(` ${k}`, String(v)) + '\n');
|
|
121
|
+
}
|
|
122
|
+
process.stdout.write(kv('last built', m.lastBuiltAt) + '\n');
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
async function runList(args) {
|
|
126
|
+
const cwd = resolveCwd(args);
|
|
127
|
+
const wantJson = flagBool(args, 'json');
|
|
128
|
+
const missing = FrameworkQueryApi.missingDescription(cwd);
|
|
129
|
+
if (missing) {
|
|
130
|
+
process.stderr.write(missing + '\n');
|
|
131
|
+
return 1;
|
|
132
|
+
}
|
|
133
|
+
const api = FrameworkQueryApi.fromStore(cwd);
|
|
134
|
+
const framework = flagString(args, 'framework');
|
|
135
|
+
const subtype = flagString(args, 'subtype');
|
|
136
|
+
const file = flagString(args, 'file');
|
|
137
|
+
const limit = Number(flagString(args, 'limit') ?? '50');
|
|
138
|
+
const entities = api.list({
|
|
139
|
+
...(framework ? { framework } : {}),
|
|
140
|
+
...(subtype ? { subtype } : {}),
|
|
141
|
+
...(file ? { file } : {}),
|
|
142
|
+
limit,
|
|
143
|
+
});
|
|
144
|
+
if (wantJson) {
|
|
145
|
+
process.stdout.write(asJson({
|
|
146
|
+
schema: 'sharkcraft.framework-list/v1',
|
|
147
|
+
filters: { framework, subtype, file },
|
|
148
|
+
total: entities.length,
|
|
149
|
+
entities: entities.map((n) => ({ id: n.id, label: n.label, path: n.path, data: n.data })),
|
|
150
|
+
}) + '\n');
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
process.stdout.write(header('Framework entities'));
|
|
154
|
+
process.stdout.write(kv('total', String(entities.length)) + '\n');
|
|
155
|
+
for (const n of entities) {
|
|
156
|
+
process.stdout.write(` ${n.data?.['framework'] ?? '?'}:${n.data?.['subtype'] ?? '?'} ${n.label} ${n.path ?? ''}\n`);
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
async function runRoutes(args) {
|
|
161
|
+
const cwd = resolveCwd(args);
|
|
162
|
+
const wantJson = flagBool(args, 'json');
|
|
163
|
+
const missing = FrameworkQueryApi.missingDescription(cwd);
|
|
164
|
+
if (missing) {
|
|
165
|
+
process.stderr.write(missing + '\n');
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
const api = FrameworkQueryApi.fromStore(cwd);
|
|
169
|
+
const routes = api.routes();
|
|
170
|
+
if (wantJson) {
|
|
171
|
+
process.stdout.write(asJson({ schema: 'sharkcraft.framework-routes/v1', total: routes.length, routes }) + '\n');
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
process.stdout.write(header('NestJS routes'));
|
|
175
|
+
process.stdout.write(kv('total', String(routes.length)) + '\n');
|
|
176
|
+
for (const r of routes.slice(0, 100)) {
|
|
177
|
+
process.stdout.write(` ${r.method.padEnd(6)} ${r.path.padEnd(36)} → ${r.handler} (${r.file})\n`);
|
|
178
|
+
}
|
|
179
|
+
return 0;
|
|
180
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ICommandHandler } from '../command-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* `shrk gate` — run all code-intelligence quality gates and emit one
|
|
4
|
+
* pass/fail report. Designed as the single command CI should call
|
|
5
|
+
* before merge.
|
|
6
|
+
*
|
|
7
|
+
* Exit codes:
|
|
8
|
+
* - 0 if overall status is `pass` (no failures, no warnings)
|
|
9
|
+
* - 0 if overall is `warn` (default — opt-in to fail via --strict)
|
|
10
|
+
* - 1 if overall is `fail`
|
|
11
|
+
*
|
|
12
|
+
* Pass `--strict` to treat `warn` as failure.
|
|
13
|
+
*/
|
|
14
|
+
export declare const gateCommand: ICommandHandler;
|
|
15
|
+
//# sourceMappingURL=gate.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.command.d.ts","sourceRoot":"","sources":["../../src/commands/gate.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,eA2FzB,CAAC"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { QualityGateReportStore, renderGateReportMarkdown, runQualityGates, } from '@shrkcrft/quality-gates';
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import * as nodePath from 'node:path';
|
|
4
|
+
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
5
|
+
import { asJson, header, kv } from "../output/format-output.js";
|
|
6
|
+
/**
|
|
7
|
+
* `shrk gate` — run all code-intelligence quality gates and emit one
|
|
8
|
+
* pass/fail report. Designed as the single command CI should call
|
|
9
|
+
* before merge.
|
|
10
|
+
*
|
|
11
|
+
* Exit codes:
|
|
12
|
+
* - 0 if overall status is `pass` (no failures, no warnings)
|
|
13
|
+
* - 0 if overall is `warn` (default — opt-in to fail via --strict)
|
|
14
|
+
* - 1 if overall is `fail`
|
|
15
|
+
*
|
|
16
|
+
* Pass `--strict` to treat `warn` as failure.
|
|
17
|
+
*/
|
|
18
|
+
export const gateCommand = {
|
|
19
|
+
name: 'gate',
|
|
20
|
+
description: 'Aggregator: runs the code-intelligence quality gates (graph freshness, architecture, impact-since-ref) and reports a single pass/fail.',
|
|
21
|
+
usage: 'shrk gate [--since <gitref>] [--fail-on critical,high] [--disable arch,impact,api-diff] [--api-baseline <path>] [--no-fail-on-breaking] [--strict] [--no-persist] [--json] [--markdown] [--output <path>]\n shrk gate scaffold-ci [--provider github|generic] [--force] [--json]\n shrk gate scaffold-hook [--provider husky|raw] [--force] [--json]',
|
|
22
|
+
async run(args) {
|
|
23
|
+
if (args.positional[0] === 'scaffold-ci') {
|
|
24
|
+
const sliced = { ...args, positional: args.positional.slice(1) };
|
|
25
|
+
return runGateScaffoldCi(sliced);
|
|
26
|
+
}
|
|
27
|
+
if (args.positional[0] === 'scaffold-hook') {
|
|
28
|
+
const sliced = { ...args, positional: args.positional.slice(1) };
|
|
29
|
+
return runGateScaffoldHook(sliced);
|
|
30
|
+
}
|
|
31
|
+
const cwd = resolveCwd(args);
|
|
32
|
+
const wantJson = flagBool(args, 'json');
|
|
33
|
+
const wantMarkdown = flagBool(args, 'markdown');
|
|
34
|
+
const outputPath = flagString(args, 'output');
|
|
35
|
+
const strict = flagBool(args, 'strict');
|
|
36
|
+
const sinceRef = flagString(args, 'since');
|
|
37
|
+
const failOnRaw = flagString(args, 'fail-on');
|
|
38
|
+
const disableRaw = flagString(args, 'disable');
|
|
39
|
+
const apiBaseline = flagString(args, 'api-baseline');
|
|
40
|
+
const noFailOnBreaking = flagBool(args, 'no-fail-on-breaking');
|
|
41
|
+
const failOn = failOnRaw
|
|
42
|
+
? failOnRaw.split(',').map((s) => s.trim()).filter(Boolean)
|
|
43
|
+
: undefined;
|
|
44
|
+
const disable = disableRaw ? disableRaw.split(',').map((s) => s.trim()).filter(Boolean) : undefined;
|
|
45
|
+
const report = runQualityGates({
|
|
46
|
+
projectRoot: cwd,
|
|
47
|
+
impact: {
|
|
48
|
+
...(sinceRef ? { sinceRef } : {}),
|
|
49
|
+
...(failOn ? { failOn } : {}),
|
|
50
|
+
},
|
|
51
|
+
...(apiBaseline
|
|
52
|
+
? {
|
|
53
|
+
apiDiff: {
|
|
54
|
+
baselinePath: apiBaseline,
|
|
55
|
+
failOnBreaking: !noFailOnBreaking,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
: {}),
|
|
59
|
+
...(disable ? { disable } : {}),
|
|
60
|
+
});
|
|
61
|
+
// Persist the report so dashboards and follow-up tooling can read
|
|
62
|
+
// it without re-running every gate. Opt out with `--no-persist`.
|
|
63
|
+
if (!flagBool(args, 'no-persist')) {
|
|
64
|
+
try {
|
|
65
|
+
new QualityGateReportStore(cwd).write(report);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Persistence is best-effort; never fail the gate on a
|
|
69
|
+
// disk-write error.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (wantMarkdown) {
|
|
73
|
+
const md = renderGateReportMarkdown(report);
|
|
74
|
+
if (outputPath) {
|
|
75
|
+
const abs = nodePath.isAbsolute(outputPath)
|
|
76
|
+
? outputPath
|
|
77
|
+
: nodePath.resolve(cwd, outputPath);
|
|
78
|
+
writeFileSync(abs, md, 'utf8');
|
|
79
|
+
process.stdout.write(`Markdown report written → ${abs}\n`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
process.stdout.write(md);
|
|
83
|
+
}
|
|
84
|
+
return exitCode(report.overall, strict);
|
|
85
|
+
}
|
|
86
|
+
if (wantJson) {
|
|
87
|
+
process.stdout.write(asJson(report) + '\n');
|
|
88
|
+
return exitCode(report.overall, strict);
|
|
89
|
+
}
|
|
90
|
+
process.stdout.write(header(`Quality gates: ${report.overall.toUpperCase()}`));
|
|
91
|
+
process.stdout.write(kv('total duration', `${report.totalDurationMs}ms`) + '\n');
|
|
92
|
+
process.stdout.write(kv('summary', `pass=${report.counts.pass} warn=${report.counts.warn} fail=${report.counts.fail} skipped=${report.counts.skipped}`) + '\n');
|
|
93
|
+
process.stdout.write('\nGates:\n');
|
|
94
|
+
for (const g of report.gates) {
|
|
95
|
+
const status = g.status.padEnd(8);
|
|
96
|
+
process.stdout.write(` [${status}] ${g.label} (${g.durationMs}ms)\n`);
|
|
97
|
+
process.stdout.write(` ${g.message}\n`);
|
|
98
|
+
if (g.nextCommands && g.nextCommands.length > 0) {
|
|
99
|
+
for (const c of g.nextCommands)
|
|
100
|
+
process.stdout.write(` → ${c}\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return exitCode(report.overall, strict);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
function exitCode(overall, strict) {
|
|
107
|
+
if (overall === 'fail')
|
|
108
|
+
return 1;
|
|
109
|
+
if (overall === 'warn' && strict)
|
|
110
|
+
return 1;
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
const GITHUB_WORKFLOW = `name: shrk gate
|
|
114
|
+
|
|
115
|
+
on:
|
|
116
|
+
pull_request:
|
|
117
|
+
push:
|
|
118
|
+
branches: [main]
|
|
119
|
+
|
|
120
|
+
jobs:
|
|
121
|
+
gate:
|
|
122
|
+
runs-on: ubuntu-latest
|
|
123
|
+
timeout-minutes: 10
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/checkout@v4
|
|
126
|
+
with:
|
|
127
|
+
fetch-depth: 0 # full history so 'shrk impact --since main' works
|
|
128
|
+
|
|
129
|
+
- name: Setup bun
|
|
130
|
+
uses: oven-sh/setup-bun@v2
|
|
131
|
+
|
|
132
|
+
- name: Install dependencies
|
|
133
|
+
run: bun install --frozen-lockfile
|
|
134
|
+
|
|
135
|
+
- name: Index code-intelligence graph
|
|
136
|
+
run: bunx shrk graph index
|
|
137
|
+
|
|
138
|
+
- name: Run shrk gate
|
|
139
|
+
run: bunx shrk gate --since origin/main --strict --markdown --output gate-report.md
|
|
140
|
+
|
|
141
|
+
- name: Upload gate report
|
|
142
|
+
if: always()
|
|
143
|
+
uses: actions/upload-artifact@v4
|
|
144
|
+
with:
|
|
145
|
+
name: shrk-gate-report
|
|
146
|
+
path: |
|
|
147
|
+
gate-report.md
|
|
148
|
+
.sharkcraft/quality-gates/last.json
|
|
149
|
+
|
|
150
|
+
- name: Comment on PR
|
|
151
|
+
if: github.event_name == 'pull_request' && always()
|
|
152
|
+
uses: marocchino/sticky-pull-request-comment@v2
|
|
153
|
+
with:
|
|
154
|
+
header: shrk-gate
|
|
155
|
+
path: gate-report.md
|
|
156
|
+
`;
|
|
157
|
+
const HUSKY_PRE_COMMIT = `#!/usr/bin/env sh
|
|
158
|
+
# shrk gate pre-commit hook (husky-compatible).
|
|
159
|
+
#
|
|
160
|
+
# Re-indexes the code graph from staged files only, then runs the
|
|
161
|
+
# default gate set against the change. Add or remove --disable flags
|
|
162
|
+
# to narrow the gate set if a particular check is too slow at this
|
|
163
|
+
# point in the loop.
|
|
164
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
165
|
+
|
|
166
|
+
set -e
|
|
167
|
+
|
|
168
|
+
bunx shrk graph index --changed
|
|
169
|
+
bunx shrk gate --strict
|
|
170
|
+
`;
|
|
171
|
+
const RAW_PRE_COMMIT = `#!/usr/bin/env sh
|
|
172
|
+
# shrk gate pre-commit hook (raw .git/hooks variant).
|
|
173
|
+
#
|
|
174
|
+
# Symlink or copy this file into .git/hooks/pre-commit:
|
|
175
|
+
# ln -s ../../scripts/pre-commit .git/hooks/pre-commit
|
|
176
|
+
# (or just \`cp scripts/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit\`).
|
|
177
|
+
set -e
|
|
178
|
+
|
|
179
|
+
bunx shrk graph index --changed
|
|
180
|
+
bunx shrk gate --strict
|
|
181
|
+
`;
|
|
182
|
+
const GENERIC_SCRIPT = `#!/usr/bin/env bash
|
|
183
|
+
# Generic CI runner for shrk gate. Copy / adapt for your provider.
|
|
184
|
+
#
|
|
185
|
+
# Usage:
|
|
186
|
+
# ./scripts/shrk-gate.sh
|
|
187
|
+
#
|
|
188
|
+
# Exit codes:
|
|
189
|
+
# 0 — gate pass or warn (non-strict)
|
|
190
|
+
# 1 — gate fail (or warn under --strict)
|
|
191
|
+
set -euo pipefail
|
|
192
|
+
|
|
193
|
+
# Ensure the code-intelligence graph is fresh.
|
|
194
|
+
bunx shrk graph index
|
|
195
|
+
|
|
196
|
+
# Run the aggregator. --strict turns warn into fail; drop it if you
|
|
197
|
+
# prefer to surface warnings without blocking merge.
|
|
198
|
+
bunx shrk gate \\
|
|
199
|
+
--since "\${BASE_REF:-origin/main}" \\
|
|
200
|
+
--strict \\
|
|
201
|
+
--markdown \\
|
|
202
|
+
--output gate-report.md
|
|
203
|
+
`;
|
|
204
|
+
async function runGateScaffoldHook(args) {
|
|
205
|
+
const cwd = resolveCwd(args);
|
|
206
|
+
const wantJson = flagBool(args, 'json');
|
|
207
|
+
const provider = flagString(args, 'provider') ?? 'husky';
|
|
208
|
+
const force = flagBool(args, 'force');
|
|
209
|
+
let target;
|
|
210
|
+
let body;
|
|
211
|
+
switch (provider) {
|
|
212
|
+
case 'husky':
|
|
213
|
+
target = nodePath.join(cwd, '.husky', 'pre-commit');
|
|
214
|
+
body = HUSKY_PRE_COMMIT;
|
|
215
|
+
break;
|
|
216
|
+
case 'raw':
|
|
217
|
+
target = nodePath.join(cwd, 'scripts', 'pre-commit');
|
|
218
|
+
body = RAW_PRE_COMMIT;
|
|
219
|
+
break;
|
|
220
|
+
default:
|
|
221
|
+
process.stderr.write(`Unknown --provider "${provider}". Use husky | raw.\n`);
|
|
222
|
+
return 2;
|
|
223
|
+
}
|
|
224
|
+
if (existsSync(target) && !force) {
|
|
225
|
+
const msg = `${target} already exists. Use --force to overwrite.\n`;
|
|
226
|
+
if (wantJson) {
|
|
227
|
+
process.stdout.write(asJson({ ok: false, error: 'exists', path: target }) + '\n');
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
process.stderr.write(msg);
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
mkdirSync(nodePath.dirname(target), { recursive: true });
|
|
234
|
+
writeFileSync(target, body, 'utf8');
|
|
235
|
+
try {
|
|
236
|
+
chmodSync(target, 0o755);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// ignore — Windows / strict FS.
|
|
240
|
+
}
|
|
241
|
+
if (wantJson) {
|
|
242
|
+
process.stdout.write(asJson({ ok: true, provider, wrote: target, bytes: body.length }) + '\n');
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
process.stdout.write(`Scaffolded ${provider} pre-commit hook → ${target}\n`);
|
|
246
|
+
if (provider === 'raw') {
|
|
247
|
+
process.stdout.write(`Activate it with:\n ln -s ../../scripts/pre-commit .git/hooks/pre-commit\n`);
|
|
248
|
+
}
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
async function runGateScaffoldCi(args) {
|
|
252
|
+
const cwd = resolveCwd(args);
|
|
253
|
+
const wantJson = flagBool(args, 'json');
|
|
254
|
+
const provider = flagString(args, 'provider') ?? 'github';
|
|
255
|
+
const force = flagBool(args, 'force');
|
|
256
|
+
let target;
|
|
257
|
+
let body;
|
|
258
|
+
switch (provider) {
|
|
259
|
+
case 'github':
|
|
260
|
+
target = nodePath.join(cwd, '.github', 'workflows', 'shrk-gate.yml');
|
|
261
|
+
body = GITHUB_WORKFLOW;
|
|
262
|
+
break;
|
|
263
|
+
case 'generic':
|
|
264
|
+
target = nodePath.join(cwd, 'scripts', 'shrk-gate.sh');
|
|
265
|
+
body = GENERIC_SCRIPT;
|
|
266
|
+
break;
|
|
267
|
+
default:
|
|
268
|
+
process.stderr.write(`Unknown --provider "${provider}". Use github | generic.\n`);
|
|
269
|
+
return 2;
|
|
270
|
+
}
|
|
271
|
+
if (existsSync(target) && !force) {
|
|
272
|
+
const msg = `${target} already exists. Use --force to overwrite.\n`;
|
|
273
|
+
if (wantJson) {
|
|
274
|
+
process.stdout.write(asJson({ ok: false, error: 'exists', path: target }) + '\n');
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
process.stderr.write(msg);
|
|
278
|
+
return 1;
|
|
279
|
+
}
|
|
280
|
+
mkdirSync(nodePath.dirname(target), { recursive: true });
|
|
281
|
+
writeFileSync(target, body, 'utf8');
|
|
282
|
+
if (provider === 'generic') {
|
|
283
|
+
try {
|
|
284
|
+
chmodSync(target, 0o755);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// ignore — Windows or stricter FS.
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (wantJson) {
|
|
291
|
+
process.stdout.write(asJson({ ok: true, provider, wrote: target, bytes: body.length }) + '\n');
|
|
292
|
+
return 0;
|
|
293
|
+
}
|
|
294
|
+
process.stdout.write(`Scaffolded ${provider} CI runner → ${target}\n`);
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../command-registry.js';
|
|
2
|
+
export declare function runGraphIndex(args: ParsedArgs): Promise<number>;
|
|
3
|
+
export declare function runGraphCycles(args: ParsedArgs): Promise<number>;
|
|
4
|
+
export declare function runGraphUnresolved(args: ParsedArgs): Promise<number>;
|
|
5
|
+
export declare function runGraphDeps(args: ParsedArgs): Promise<number>;
|
|
6
|
+
export declare function runGraphStatus(args: ParsedArgs): Promise<number>;
|
|
7
|
+
export declare function runGraphSearch(args: ParsedArgs): Promise<number>;
|
|
8
|
+
export declare function runGraphContext(args: ParsedArgs): Promise<number>;
|
|
9
|
+
export declare function runGraphImpact(args: ParsedArgs): Promise<number>;
|
|
10
|
+
export declare function runGraphCallers(args: ParsedArgs): Promise<number>;
|
|
11
|
+
//# sourceMappingURL=graph-code-subverbs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-code-subverbs.d.ts","sourceRoot":"","sources":["../../src/commands/graph-code-subverbs.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAQ3F,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBrE;AA4FD,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+DtE;AAiBD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwF1E;AAID,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmEpE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+DtE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkCtE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA6HvE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwFtE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAuCvE"}
|