@shrkcrft/cli 0.1.0-alpha.22 → 0.1.0-alpha.24
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/dist/command-registry.d.ts +9 -0
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +15 -0
- package/dist/commands/ai-status.command.d.ts.map +1 -1
- package/dist/commands/ai-status.command.js +10 -0
- package/dist/commands/apply.command.d.ts.map +1 -1
- package/dist/commands/apply.command.js +9 -2
- package/dist/commands/ask.command.d.ts.map +1 -1
- package/dist/commands/ask.command.js +2 -1
- package/dist/commands/changes.command.d.ts.map +1 -1
- package/dist/commands/changes.command.js +14 -4
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +139 -7
- package/dist/commands/command-catalog.d.ts +11 -0
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +291 -0
- package/dist/commands/commands.command.d.ts +15 -0
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +162 -3
- package/dist/commands/compress.command.d.ts.map +1 -1
- package/dist/commands/compress.command.js +39 -4
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +3 -1
- package/dist/commands/daily.commands.d.ts.map +1 -1
- package/dist/commands/daily.commands.js +48 -5
- package/dist/commands/diff-check.command.d.ts.map +1 -1
- package/dist/commands/diff-check.command.js +6 -2
- package/dist/commands/feedback.command.d.ts.map +1 -1
- package/dist/commands/feedback.command.js +4 -0
- package/dist/commands/finish.command.d.ts +3 -0
- package/dist/commands/finish.command.d.ts.map +1 -0
- package/dist/commands/finish.command.js +70 -0
- package/dist/commands/framework.command.d.ts.map +1 -1
- package/dist/commands/framework.command.js +20 -3
- package/dist/commands/gate.command.d.ts.map +1 -1
- package/dist/commands/gate.command.js +98 -13
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
- package/dist/commands/graph-code-subverbs.js +215 -81
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +6 -0
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +20 -2
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +86 -1
- package/dist/commands/knowledge.command.d.ts.map +1 -1
- package/dist/commands/knowledge.command.js +21 -1
- package/dist/commands/onboard.command.d.ts.map +1 -1
- package/dist/commands/onboard.command.js +11 -2
- package/dist/commands/orchestrate.command.d.ts.map +1 -1
- package/dist/commands/orchestrate.command.js +6 -1
- package/dist/commands/pack-author.command.d.ts.map +1 -1
- package/dist/commands/pack-author.command.js +13 -1
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +95 -17
- package/dist/commands/paths.command.d.ts.map +1 -1
- package/dist/commands/paths.command.js +8 -1
- package/dist/commands/pipelines.command.d.ts.map +1 -1
- package/dist/commands/pipelines.command.js +2 -1
- package/dist/commands/plan-context.command.d.ts.map +1 -1
- package/dist/commands/plan-context.command.js +6 -3
- package/dist/commands/plan-simulate.command.js +1 -1
- package/dist/commands/policy-lint.command.d.ts.map +1 -1
- package/dist/commands/policy-lint.command.js +20 -7
- package/dist/commands/recommend.command.d.ts.map +1 -1
- package/dist/commands/recommend.command.js +10 -2
- package/dist/commands/registry.command.d.ts.map +1 -1
- package/dist/commands/registry.command.js +117 -9
- package/dist/commands/report.command.js +1 -1
- package/dist/commands/reuse.command.d.ts.map +1 -1
- package/dist/commands/reuse.command.js +17 -5
- package/dist/commands/review.command.d.ts.map +1 -1
- package/dist/commands/review.command.js +20 -2
- package/dist/commands/rule-graph-subverbs.d.ts.map +1 -1
- package/dist/commands/rule-graph-subverbs.js +31 -4
- package/dist/commands/search-structural.command.d.ts.map +1 -1
- package/dist/commands/search-structural.command.js +2 -2
- package/dist/commands/search.command.d.ts.map +1 -1
- package/dist/commands/search.command.js +19 -1
- package/dist/commands/simulate.command.d.ts.map +1 -1
- package/dist/commands/simulate.command.js +6 -1
- package/dist/commands/smart-context.command.d.ts.map +1 -1
- package/dist/commands/smart-context.command.js +101 -41
- package/dist/commands/spec.command.d.ts.map +1 -1
- package/dist/commands/spec.command.js +73 -13
- package/dist/commands/task-context.command.d.ts.map +1 -1
- package/dist/commands/task-context.command.js +26 -2
- package/dist/commands/test.command.d.ts.map +1 -1
- package/dist/commands/test.command.js +17 -0
- package/dist/commands/trace.command.d.ts.map +1 -1
- package/dist/commands/trace.command.js +87 -4
- package/dist/commands/wiring.command.d.ts +12 -0
- package/dist/commands/wiring.command.d.ts.map +1 -0
- package/dist/commands/wiring.command.js +384 -0
- package/dist/diff/collect-changed-paths.d.ts +5 -3
- package/dist/diff/collect-changed-paths.d.ts.map +1 -1
- package/dist/diff/collect-changed-paths.js +73 -36
- package/dist/diff/deleted-orphans.d.ts +42 -0
- package/dist/diff/deleted-orphans.d.ts.map +1 -0
- package/dist/diff/deleted-orphans.js +46 -0
- package/dist/finish/run-finish.d.ts +62 -0
- package/dist/finish/run-finish.d.ts.map +1 -0
- package/dist/finish/run-finish.js +239 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -1
- package/dist/status/freshness.d.ts +30 -0
- package/dist/status/freshness.d.ts.map +1 -0
- package/dist/status/freshness.js +81 -0
- package/package.json +33 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.command.d.ts","sourceRoot":"","sources":["../../src/commands/context.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgChC,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"context.command.d.ts","sourceRoot":"","sources":["../../src/commands/context.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgChC,eAAO,MAAM,cAAc,EAAE,eA2I5B,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { entrypointBanner, inspectSharkcraft } from '@shrkcrft/inspector';
|
|
1
|
+
import { contextTuningBoostFor, entrypointBanner, inspectSharkcraft } from '@shrkcrft/inspector';
|
|
2
2
|
import { buildContext } from '@shrkcrft/context';
|
|
3
3
|
import { loadIntentBenchmark, runIntentBenchmark, STARTER_INTENT_BENCHMARK, writeBenchmarkRun, } from '@shrkcrft/context-planner';
|
|
4
4
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
@@ -67,6 +67,7 @@ export const contextCommand = {
|
|
|
67
67
|
const includeCommands = flagBool(args, 'include-commands');
|
|
68
68
|
const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
|
|
69
69
|
const overview = buildProjectOverview(inspection.workspace, inspection.config?.projectName);
|
|
70
|
+
const contextBoost = contextTuningBoostFor(inspection, task);
|
|
70
71
|
const result = buildContext(inspection.knowledgeEntries, {
|
|
71
72
|
task,
|
|
72
73
|
framework,
|
|
@@ -81,6 +82,7 @@ export const contextCommand = {
|
|
|
81
82
|
includeDocs,
|
|
82
83
|
includeCommands,
|
|
83
84
|
projectOverview: renderOverviewText(overview),
|
|
85
|
+
...(contextBoost ? { boostFor: contextBoost } : {}),
|
|
84
86
|
});
|
|
85
87
|
// Surface top commands prominently before the long context body.
|
|
86
88
|
// Auto-promote commands-first for action-like tasks (rename / add /
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daily.commands.d.ts","sourceRoot":"","sources":["../../src/commands/daily.commands.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAMhC,eAAO,MAAM,WAAW,EAAE,eAsFzB,CAAC;AAcF,eAAO,MAAM,WAAW,EAAE,eAoGzB,CAAC;AAKF,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"daily.commands.d.ts","sourceRoot":"","sources":["../../src/commands/daily.commands.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAMhC,eAAO,MAAM,WAAW,EAAE,eAsFzB,CAAC;AAcF,eAAO,MAAM,WAAW,EAAE,eAoGzB,CAAC;AAKF,eAAO,MAAM,cAAc,EAAE,eAyG5B,CAAC"}
|
|
@@ -186,14 +186,33 @@ export const explainCommand = {
|
|
|
186
186
|
const top = inspection.knowledgeEntries
|
|
187
187
|
.filter((e) => matches(topic, e.id, e.title, e.content, e.tags.join(' ')))
|
|
188
188
|
.slice(0, maxEntries);
|
|
189
|
+
// Surface the matched items themselves, not just counts. `explain`
|
|
190
|
+
// otherwise duplicates `shrk search`'s job while hiding what matched.
|
|
191
|
+
const relevantRules = packet.relevantRules.map((r) => ({
|
|
192
|
+
id: r.id,
|
|
193
|
+
title: r.title,
|
|
194
|
+
priority: r.priority,
|
|
195
|
+
}));
|
|
196
|
+
const relevantPaths = packet.relevantPaths.map((p) => ({
|
|
197
|
+
id: p.id,
|
|
198
|
+
title: p.title,
|
|
199
|
+
priority: p.priority,
|
|
200
|
+
}));
|
|
201
|
+
const relevantTemplates = packet.relevantTemplates.map((t) => ({
|
|
202
|
+
id: t.id,
|
|
203
|
+
name: t.name,
|
|
204
|
+
}));
|
|
189
205
|
if (flagBool(args, 'json')) {
|
|
190
206
|
process.stdout.write(asJson({
|
|
191
207
|
topic,
|
|
192
208
|
summary: {
|
|
193
|
-
relevantRules:
|
|
194
|
-
relevantPaths:
|
|
195
|
-
relevantTemplates:
|
|
209
|
+
relevantRules: relevantRules.length,
|
|
210
|
+
relevantPaths: relevantPaths.length,
|
|
211
|
+
relevantTemplates: relevantTemplates.length,
|
|
196
212
|
},
|
|
213
|
+
relevantRules,
|
|
214
|
+
relevantPaths,
|
|
215
|
+
relevantTemplates,
|
|
197
216
|
entries: top.map((e) => ({
|
|
198
217
|
id: e.id,
|
|
199
218
|
title: e.title,
|
|
@@ -205,11 +224,35 @@ export const explainCommand = {
|
|
|
205
224
|
return 0;
|
|
206
225
|
}
|
|
207
226
|
process.stdout.write(header(`explain: ${topic}`));
|
|
208
|
-
process.stdout.write(kv('matches', `rules=${
|
|
227
|
+
process.stdout.write(kv('matches', `rules=${relevantRules.length} paths=${relevantPaths.length} templates=${relevantTemplates.length}`) + '\n\n');
|
|
228
|
+
if (relevantRules.length > 0) {
|
|
229
|
+
process.stdout.write('Rules:\n');
|
|
230
|
+
for (const r of relevantRules) {
|
|
231
|
+
process.stdout.write(` • [${r.priority}] ${r.id} — ${r.title}\n`);
|
|
232
|
+
}
|
|
233
|
+
process.stdout.write('\n');
|
|
234
|
+
}
|
|
235
|
+
if (relevantPaths.length > 0) {
|
|
236
|
+
process.stdout.write('Paths:\n');
|
|
237
|
+
for (const p of relevantPaths) {
|
|
238
|
+
process.stdout.write(` • [${p.priority}] ${p.id} — ${p.title}\n`);
|
|
239
|
+
}
|
|
240
|
+
process.stdout.write('\n');
|
|
241
|
+
}
|
|
242
|
+
if (relevantTemplates.length > 0) {
|
|
243
|
+
process.stdout.write('Templates:\n');
|
|
244
|
+
for (const t of relevantTemplates) {
|
|
245
|
+
process.stdout.write(` • ${t.id} — ${t.name}\n`);
|
|
246
|
+
}
|
|
247
|
+
process.stdout.write('\n');
|
|
248
|
+
}
|
|
209
249
|
if (top.length === 0) {
|
|
210
|
-
|
|
250
|
+
if (relevantRules.length + relevantPaths.length + relevantTemplates.length === 0) {
|
|
251
|
+
process.stdout.write('(no matching knowledge entries)\n');
|
|
252
|
+
}
|
|
211
253
|
return 0;
|
|
212
254
|
}
|
|
255
|
+
process.stdout.write('Knowledge:\n');
|
|
213
256
|
for (const e of top) {
|
|
214
257
|
process.stdout.write(`• [${e.priority}] ${e.id} — ${e.title}\n`);
|
|
215
258
|
const body = e.content
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-check.command.d.ts","sourceRoot":"","sources":["../../src/commands/diff-check.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAcH,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"diff-check.command.d.ts","sourceRoot":"","sources":["../../src/commands/diff-check.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAcH,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA6GhC,eAAO,MAAM,gBAAgB,EAAE,eAkI9B,CAAC"}
|
|
@@ -34,9 +34,12 @@ function resolveScope(args, cwd) {
|
|
|
34
34
|
const staged = flagBool(args, 'staged');
|
|
35
35
|
const since = flagString(args, 'since');
|
|
36
36
|
const filesRaw = flagString(args, 'files');
|
|
37
|
+
// Files come from `--files a,b` or as bare positional args
|
|
38
|
+
// (`shrk diff-check a.ts b.ts`). Positionals were previously ignored, which
|
|
39
|
+
// silently widened the scope back to the full worktree.
|
|
37
40
|
const files = filesRaw
|
|
38
41
|
? filesRaw.split(',').map((s) => s.trim()).filter((s) => s.length > 0)
|
|
39
|
-
:
|
|
42
|
+
: args.positional.filter((s) => s.length > 0);
|
|
40
43
|
if (files.length > 0) {
|
|
41
44
|
return { mode: 'files', options: { projectRoot: cwd, files } };
|
|
42
45
|
}
|
|
@@ -97,7 +100,8 @@ function deriveVerdict(env) {
|
|
|
97
100
|
export const diffCheckCommand = {
|
|
98
101
|
name: 'diff-check',
|
|
99
102
|
description: 'Self-check the current git diff against this project\'s boundary + import-hygiene rules. Single-call composite of `shrk check boundaries --changed-only` + `shrk check imports --changed-only`, with one verdict and one nextAction line. Designed for AI agents to run after editing — pass --json for the structured envelope.',
|
|
100
|
-
usage: 'shrk [--cwd <dir>] diff-check [
|
|
103
|
+
usage: 'shrk [--cwd <dir>] diff-check [files... | --files a.ts,b.ts | --staged | --since <ref>] [--json]',
|
|
104
|
+
booleanFlags: new Set(['json', 'staged']),
|
|
101
105
|
async run(args) {
|
|
102
106
|
const cwd = resolveCwd(args);
|
|
103
107
|
const wantJson = flagBool(args, 'json');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feedback.command.d.ts","sourceRoot":"","sources":["../../src/commands/feedback.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"feedback.command.d.ts","sourceRoot":"","sources":["../../src/commands/feedback.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAiBhC,eAAO,MAAM,qBAAqB,EAAE,eAiCnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eA0CtC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAyDpC,CAAC;AAEF,eAAO,MAAM,+BAA+B,EAAE,eAwB7C,CAAC;AAIF,eAAO,MAAM,wBAAwB,EAAE,eAqBtC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,eAoDxC,CAAC;AAGF,eAAO,MAAM,sBAAsB,EAAE,eAqBpC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eA6BjC,CAAC"}
|
|
@@ -10,6 +10,7 @@ import * as nodePath from 'node:path';
|
|
|
10
10
|
import { FeedbackBucket, ingestFeedbackFile, inspectSharkcraft, loadFeedbackRules, renderFeedbackBacklog, } from '@shrkcrft/inspector';
|
|
11
11
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
12
12
|
import { asJson, header } from "../output/format-output.js";
|
|
13
|
+
import { feedbackRulesDoctorHints, renderFailureHints } from "../output/failure-hints.js";
|
|
13
14
|
function ingestPositional(args) {
|
|
14
15
|
return args.positional[0];
|
|
15
16
|
}
|
|
@@ -256,6 +257,9 @@ export const feedbackRulesDoctorCommand = {
|
|
|
256
257
|
process.stdout.write(` ERROR ${e.id}: ${e.message}\n`);
|
|
257
258
|
for (const w of warnings)
|
|
258
259
|
process.stdout.write(` WARN ${w.id}: ${w.message}\n`);
|
|
260
|
+
if (errors.length > 0) {
|
|
261
|
+
process.stdout.write(renderFailureHints(feedbackRulesDoctorHints()));
|
|
262
|
+
}
|
|
259
263
|
return errors.length > 0 ? 1 : 0;
|
|
260
264
|
},
|
|
261
265
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finish.command.d.ts","sourceRoot":"","sources":["../../src/commands/finish.command.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA8DhC,eAAO,MAAM,aAAa,EAAE,eAmB3B,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
2
|
+
import { asJson, bullet, header, kv } from "../output/format-output.js";
|
|
3
|
+
import { runFinishGates } from "../finish/run-finish.js";
|
|
4
|
+
function resolveScope(args, cwd) {
|
|
5
|
+
const staged = flagBool(args, 'staged');
|
|
6
|
+
const since = flagString(args, 'since');
|
|
7
|
+
const filesRaw = flagString(args, 'files');
|
|
8
|
+
const files = filesRaw
|
|
9
|
+
? filesRaw.split(',').map((s) => s.trim()).filter((s) => s.length > 0)
|
|
10
|
+
: args.positional.filter((s) => s.length > 0);
|
|
11
|
+
if (files.length > 0)
|
|
12
|
+
return { mode: 'files', options: { projectRoot: cwd, files } };
|
|
13
|
+
if (staged)
|
|
14
|
+
return { mode: 'staged', options: { projectRoot: cwd, staged: true } };
|
|
15
|
+
if (since)
|
|
16
|
+
return { mode: 'since', options: { projectRoot: cwd, since } };
|
|
17
|
+
return { mode: 'worktree', options: { projectRoot: cwd, includeWorktree: true } };
|
|
18
|
+
}
|
|
19
|
+
const STATUS_GLYPH = {
|
|
20
|
+
pass: '✓',
|
|
21
|
+
fail: '✗',
|
|
22
|
+
skipped: '–',
|
|
23
|
+
};
|
|
24
|
+
function renderText(report) {
|
|
25
|
+
process.stdout.write(header('Finish — is this changeset safe to complete?'));
|
|
26
|
+
process.stdout.write(kv('scope', `${report.scope.mode} (${report.scope.fileCount} file${report.scope.fileCount === 1 ? '' : 's'})`) +
|
|
27
|
+
'\n');
|
|
28
|
+
for (const g of report.gates) {
|
|
29
|
+
process.stdout.write(` ${STATUS_GLYPH[g.status]} ${g.name.padEnd(11)} ${g.status.padEnd(8)} ${g.detail}\n`);
|
|
30
|
+
}
|
|
31
|
+
if (report.impact.ran) {
|
|
32
|
+
process.stdout.write(kv('impact', `risk=${report.impact.risk}, ${report.impact.directDependents} direct / ${report.impact.transitiveDependents} transitive dependents`) +
|
|
33
|
+
'\n');
|
|
34
|
+
}
|
|
35
|
+
else if (report.impact.note) {
|
|
36
|
+
process.stdout.write(kv('impact', `(skipped — ${report.impact.note})`) + '\n');
|
|
37
|
+
}
|
|
38
|
+
process.stdout.write(kv('verdict', report.verdict) + '\n\n');
|
|
39
|
+
process.stdout.write(report.summary + '\n');
|
|
40
|
+
const failing = report.gates.filter((g) => g.status === 'fail');
|
|
41
|
+
for (const g of failing) {
|
|
42
|
+
process.stdout.write(`\n${g.name} — failing items:\n`);
|
|
43
|
+
for (const item of g.items.slice(0, 15)) {
|
|
44
|
+
const loc = item.file ? `${item.file}${item.line ? `:${item.line}` : ''}` : '';
|
|
45
|
+
process.stdout.write(bullet(`${loc ? loc + ' — ' : ''}${item.message}`) + '\n');
|
|
46
|
+
}
|
|
47
|
+
if (g.items.length > 15) {
|
|
48
|
+
process.stdout.write(` … and ${g.items.length - 15} more (pass --json for the full list).\n`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
process.stdout.write(`\nNext: ${report.nextAction}\n`);
|
|
52
|
+
}
|
|
53
|
+
export const finishCommand = {
|
|
54
|
+
name: 'finish',
|
|
55
|
+
description: 'Composite "is this changeset safe to finish?" gate: EXECUTES every deterministic changed-only check inline — boundaries + import-hygiene + wiring + policy + deleted-orphans — plus an impact summary, and returns ONE pass/fail. The single trustworthy "done?" call after editing (superset of `diff-check`; honors 0-rules→skipped). Read-only.',
|
|
56
|
+
usage: 'shrk [--cwd <dir>] finish [files... | --files a.ts,b.ts | --staged | --since <ref>] [--json]',
|
|
57
|
+
booleanFlags: new Set(['json', 'staged']),
|
|
58
|
+
async run(args) {
|
|
59
|
+
const cwd = resolveCwd(args);
|
|
60
|
+
const wantJson = flagBool(args, 'json');
|
|
61
|
+
const { mode, options } = resolveScope(args, cwd);
|
|
62
|
+
const report = await runFinishGates({ cwd, mode, scope: options });
|
|
63
|
+
if (wantJson) {
|
|
64
|
+
process.stdout.write(asJson(report) + '\n');
|
|
65
|
+
return report.verdict === 'fail' ? 1 : 0;
|
|
66
|
+
}
|
|
67
|
+
renderText(report);
|
|
68
|
+
return report.verdict === 'fail' ? 1 : 0;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framework.command.d.ts","sourceRoot":"","sources":["../../src/commands/framework.command.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"framework.command.d.ts","sourceRoot":"","sources":["../../src/commands/framework.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAIhC;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAe9B,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { buildRegistryWithPacks, defaultRegistry, FrameworkQueryApi, runExtractors, } from '@shrkcrft/framework-scanners';
|
|
2
2
|
import { inspectSharkcraft } from '@shrkcrft/inspector';
|
|
3
|
-
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
3
|
+
import { flagBool, flagPositiveInt, flagString, resolveCwd, } from "../command-registry.js";
|
|
4
4
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
5
|
+
import { computeMtimeFreshness } from "../status/freshness.js";
|
|
5
6
|
/**
|
|
6
7
|
* `shrk framework` — run / inspect the framework-aware extractors.
|
|
7
8
|
*
|
|
@@ -109,8 +110,20 @@ async function runStatus(args) {
|
|
|
109
110
|
}
|
|
110
111
|
const api = FrameworkQueryApi.fromStore(cwd);
|
|
111
112
|
const m = api.manifest();
|
|
113
|
+
// Honest freshness: the framework store is stale once a source file changed
|
|
114
|
+
// after it was built. Mirror the `state + lastBuiltAt + drift` shape that
|
|
115
|
+
// `graph status` exposes.
|
|
116
|
+
const fresh = computeMtimeFreshness(cwd, m.lastBuiltAt);
|
|
112
117
|
if (wantJson) {
|
|
113
|
-
process.stdout.write(asJson({
|
|
118
|
+
process.stdout.write(asJson({
|
|
119
|
+
ok: true,
|
|
120
|
+
state: fresh.state,
|
|
121
|
+
lastBuiltAt: m.lastBuiltAt,
|
|
122
|
+
lastChangedAt: fresh.lastChangedAt,
|
|
123
|
+
behindMs: fresh.behindMs,
|
|
124
|
+
...(fresh.state === 'stale' ? { nextCommand: 'shrk framework index' } : {}),
|
|
125
|
+
manifest: m,
|
|
126
|
+
}) + '\n');
|
|
114
127
|
return 0;
|
|
115
128
|
}
|
|
116
129
|
process.stdout.write(header('Framework status'));
|
|
@@ -120,6 +133,10 @@ async function runStatus(args) {
|
|
|
120
133
|
process.stdout.write(kv(` ${k}`, String(v)) + '\n');
|
|
121
134
|
}
|
|
122
135
|
process.stdout.write(kv('last built', m.lastBuiltAt) + '\n');
|
|
136
|
+
process.stdout.write(kv('state', fresh.state) + '\n');
|
|
137
|
+
if (fresh.state === 'stale') {
|
|
138
|
+
process.stdout.write(`! stale — source changed since last build${fresh.lastChangedAt ? ` (last change ${fresh.lastChangedAt})` : ''}; re-run \`shrk framework index\`\n`);
|
|
139
|
+
}
|
|
123
140
|
return 0;
|
|
124
141
|
}
|
|
125
142
|
async function runList(args) {
|
|
@@ -134,7 +151,7 @@ async function runList(args) {
|
|
|
134
151
|
const framework = flagString(args, 'framework');
|
|
135
152
|
const subtype = flagString(args, 'subtype');
|
|
136
153
|
const file = flagString(args, 'file');
|
|
137
|
-
const limit =
|
|
154
|
+
const limit = flagPositiveInt(args, 'limit', 50);
|
|
138
155
|
const entities = api.list({
|
|
139
156
|
...(framework ? { framework } : {}),
|
|
140
157
|
...(subtype ? { subtype } : {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.command.d.ts","sourceRoot":"","sources":["../../src/commands/gate.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gate.command.d.ts","sourceRoot":"","sources":["../../src/commands/gate.command.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,eAiMzB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { QualityGateReportStore, renderGateReportMarkdown, runQualityGates, } from '@shrkcrft/quality-gates';
|
|
2
|
-
import {
|
|
2
|
+
import { inspectSharkcraft, resolveChangedFiles, resolveProjectConfig, } from '@shrkcrft/inspector';
|
|
3
3
|
import { chmodSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import * as nodePath from 'node:path';
|
|
5
5
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
@@ -19,7 +19,7 @@ import { asJson, header, kv } from "../output/format-output.js";
|
|
|
19
19
|
export const gateCommand = {
|
|
20
20
|
name: 'gate',
|
|
21
21
|
description: 'Aggregator: runs the code-intelligence quality gates (graph freshness, architecture, impact-since-ref) and reports a single pass/fail.',
|
|
22
|
-
usage: 'shrk gate [--since <gitref>] [--fail-on critical,high] [--arch-all] [--disable arch,impact,api-diff] [--api-baseline <path>] [--no-fail-on-breaking] [--strict] [--no-persist] [--json] [--markdown] [--output <path>]\n (the arch gate is baseline-relative
|
|
22
|
+
usage: 'shrk gate [--since <gitref>] [--changed-only] [--staged] [--files a,b,c] [--fail-on critical,high] [--arch-all] [--disable arch,impact,policy,knowledge-symbol,api-diff] [--api-baseline <path>] [--no-fail-on-breaking] [--strict] [--no-persist] [--json] [--markdown] [--output <path>]\n (the arch gate is baseline-relative once a baseline is frozen — fails only on NEW errors; with no baseline it warns on errors rather than going perpetually red — --arch-all fails on total, --strict escalates the warn)\n (--changed-only / --staged / --files / --since scope the wiring + policy + knowledge-symbol gates to the changeset; they also drive the impact gate — --since diffs the gitref, the others analyze the changed-file set)\n shrk gate scaffold-ci [--provider github|generic] [--force] [--json]\n shrk gate scaffold-hook [--provider husky|raw] [--force] [--json]',
|
|
23
23
|
async run(args) {
|
|
24
24
|
if (args.positional[0] === 'scaffold-ci') {
|
|
25
25
|
const sliced = { ...args, positional: args.positional.slice(1) };
|
|
@@ -39,30 +39,115 @@ export const gateCommand = {
|
|
|
39
39
|
const disableRaw = flagString(args, 'disable');
|
|
40
40
|
const apiBaseline = flagString(args, 'api-baseline');
|
|
41
41
|
const noFailOnBreaking = flagBool(args, 'no-fail-on-breaking');
|
|
42
|
-
|
|
42
|
+
// `--fail-on` accepts only `high` / `critical`. An unknown token used to
|
|
43
|
+
// silently REPLACE the default `['critical']`, leaving nothing able to fail
|
|
44
|
+
// the gate — reject it loudly (exit 2) instead.
|
|
45
|
+
const failOnTokens = failOnRaw
|
|
43
46
|
? failOnRaw.split(',').map((s) => s.trim()).filter(Boolean)
|
|
44
47
|
: undefined;
|
|
48
|
+
if (failOnTokens) {
|
|
49
|
+
const allowedRisk = new Set(['high', 'critical']);
|
|
50
|
+
const unknown = failOnTokens.filter((t) => !allowedRisk.has(t));
|
|
51
|
+
if (unknown.length > 0) {
|
|
52
|
+
process.stderr.write(`Unknown --fail-on value(s): ${unknown.join(', ')}. Allowed: high, critical.\n`);
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const failOn = failOnTokens && failOnTokens.length > 0
|
|
57
|
+
? failOnTokens
|
|
58
|
+
: undefined;
|
|
45
59
|
const disable = disableRaw ? disableRaw.split(',').map((s) => s.trim()).filter(Boolean) : undefined;
|
|
46
60
|
// --arch-all: fail on TOTAL architecture errors (ignore the frozen baseline).
|
|
47
61
|
// By default the arch gate is baseline-relative — it fails only on NEW errors.
|
|
48
62
|
const archAll = flagBool(args, 'arch-all');
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
63
|
+
// Changeset scope. `--changed-only` (tracked + untracked worktree),
|
|
64
|
+
// `--staged`, `--files`, and `--since` all narrow the wiring + policy +
|
|
65
|
+
// knowledge-symbol gates to the change. `--since` additionally drives the
|
|
66
|
+
// (ref-based) impact gate, as before.
|
|
67
|
+
const changedOnly = flagBool(args, 'changed-only');
|
|
68
|
+
const staged = flagBool(args, 'staged');
|
|
69
|
+
const filesRaw = flagString(args, 'files');
|
|
70
|
+
const fileList = filesRaw ? filesRaw.split(',').map((s) => s.trim()).filter(Boolean) : [];
|
|
71
|
+
const wantChangedScope = changedOnly || staged || Boolean(sinceRef) || fileList.length > 0;
|
|
72
|
+
let changedFiles;
|
|
73
|
+
if (wantChangedScope) {
|
|
74
|
+
const resolved = resolveChangedFiles({
|
|
75
|
+
projectRoot: cwd,
|
|
76
|
+
...(fileList.length > 0 ? { files: fileList } : {}),
|
|
77
|
+
...(staged ? { staged: true } : {}),
|
|
78
|
+
...(sinceRef ? { since: sinceRef } : {}),
|
|
79
|
+
...(changedOnly && !staged && !sinceRef && fileList.length === 0
|
|
80
|
+
? { includeWorktree: true }
|
|
81
|
+
: {}),
|
|
82
|
+
});
|
|
83
|
+
changedFiles = resolved.files;
|
|
84
|
+
}
|
|
85
|
+
// Wiring + policy rules come from the project config; each gate is skipped
|
|
86
|
+
// (never red) when none are declared, so they're inert for projects that
|
|
87
|
+
// don't opt in. An INVALID config is surfaced (warn) rather than silently
|
|
88
|
+
// disabling the plane.
|
|
89
|
+
const loadedConfig = await resolveProjectConfig(cwd);
|
|
53
90
|
const wiringRules = loadedConfig.ok ? loadedConfig.value.config.wiringRules ?? [] : [];
|
|
54
|
-
const
|
|
91
|
+
const policyRules = loadedConfig.ok ? loadedConfig.value.config.policyRules ?? [] : [];
|
|
92
|
+
const configError = loadedConfig.ok ? undefined : loadedConfig.error.message;
|
|
93
|
+
// Pack-plane merge notes (missing/invalid pack rule files, dropped
|
|
94
|
+
// collisions) go to stderr so they never pollute the JSON/markdown report
|
|
95
|
+
// on stdout that CI consumes.
|
|
96
|
+
if (loadedConfig.ok) {
|
|
97
|
+
for (const d of loadedConfig.value.planeDiagnostics) {
|
|
98
|
+
process.stderr.write(`plane: ${d}\n`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const scopeOpts = wantChangedScope
|
|
102
|
+
? { changedOnly: true, changedFiles: changedFiles ?? [] }
|
|
103
|
+
: {};
|
|
104
|
+
// Knowledge symbol-ref integrity needs the loaded knowledge entries. The
|
|
105
|
+
// inspection is async, so we build it here and inject it; the gate stays
|
|
106
|
+
// synchronous and resolves the code graph itself. Best-effort — a failed
|
|
107
|
+
// inspection just skips the gate rather than failing `shrk gate`.
|
|
108
|
+
let inspection;
|
|
109
|
+
if (!disable?.includes('knowledge-symbol')) {
|
|
110
|
+
try {
|
|
111
|
+
inspection = await inspectSharkcraft({ cwd });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
inspection = undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
55
117
|
const report = runQualityGates({
|
|
56
118
|
projectRoot: cwd,
|
|
57
119
|
...(archAll ? { arch: { baselineRelative: false } } : {}),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
120
|
+
wiring: {
|
|
121
|
+
...(configError
|
|
122
|
+
? { configError }
|
|
123
|
+
: wiringRules.length > 0
|
|
124
|
+
? { rules: wiringRules }
|
|
125
|
+
: {}),
|
|
126
|
+
...scopeOpts,
|
|
127
|
+
},
|
|
128
|
+
policy: {
|
|
129
|
+
...(configError
|
|
130
|
+
? { configError }
|
|
131
|
+
: policyRules.length > 0
|
|
132
|
+
? { rules: policyRules }
|
|
133
|
+
: {}),
|
|
134
|
+
...scopeOpts,
|
|
135
|
+
},
|
|
136
|
+
...(inspection
|
|
137
|
+
? {
|
|
138
|
+
knowledgeSymbol: {
|
|
139
|
+
inspection,
|
|
140
|
+
...(wantChangedScope ? { changedFiles: changedFiles ?? [] } : {}),
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
: {}),
|
|
63
144
|
impact: {
|
|
64
145
|
...(sinceRef ? { sinceRef } : {}),
|
|
65
146
|
...(failOn ? { failOn } : {}),
|
|
147
|
+
// Scope the impact gate to the changeset too: with `--since` we keep the
|
|
148
|
+
// gitref diff; with `--changed-only` / `--staged` / `--files` (and no
|
|
149
|
+
// `--since`) we analyze the resolved changed-file set directly.
|
|
150
|
+
...(wantChangedScope && !sinceRef ? { files: changedFiles ?? [] } : {}),
|
|
66
151
|
},
|
|
67
152
|
...(apiBaseline
|
|
68
153
|
? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-code-subverbs.d.ts","sourceRoot":"","sources":["../../src/commands/graph-code-subverbs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph-code-subverbs.d.ts","sourceRoot":"","sources":["../../src/commands/graph-code-subverbs.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAiE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAgLxH,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBrE;AA4FD,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkEtE;AAiBD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA2F1E;AAID,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+EpE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+FtE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwEtE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA2MvE;AAID,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+HtE;AAID;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgEpE;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAiGvE;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgHpE"}
|