@shrkcrft/cli 0.1.0-alpha.22 → 0.1.0-alpha.23
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 +30 -5
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +200 -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/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 +183 -68
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +6 -0
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +91 -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 +87 -4
- 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 +14 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +15 -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
|
@@ -3,6 +3,8 @@ import * as nodePath from 'node:path';
|
|
|
3
3
|
import { analyzeImpact, analyzeTestImpact, findSymbolInProject, FuzzyImpactSourceKind, getChangedFiles, ImpactInputKind, inspectSharkcraft, QueryMatchKind, readFeatureBundle, renderImpactGraph, renderImpactHtml, renderImpactMarkdown, renderImpactText, resolveFuzzyImpact, warmConstructCache, } from '@shrkcrft/inspector';
|
|
4
4
|
import { flagBool, flagNumber, flagString, flagList, resolveCwd, } from "../command-registry.js";
|
|
5
5
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
6
|
+
import { fuzzyImpactAmbiguousHints, renderFailureHints } from "../output/failure-hints.js";
|
|
7
|
+
import { collectChangedPaths } from "../diff/collect-changed-paths.js";
|
|
6
8
|
function collectFiles(args, cwd) {
|
|
7
9
|
const diagnostics = [];
|
|
8
10
|
const explicitFiles = flagList(args, 'files');
|
|
@@ -278,10 +280,90 @@ async function runViaGraph(args) {
|
|
|
278
280
|
process.stdout.write(`! ${d}\n`);
|
|
279
281
|
return 0;
|
|
280
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* `shrk impact --deleted`: deleted-symbol orphan check. Reads the
|
|
285
|
+
* working-tree diff's DELETED files (relative to `--since <ref>` or the
|
|
286
|
+
* default branch) and queries the graph for surviving files that still
|
|
287
|
+
* import them or reference a symbol they declared (alias-resolved, incl.
|
|
288
|
+
* barrel re-exports). Any surviving importer is reported with `file:line`
|
|
289
|
+
* and the command exits non-zero; with none it exits 0 with
|
|
290
|
+
* "no orphaned importers". Mirrors `--via-graph`'s flag/JSON style.
|
|
291
|
+
*
|
|
292
|
+
* The graph is queried against the CURRENT index snapshot — a not-yet-
|
|
293
|
+
* reindexed delete still carries its inbound edges, which is exactly what
|
|
294
|
+
* lets the check see the broken importers before they hit the build.
|
|
295
|
+
*/
|
|
296
|
+
async function runDeletedOrphans(args) {
|
|
297
|
+
const cwd = resolveCwd(args);
|
|
298
|
+
const wantJson = flagBool(args, 'json') || flagString(args, 'format') === 'json';
|
|
299
|
+
const sinceRef = flagString(args, 'since');
|
|
300
|
+
const changed = collectChangedPaths({ cwd, ...(sinceRef ? { ref: sinceRef } : {}) });
|
|
301
|
+
if (!changed.isAvailable) {
|
|
302
|
+
if (wantJson) {
|
|
303
|
+
process.stdout.write(asJson({ ok: false, error: changed.error }) + '\n');
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
process.stderr.write(`Cannot resolve diff: ${changed.error ?? 'unknown'}\n`);
|
|
307
|
+
}
|
|
308
|
+
return 2;
|
|
309
|
+
}
|
|
310
|
+
const deleted = changed.deleted;
|
|
311
|
+
if (deleted.length === 0) {
|
|
312
|
+
if (wantJson) {
|
|
313
|
+
process.stdout.write(asJson({
|
|
314
|
+
schema: 'sharkcraft.deleted-orphans/v1',
|
|
315
|
+
resolvedDeleted: [],
|
|
316
|
+
unresolvedDeleted: [],
|
|
317
|
+
orphans: [],
|
|
318
|
+
diagnostics: [`no deleted files in diff vs ${changed.ref}`],
|
|
319
|
+
}) + '\n');
|
|
320
|
+
return 0;
|
|
321
|
+
}
|
|
322
|
+
process.stdout.write(header('Deleted-symbol orphans'));
|
|
323
|
+
process.stdout.write(`No deleted files in diff vs ${changed.ref}.\n`);
|
|
324
|
+
return 0;
|
|
325
|
+
}
|
|
326
|
+
const { GraphStore, GraphQueryApi } = await import('@shrkcrft/graph');
|
|
327
|
+
if (!new GraphStore(cwd).exists()) {
|
|
328
|
+
const msg = 'code-graph store missing — run `shrk graph index` first.';
|
|
329
|
+
if (wantJson) {
|
|
330
|
+
process.stdout.write(asJson({ ok: false, error: msg }) + '\n');
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
process.stderr.write(msg + '\n');
|
|
334
|
+
}
|
|
335
|
+
return 2;
|
|
336
|
+
}
|
|
337
|
+
const { findDeletedOrphans } = await import('@shrkcrft/impact-engine');
|
|
338
|
+
const report = findDeletedOrphans(GraphQueryApi.fromStore(cwd), deleted);
|
|
339
|
+
if (wantJson) {
|
|
340
|
+
process.stdout.write(asJson(report) + '\n');
|
|
341
|
+
return report.orphans.length > 0 ? 1 : 0;
|
|
342
|
+
}
|
|
343
|
+
process.stdout.write(header('Deleted-symbol orphans'));
|
|
344
|
+
process.stdout.write(`Deleted files (vs ${changed.ref}): ${deleted.length}\n`);
|
|
345
|
+
if (report.orphans.length === 0) {
|
|
346
|
+
process.stdout.write('no orphaned importers\n');
|
|
347
|
+
for (const d of report.diagnostics.slice(0, 5))
|
|
348
|
+
process.stdout.write(`! ${d}\n`);
|
|
349
|
+
return 0;
|
|
350
|
+
}
|
|
351
|
+
process.stdout.write(`\n${report.orphans.length} surviving importer(s) still reference deleted code:\n`);
|
|
352
|
+
for (const o of report.orphans) {
|
|
353
|
+
const loc = o.path ? `${o.path}${o.line ? `:${o.line}` : ''}` : o.id;
|
|
354
|
+
const detail = o.via === 'reference' && o.symbol
|
|
355
|
+
? `references \`${o.symbol}\``
|
|
356
|
+
: 'imports';
|
|
357
|
+
process.stdout.write(` ✗ ${loc} ${detail} from deleted ${o.deletedFile}\n`);
|
|
358
|
+
}
|
|
359
|
+
for (const d of report.diagnostics.slice(0, 5))
|
|
360
|
+
process.stdout.write(`! ${d}\n`);
|
|
361
|
+
return 1;
|
|
362
|
+
}
|
|
281
363
|
export const impactCommand = {
|
|
282
364
|
name: 'impact',
|
|
283
365
|
description: 'Architecture impact analysis: direct + transitive dependents, risk + suggested commands. Supports fuzzy <query> resolution. Read-only.',
|
|
284
|
-
usage: 'shrk impact <fileOrQuery> | --file <path> | --specifier <spec> | --since <ref> | --staged | --files a,b | --plan <plan.json> | --bundle <id> [--max-depth N] [--limit N] [--format text|markdown|html|json] [--output <path>] [--tree|--no-tree] [--json] [--html] [--resolve|--resolve-only|--explain-resolution|--no-resolve]',
|
|
366
|
+
usage: 'shrk impact <fileOrQuery> | --file <path> | --specifier <spec> | --since <ref> | --staged | --files a,b | --plan <plan.json> | --bundle <id> | --deleted [--since <ref>] [--max-depth N] [--limit N] [--format text|markdown|html|json] [--output <path>] [--tree|--no-tree] [--json] [--html] [--resolve|--resolve-only|--explain-resolution|--no-resolve]',
|
|
285
367
|
async run(args) {
|
|
286
368
|
if (args.positional[0] === 'tests') {
|
|
287
369
|
return runImpactTests({ ...args, positional: args.positional.slice(1) });
|
|
@@ -299,6 +381,13 @@ export const impactCommand = {
|
|
|
299
381
|
if (args.flags.has('via-graph')) {
|
|
300
382
|
return runViaGraph(args);
|
|
301
383
|
}
|
|
384
|
+
// --deleted: reverse-of-impact orphan check. Reads the working-tree
|
|
385
|
+
// diff's DELETED files and asks the graph which surviving files still
|
|
386
|
+
// import them or reference a symbol they declared. Any surviving
|
|
387
|
+
// importer is an error (its build breaks once the delete lands).
|
|
388
|
+
if (args.flags.has('deleted')) {
|
|
389
|
+
return runDeletedOrphans(args);
|
|
390
|
+
}
|
|
302
391
|
const cwd = resolveCwd(args);
|
|
303
392
|
const inspection = await inspectSharkcraft({ cwd });
|
|
304
393
|
// Warm construct cache so fuzzy resolution can map plugin keys / events /
|
|
@@ -487,6 +576,7 @@ export const impactCommand = {
|
|
|
487
576
|
}
|
|
488
577
|
for (const d of fuzzyResolution.diagnostics)
|
|
489
578
|
process.stdout.write(` ! ${d}\n`);
|
|
579
|
+
process.stdout.write(renderFailureHints(fuzzyImpactAmbiguousHints()));
|
|
490
580
|
}
|
|
491
581
|
return 1;
|
|
492
582
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knowledge.command.d.ts","sourceRoot":"","sources":["../../src/commands/knowledge.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"knowledge.command.d.ts","sourceRoot":"","sources":["../../src/commands/knowledge.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAoGhC,eAAO,MAAM,oBAAoB,EAAE,eAgDlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAuBjC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAuCpC,CAAC;AAoBF,eAAO,MAAM,0BAA0B,EAAE,eAWxC,CAAC;AA8NF,eAAO,MAAM,sBAAsB,EAAE,eAQpC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,eA8CxC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAuBrC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,EAAE,eA8B1C,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,eA8BxC,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,eAkC1C,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join as nodePathJoin, resolve as nodePathResolve } from 'node:path';
|
|
3
3
|
import { buildAnchorUpdatePlan, buildKnowledgeStaleReport, buildRenameFilePlan, buildRenameSymbolPlan, inspectSharkcraft, ReferenceCheckOutcome, resolveChangedFiles, } from '@shrkcrft/inspector';
|
|
4
|
-
import { formatEntryCompact, formatEntryFull, projectKnowledgeEntryForJson, searchKnowledge, } from '@shrkcrft/knowledge';
|
|
4
|
+
import { ALL_KNOWLEDGE_TYPES, formatEntryCompact, formatEntryFull, isKnowledgeType, projectKnowledgeEntryForJson, searchKnowledge, } from '@shrkcrft/knowledge';
|
|
5
5
|
import { flagBool, flagNumber, flagString, flagList, resolveCwd, } from "../command-registry.js";
|
|
6
6
|
import { asJson, header } from "../output/format-output.js";
|
|
7
7
|
import { maybeRunInWatchMode } from "../output/watch-loop.js";
|
|
@@ -51,6 +51,20 @@ function renderStaleCheckHtml(p) {
|
|
|
51
51
|
${p.ci.exitNonZero ? `<p style="color:#a40000;font-weight:bold">FAIL: ${esc(p.ci.exitReason ?? '')}</p>` : ''}
|
|
52
52
|
`;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate `--type` filter values against the KnowledgeType vocabulary and
|
|
56
|
+
* reject (exit 2) on any unknown value. Without this a typo'd `--type` would
|
|
57
|
+
* silently filter every entry out and return an empty result with exit 0.
|
|
58
|
+
* Returns the exit code to propagate when a value is unknown, or null when all
|
|
59
|
+
* values are valid (or none were supplied).
|
|
60
|
+
*/
|
|
61
|
+
function rejectUnknownKnowledgeTypes(types) {
|
|
62
|
+
const unknown = types.filter((t) => !isKnowledgeType(t));
|
|
63
|
+
if (unknown.length === 0)
|
|
64
|
+
return null;
|
|
65
|
+
process.stderr.write(`Unknown --type ${unknown.join(', ')}. Valid: ${ALL_KNOWLEDGE_TYPES.join(', ')}\n`);
|
|
66
|
+
return 2;
|
|
67
|
+
}
|
|
54
68
|
export const knowledgeListCommand = {
|
|
55
69
|
name: 'list',
|
|
56
70
|
description: 'List knowledge entries.',
|
|
@@ -58,6 +72,9 @@ export const knowledgeListCommand = {
|
|
|
58
72
|
async run(args) {
|
|
59
73
|
const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
|
|
60
74
|
const types = flagList(args, 'type');
|
|
75
|
+
const typeReject = rejectUnknownKnowledgeTypes(types);
|
|
76
|
+
if (typeReject !== null)
|
|
77
|
+
return typeReject;
|
|
61
78
|
const scope = flagList(args, 'scope');
|
|
62
79
|
let entries = inspection.knowledgeEntries;
|
|
63
80
|
if (types.length)
|
|
@@ -129,6 +146,9 @@ export const knowledgeSearchCommand = {
|
|
|
129
146
|
const query = args.positional.join(' ').trim();
|
|
130
147
|
const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
|
|
131
148
|
const types = flagList(args, 'type');
|
|
149
|
+
const typeReject = rejectUnknownKnowledgeTypes(types);
|
|
150
|
+
if (typeReject !== null)
|
|
151
|
+
return typeReject;
|
|
132
152
|
const scope = flagList(args, 'scope');
|
|
133
153
|
const tags = flagList(args, 'tag');
|
|
134
154
|
const appliesWhen = flagList(args, 'appliesWhen');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboard.command.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.command.ts"],"names":[],"mappings":"AA6CA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"onboard.command.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.command.ts"],"names":[],"mappings":"AA6CA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,cAAc,EAAE,eAwO5B,CAAC"}
|
|
@@ -14,13 +14,22 @@ export const onboardCommand = {
|
|
|
14
14
|
return runOnboardAdopt(sliced);
|
|
15
15
|
}
|
|
16
16
|
const cwd = resolveCwd(args);
|
|
17
|
-
const
|
|
17
|
+
const writeDraftsFlag = flagBool(args, 'write-drafts');
|
|
18
|
+
const dryRunFlag = flagBool(args, 'dry-run');
|
|
19
|
+
// --dry-run is authoritative: it overrides --write-drafts so that
|
|
20
|
+
// `onboard --dry-run --write-drafts` never writes. `writeDrafts` below is
|
|
21
|
+
// the EFFECTIVE value used by every write guard / mode label.
|
|
22
|
+
const writeDrafts = writeDraftsFlag && !dryRunFlag;
|
|
23
|
+
const dryRunOverrodeWrite = writeDraftsFlag && dryRunFlag;
|
|
18
24
|
const scaffoldTemplates = flagBool(args, 'scaffold-templates');
|
|
19
25
|
const importAgents = flagBool(args, 'import-agents');
|
|
20
26
|
const diffMode = flagBool(args, 'diff');
|
|
21
|
-
const dryRun =
|
|
27
|
+
const dryRun = !writeDrafts;
|
|
22
28
|
const preferredPreset = flagString(args, 'preset');
|
|
23
29
|
const asJsonOut = flagBool(args, 'json');
|
|
30
|
+
if (dryRunOverrodeWrite) {
|
|
31
|
+
process.stderr.write('Note: --dry-run overrode --write-drafts; no files were written.\n');
|
|
32
|
+
}
|
|
24
33
|
const inspection = await inspectSharkcraft({ cwd });
|
|
25
34
|
const plan = buildOnboardingPlan(inspection, {
|
|
26
35
|
...(preferredPreset ? { preferredPreset } : {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrate.command.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrate.command.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestrate.command.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrate.command.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,kBAAkB,EAAE,eAoDhC,CAAC"}
|
|
@@ -3,10 +3,11 @@ import * as nodePath from 'node:path';
|
|
|
3
3
|
import { buildAgentOrchestrationPlan, inspectSharkcraft, OrchestrationMode, renderOrchestrationMarkdown, renderOrchestrationText, } from '@shrkcrft/inspector';
|
|
4
4
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
5
5
|
import { asJson } from "../output/format-output.js";
|
|
6
|
+
const VALID_ORCHESTRATION_MODES = ['conservative', 'balanced', 'aggressive'];
|
|
6
7
|
export const orchestrateCommand = {
|
|
7
8
|
name: 'orchestrate',
|
|
8
9
|
description: 'Produce a read-only agent orchestration plan (discovery / plan / review / apply / validate). No execution; no writes.',
|
|
9
|
-
usage: 'shrk orchestrate "<task>" [--mode conservative|balanced|aggressive] [--
|
|
10
|
+
usage: 'shrk orchestrate "<task>" [--mode conservative|balanced|aggressive] [--json] [--output <file.md>]',
|
|
10
11
|
async run(args) {
|
|
11
12
|
const task = args.positional.join(' ').trim();
|
|
12
13
|
if (!task) {
|
|
@@ -16,6 +17,10 @@ export const orchestrateCommand = {
|
|
|
16
17
|
const cwd = resolveCwd(args);
|
|
17
18
|
const inspection = await inspectSharkcraft({ cwd });
|
|
18
19
|
const modeRaw = (flagString(args, 'mode') ?? '').toLowerCase();
|
|
20
|
+
// Warn on a typo'd --mode rather than silently treating it as balanced.
|
|
21
|
+
if (modeRaw && !VALID_ORCHESTRATION_MODES.includes(modeRaw)) {
|
|
22
|
+
process.stderr.write(`Unknown --mode ${modeRaw}. Valid: ${VALID_ORCHESTRATION_MODES.join(', ')} (defaulting to balanced).\n`);
|
|
23
|
+
}
|
|
19
24
|
const mode = modeRaw === 'conservative'
|
|
20
25
|
? OrchestrationMode.Conservative
|
|
21
26
|
: modeRaw === 'aggressive'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pack-author.command.d.ts","sourceRoot":"","sources":["../../src/commands/pack-author.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAuChC,eAAO,MAAM,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"pack-author.command.d.ts","sourceRoot":"","sources":["../../src/commands/pack-author.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAuChC,eAAO,MAAM,uBAAuB,EAAE,eA2CrC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAoFtC,CAAC;AAKF,eAAO,MAAM,wBAAwB,EAAE,eAgCtC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,eAgBvC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,EAAE,eA2BlC,CAAC"}
|
|
@@ -49,8 +49,16 @@ export const packAuthorStatusCommand = {
|
|
|
49
49
|
const cwd = resolveCwd(args);
|
|
50
50
|
const inspection = await inspectSharkcraft({ cwd });
|
|
51
51
|
const status = buildPackAuthorStatus(inspection);
|
|
52
|
+
// Freshness: the pack authoring state is stale (relative to a signed,
|
|
53
|
+
// applied baseline) while drafts are pending — they have been authored
|
|
54
|
+
// since the last apply/sign but not yet incorporated.
|
|
55
|
+
const state = status.pendingDrafts > 0 ? 'stale' : 'fresh';
|
|
52
56
|
if (flagBool(args, 'json')) {
|
|
53
|
-
process.stdout.write(asJson(
|
|
57
|
+
process.stdout.write(asJson({
|
|
58
|
+
...status,
|
|
59
|
+
state,
|
|
60
|
+
...(state === 'stale' ? { nextCommand: 'shrk pack author pending' } : {}),
|
|
61
|
+
}) + '\n');
|
|
54
62
|
return 0;
|
|
55
63
|
}
|
|
56
64
|
process.stdout.write(header('Pack author status'));
|
|
@@ -60,6 +68,10 @@ export const packAuthorStatusCommand = {
|
|
|
60
68
|
process.stdout.write(` pending drafts: ${status.pendingDrafts}\n`);
|
|
61
69
|
for (const f of status.pendingDraftFiles)
|
|
62
70
|
process.stdout.write(` - ${f}\n`);
|
|
71
|
+
process.stdout.write(` state: ${state}\n`);
|
|
72
|
+
if (state === 'stale') {
|
|
73
|
+
process.stdout.write(` ! stale — ${status.pendingDrafts} pending draft(s) authored since the last apply/sign; run \`shrk pack author pending\`\n`);
|
|
74
|
+
}
|
|
63
75
|
process.stdout.write(' contribution counts:\n');
|
|
64
76
|
for (const [k, n] of Object.entries(status.contributionCounts)) {
|
|
65
77
|
const support = status.authoringSupport[k] ?? 'deferred';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packs.command.d.ts","sourceRoot":"","sources":["../../src/commands/packs.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAQhC,eAAO,MAAM,yBAAyB,EAAE,eAoCvC,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,eAsGzC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eA+CnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"packs.command.d.ts","sourceRoot":"","sources":["../../src/commands/packs.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAQhC,eAAO,MAAM,yBAAyB,EAAE,eAoCvC,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,eAsGzC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eA+CnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,eAwD9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAyE7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAqDjC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eAoGhC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eA2IhC,CAAC;AA4DF,eAAO,MAAM,gBAAgB,EAAE,eA+K9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAmCtC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eAmFhC,CAAC;AAMF,eAAO,MAAM,qBAAqB,EAAE,eA4BnC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,eA+E/B,CAAC"}
|
|
@@ -201,6 +201,7 @@ export const packsListCommand = {
|
|
|
201
201
|
resolvedCounts: p.resolvedCounts,
|
|
202
202
|
signatureStatus: p.signatureStatus,
|
|
203
203
|
signatureMessage: p.signatureMessage,
|
|
204
|
+
signatureDev: p.signatureDev,
|
|
204
205
|
loadError: p.loadError,
|
|
205
206
|
validationIssues: p.validationIssues,
|
|
206
207
|
}))) + '\n');
|
|
@@ -282,6 +283,9 @@ export const packsGetCommand = {
|
|
|
282
283
|
if (pack.signatureStatus) {
|
|
283
284
|
process.stdout.write('\nSignature:\n');
|
|
284
285
|
process.stdout.write(` status: ${pack.signatureStatus}\n`);
|
|
286
|
+
if (pack.signatureDev) {
|
|
287
|
+
process.stdout.write(' dev: yes (NOT release-trusted)\n');
|
|
288
|
+
}
|
|
285
289
|
if (pack.signatureMessage) {
|
|
286
290
|
process.stdout.write(` message: ${pack.signatureMessage}\n`);
|
|
287
291
|
}
|
|
@@ -355,20 +359,30 @@ export const packsInspectCommand = {
|
|
|
355
359
|
export const packsDoctorCommand = {
|
|
356
360
|
name: 'doctor',
|
|
357
361
|
description: 'Validate pack discovery: invalid manifests, missing files, empty contributions, duplicates, template/pipeline quality, action-hint coverage, signatures. `--release` folds pack-release-check findings into the report. `--signature-explain` adds per-pack signature explanation.',
|
|
358
|
-
usage: 'shrk [--cwd <dir>] packs doctor [--verify-signatures] [--require-signatures] [--release] [--strict] [--secret <secret>] [--signature-explain] [--json]',
|
|
362
|
+
usage: 'shrk [--cwd <dir>] packs doctor [--verify-signatures] [--require-signatures] [--allow-dev-signature] [--release] [--strict] [--secret <secret>] [--signature-explain] [--json]',
|
|
359
363
|
async run(args) {
|
|
360
|
-
const
|
|
364
|
+
const signatureExplain = flagBool(args, 'signature-explain');
|
|
365
|
+
// `--signature-explain` implies signature verification so the lifecycle
|
|
366
|
+
// states it prints are backed by a real HMAC check this run — otherwise a
|
|
367
|
+
// bogus-HMAC-but-fresh-timestamp pack would be reported off freshness
|
|
368
|
+
// alone instead of the honest "invalid".
|
|
369
|
+
const verify = flagBool(args, 'verify-signatures') ||
|
|
370
|
+
flagBool(args, 'require-signatures') ||
|
|
371
|
+
signatureExplain;
|
|
361
372
|
const required = flagBool(args, 'require-signatures');
|
|
362
373
|
const release = flagBool(args, 'release');
|
|
363
374
|
const strict = flagBool(args, 'strict');
|
|
364
375
|
const secret = flagString(args, 'secret');
|
|
365
|
-
const
|
|
376
|
+
const allowDev = flagBool(args, 'allow-dev-signature');
|
|
366
377
|
const inspection = await inspectSharkcraft({
|
|
367
378
|
cwd: resolveCwd(args),
|
|
368
379
|
...(verify ? { verifyPackSignatures: true } : {}),
|
|
369
380
|
...(secret !== undefined ? { packSecret: secret } : {}),
|
|
370
381
|
});
|
|
371
|
-
const report = buildPackDoctorReport(inspection, {
|
|
382
|
+
const report = buildPackDoctorReport(inspection, {
|
|
383
|
+
requireSignatures: required,
|
|
384
|
+
...(allowDev ? { allowDevSignatures: true } : {}),
|
|
385
|
+
});
|
|
372
386
|
if (release) {
|
|
373
387
|
const releaseChecks = await runPackReleaseChecksForReport(inspection);
|
|
374
388
|
mergePackReleaseChecks(inspection, report, releaseChecks, { strict });
|
|
@@ -439,32 +453,93 @@ export const packsDoctorCommand = {
|
|
|
439
453
|
};
|
|
440
454
|
export const packsVerifyCommand = {
|
|
441
455
|
name: 'verify',
|
|
442
|
-
description: 'Verify HMAC signatures on every discovered pack. Unsigned packs are reported but do not fail.',
|
|
443
|
-
usage: 'shrk [--cwd <dir>] packs verify [--secret <secret>] [--required] [--json]',
|
|
456
|
+
description: 'Verify HMAC signatures on every discovered pack. Unsigned packs are reported but do not fail. Under --required, a signed pack that could not be verified (no secret) or that carries a dev signature fails too — pass --allow-dev-signature to trust dev signatures for local-only flows.',
|
|
457
|
+
usage: 'shrk [--cwd <dir>] packs verify [--secret <secret>] [--required] [--allow-dev-signature] [--json]',
|
|
444
458
|
async run(args) {
|
|
445
459
|
const secret = flagString(args, 'secret');
|
|
460
|
+
const allowDev = flagBool(args, 'allow-dev-signature');
|
|
446
461
|
const inspection = await inspectSharkcraft({
|
|
447
462
|
cwd: resolveCwd(args),
|
|
448
463
|
verifyPackSignatures: true,
|
|
449
464
|
...(secret !== undefined ? { packSecret: secret } : {}),
|
|
450
465
|
});
|
|
451
466
|
const required = flagBool(args, 'required');
|
|
452
|
-
const rows = inspection.packs.discoveredPacks.map((p) =>
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
467
|
+
const rows = inspection.packs.discoveredPacks.map((p) => {
|
|
468
|
+
let signatureStatus = (p.signatureStatus ?? 'not-checked');
|
|
469
|
+
let signatureMessage = p.signatureMessage;
|
|
470
|
+
let signatureDev = p.signatureDev === true;
|
|
471
|
+
// --allow-dev-signature: actually run the HMAC check against the
|
|
472
|
+
// well-known dev secret so a *tampered* dev signature still fails. The
|
|
473
|
+
// default discovery path short-circuits to 'dev-signature' BEFORE
|
|
474
|
+
// hashing, so we must re-verify here rather than blindly trust the tag.
|
|
475
|
+
if (allowDev && signatureStatus === 'dev-signature' && p.manifest) {
|
|
476
|
+
const v = verifyPackManifest(p.manifest, {
|
|
477
|
+
allowDev: true,
|
|
478
|
+
...(secret !== undefined ? { secret } : {}),
|
|
479
|
+
});
|
|
480
|
+
signatureStatus = (v.ok ? 'verified' : v.status);
|
|
481
|
+
signatureMessage = v.ok ? 'Dev signature verified (--allow-dev-signature).' : v.message;
|
|
482
|
+
signatureDev = true;
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
packageName: p.packageName,
|
|
486
|
+
packageVersion: p.packageVersion,
|
|
487
|
+
valid: p.valid,
|
|
488
|
+
signatureStatus,
|
|
489
|
+
signatureMessage,
|
|
490
|
+
signatureDev,
|
|
491
|
+
};
|
|
492
|
+
});
|
|
459
493
|
const tampered = rows.some((r) => r.signatureStatus === 'invalid-signature');
|
|
460
494
|
const unsigned = rows.some((r) => r.signatureStatus === 'missing-signature');
|
|
461
|
-
|
|
495
|
+
// A SIGNED pack we could NOT actually verify this run: the secret was
|
|
496
|
+
// missing, or it carries a dev signature that is not release-trusted (and
|
|
497
|
+
// --allow-dev-signature was not passed). Reporting these as "OK" was the
|
|
498
|
+
// fail-open hole — required verification that could not run must FAIL.
|
|
499
|
+
const missingSecretCount = rows.filter((r) => r.signatureStatus === 'missing-secret').length;
|
|
500
|
+
const devCount = rows.filter((r) => r.signatureStatus === 'dev-signature').length;
|
|
501
|
+
const unverifiable = missingSecretCount > 0 || devCount > 0;
|
|
502
|
+
const signedCount = rows.filter((r) => r.signatureStatus !== 'missing-signature' && r.signatureStatus !== 'not-checked').length;
|
|
503
|
+
const verifiedCount = rows.filter((r) => r.signatureStatus === 'verified').length;
|
|
504
|
+
const allSignedVerified = signedCount > 0 && verifiedCount === signedCount;
|
|
505
|
+
const passed = !tampered && (!required || (!unsigned && !unverifiable));
|
|
506
|
+
const verdict = (() => {
|
|
507
|
+
if (tampered)
|
|
508
|
+
return 'TAMPERED pack detected — abort!';
|
|
509
|
+
if (required && (unverifiable || unsigned)) {
|
|
510
|
+
const bits = [];
|
|
511
|
+
if (missingSecretCount > 0)
|
|
512
|
+
bits.push(`${missingSecretCount} signed pack(s) could not be verified (no secret) — set ${PACK_SECRET_ENV}`);
|
|
513
|
+
if (devCount > 0)
|
|
514
|
+
bits.push(`${devCount} dev-signed pack(s) are not release-trusted — pass --allow-dev-signature to accept them`);
|
|
515
|
+
if (unsigned)
|
|
516
|
+
bits.push('unsigned pack present');
|
|
517
|
+
return bits.join('; ');
|
|
518
|
+
}
|
|
519
|
+
if (allSignedVerified && !unsigned && !unverifiable)
|
|
520
|
+
return 'all signatures OK ✓';
|
|
521
|
+
if (unverifiable || unsigned) {
|
|
522
|
+
const bits = [];
|
|
523
|
+
if (missingSecretCount > 0)
|
|
524
|
+
bits.push(`${missingSecretCount} signed pack(s) unverified (no secret)`);
|
|
525
|
+
if (devCount > 0)
|
|
526
|
+
bits.push(`${devCount} dev-signed pack(s) not release-trusted`);
|
|
527
|
+
if (unsigned)
|
|
528
|
+
bits.push('unsigned pack present');
|
|
529
|
+
return bits.join('; ') + ' (use --required to fail)';
|
|
530
|
+
}
|
|
531
|
+
return signedCount > 0 ? 'all signatures OK ✓' : 'no signatures to verify';
|
|
532
|
+
})();
|
|
462
533
|
if (flagBool(args, 'json')) {
|
|
463
534
|
process.stdout.write(asJson({
|
|
464
535
|
passed,
|
|
465
536
|
required,
|
|
537
|
+
allowDevSignature: allowDev,
|
|
466
538
|
tampered,
|
|
467
539
|
unsignedPresent: unsigned,
|
|
540
|
+
unverifiable,
|
|
541
|
+
unverifiableCount: missingSecretCount,
|
|
542
|
+
devSignatureCount: devCount,
|
|
468
543
|
packs: rows,
|
|
469
544
|
}) + '\n');
|
|
470
545
|
return passed ? 0 : 1;
|
|
@@ -483,10 +558,13 @@ export const packsVerifyCommand = {
|
|
|
483
558
|
? 'UNSIGNED'
|
|
484
559
|
: r.signatureStatus === 'missing-secret'
|
|
485
560
|
? 'NO SECRET'
|
|
486
|
-
:
|
|
487
|
-
|
|
561
|
+
: r.signatureStatus === 'dev-signature'
|
|
562
|
+
? 'DEV'
|
|
563
|
+
: 'NOT CHECKED';
|
|
564
|
+
const devNote = r.signatureStatus === 'dev-signature' ? ' (not release-trusted)' : '';
|
|
565
|
+
process.stdout.write(` ${tag.padEnd(12)} ${r.packageName.padEnd(40)} ${r.signatureMessage ?? ''}${devNote}\n`);
|
|
488
566
|
}
|
|
489
|
-
process.stdout.write(`\nVerdict: ${
|
|
567
|
+
process.stdout.write(`\nVerdict: ${verdict}\n`);
|
|
490
568
|
if (!secret && !process.env[PACK_SECRET_ENV]) {
|
|
491
569
|
process.stdout.write(`(Tip: set ${PACK_SECRET_ENV} or pass --secret to verify signed packs.)\n`);
|
|
492
570
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.command.d.ts","sourceRoot":"","sources":["../../src/commands/paths.command.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,gBAAgB,EAAE,eAmB9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAuB7B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eAsBhC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"paths.command.d.ts","sourceRoot":"","sources":["../../src/commands/paths.command.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,gBAAgB,EAAE,eAmB9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAuB7B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eAsBhC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,eAiC9B,CAAC"}
|
|
@@ -82,11 +82,18 @@ export const pathsBestCommand = {
|
|
|
82
82
|
}
|
|
83
83
|
const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
|
|
84
84
|
const selection = inspection.pathService.findBestForTask(task);
|
|
85
|
+
const wantJson = flagBool(args, 'json');
|
|
85
86
|
if (!selection) {
|
|
87
|
+
// Keep `--json` machine-parseable on a miss: emit `{match:null}` rather
|
|
88
|
+
// than the human sentence so a consumer can `JSON.parse` the output.
|
|
89
|
+
if (wantJson) {
|
|
90
|
+
process.stdout.write(asJson({ match: null }) + '\n');
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
86
93
|
process.stdout.write('No matching path convention.\n');
|
|
87
94
|
return 0;
|
|
88
95
|
}
|
|
89
|
-
if (
|
|
96
|
+
if (wantJson) {
|
|
90
97
|
process.stdout.write(asJson(selection) + '\n');
|
|
91
98
|
return 0;
|
|
92
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipelines.command.d.ts","sourceRoot":"","sources":["../../src/commands/pipelines.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pipelines.command.d.ts","sourceRoot":"","sources":["../../src/commands/pipelines.command.ts"],"names":[],"mappings":"AAeA,OAAO,EAOL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC,eAAO,MAAM,oBAAoB,EAAE,eA2BlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAuBjC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAuDrC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eA+DlC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAoCpC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eA6ClC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAiElC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { inspectSharkcraft, buildProjectOverview, renderOverviewText } from '@shrkcrft/inspector';
|
|
1
|
+
import { inspectSharkcraft, buildProjectOverview, renderOverviewText, contextTuningBoostFor, } from '@shrkcrft/inspector';
|
|
2
2
|
import { buildContext } from '@shrkcrft/context';
|
|
3
3
|
import { findNextStep, formatPipelineCompact, formatPipelineFull, interpolatePipeline, renderPipelineScript, } from '@shrkcrft/pipelines';
|
|
4
4
|
import { flagBool, flagNumber, flagString, flagList, flagVars, resolveCwd, } from "../command-registry.js";
|
|
@@ -81,6 +81,7 @@ export const pipelinesContextCommand = {
|
|
|
81
81
|
scope,
|
|
82
82
|
maxTokens,
|
|
83
83
|
projectOverview: renderOverviewText(overview),
|
|
84
|
+
boostFor: contextTuningBoostFor(inspection, task),
|
|
84
85
|
});
|
|
85
86
|
if (flagBool(args, 'json')) {
|
|
86
87
|
process.stdout.write(asJson({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/plan-context.command.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"plan-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/plan-context.command.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAyEhC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { planContext } from '@shrkcrft/context-planner';
|
|
2
|
-
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
2
|
+
import { flagBool, flagPositiveInt, flagString, resolveCwd, } from "../command-registry.js";
|
|
3
3
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
4
4
|
/**
|
|
5
5
|
* `shrk plan-context` — produce a deterministic, token-budgeted context
|
|
@@ -21,8 +21,11 @@ export const planContextCommand = {
|
|
|
21
21
|
process.stderr.write(this.usage + '\n');
|
|
22
22
|
return 2;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
// NaN-safe: a fat-fingered `--budget abc` would otherwise become NaN and
|
|
25
|
+
// print "NaN tokens" while selecting 0 files at exit 0. Fall back to the
|
|
26
|
+
// documented defaults instead.
|
|
27
|
+
const budget = flagPositiveInt(args, 'budget', 8000);
|
|
28
|
+
const maxFiles = flagPositiveInt(args, 'max-files', 30);
|
|
26
29
|
const hintedFiles = collectMulti(args, 'hint-file');
|
|
27
30
|
const hintedPackages = collectMulti(args, 'hint-package');
|
|
28
31
|
const pack = planContext({
|
|
@@ -6,7 +6,7 @@ import { asJson } from "../output/format-output.js";
|
|
|
6
6
|
export const planSimulateCommand = {
|
|
7
7
|
name: 'simulate',
|
|
8
8
|
description: 'Simulate a saved generation plan (v1 or v2): virtual final content, classified operations, boundary/policy/impact/test gates, apply readiness. Read-only.',
|
|
9
|
-
usage: 'shrk plan simulate <plan.json> [--format text|markdown|html|json] [--output <file>] [--strict] [--
|
|
9
|
+
usage: 'shrk plan simulate <plan.json> [--format text|markdown|html|json] [--output <file>] [--strict] [--no-boundaries] [--no-impact] [--no-tests] [--no-policies] [--no-ownership] [--include-memory] [--diff] [--max-diff-lines N]',
|
|
10
10
|
async run(args) {
|
|
11
11
|
const planPath = args.positional[0];
|
|
12
12
|
if (!planPath) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-lint.command.d.ts","sourceRoot":"","sources":["../../src/commands/policy-lint.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"policy-lint.command.d.ts","sourceRoot":"","sources":["../../src/commands/policy-lint.command.ts"],"names":[],"mappings":"AAIA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,iBAAiB,EAAE,eAkJ/B,CAAC"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as nodePath from 'node:path';
|
|
2
2
|
import { runPolicyLint } from '@shrkcrft/boundaries';
|
|
3
|
-
import {
|
|
4
|
-
import { resolveChangedFiles } from '@shrkcrft/inspector';
|
|
3
|
+
import { resolveChangedFiles, resolveProjectConfig } from '@shrkcrft/inspector';
|
|
5
4
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
6
5
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
7
6
|
const VALID_SURFACES = new Set(['template', 'style', 'ts']);
|
|
@@ -27,11 +26,11 @@ export const policyLintCommand = {
|
|
|
27
26
|
surfaces = parts;
|
|
28
27
|
}
|
|
29
28
|
// Distinguish invalid config from valid-with-no-rules (no silent fail-open).
|
|
30
|
-
const loaded = await
|
|
29
|
+
const loaded = await resolveProjectConfig(cwd);
|
|
31
30
|
if (!loaded.ok) {
|
|
32
31
|
const msg = loaded.error.message;
|
|
33
32
|
if (wantJson) {
|
|
34
|
-
process.stdout.write(asJson({ schema: 'sharkcraft.policy-lint/v1', error: msg, rules: [], findings: [], diagnostics: [msg], verdict: 'errors' }) + '\n');
|
|
33
|
+
process.stdout.write(asJson({ schema: 'sharkcraft.policy-lint/v1', error: msg, rules: [], findings: [], diagnostics: [msg], evaluated: 0, verdict: 'errors' }) + '\n');
|
|
35
34
|
return 1;
|
|
36
35
|
}
|
|
37
36
|
process.stdout.write(header('Policy lint'));
|
|
@@ -39,9 +38,10 @@ export const policyLintCommand = {
|
|
|
39
38
|
return 1;
|
|
40
39
|
}
|
|
41
40
|
const rules = loaded.value.config.policyRules ?? [];
|
|
41
|
+
const planeDiagnostics = loaded.value.planeDiagnostics;
|
|
42
42
|
if (rules.length === 0) {
|
|
43
43
|
if (wantJson) {
|
|
44
|
-
process.stdout.write(asJson({ schema: 'sharkcraft.policy-lint/v1', rules: [], findings: [], diagnostics: [], verdict: 'pass' }) + '\n');
|
|
44
|
+
process.stdout.write(asJson({ schema: 'sharkcraft.policy-lint/v1', rules: [], findings: [], diagnostics: [], evaluated: 0, verdict: 'pass' }) + '\n');
|
|
45
45
|
return 0;
|
|
46
46
|
}
|
|
47
47
|
process.stdout.write(header('Policy lint'));
|
|
@@ -71,18 +71,31 @@ export const policyLintCommand = {
|
|
|
71
71
|
// hold the rule definitions themselves, which can self-match).
|
|
72
72
|
const sharkcraftRel = nodePath.relative(cwd, loaded.value.sharkcraftDir).split(nodePath.sep).join('/');
|
|
73
73
|
const excludeDirs = sharkcraftRel && !sharkcraftRel.startsWith('..') ? [sharkcraftRel] : [];
|
|
74
|
-
const
|
|
74
|
+
const reportRaw = runPolicyLint(cwd, rules, {
|
|
75
75
|
...(surfaces ? { surfaces } : {}),
|
|
76
76
|
...(changedOnly || since ? { changedOnly: true, changedFiles: changedFiles ?? [] } : {}),
|
|
77
77
|
...(only ? { only: only.split(',').map((s) => s.trim()).filter(Boolean) } : {}),
|
|
78
78
|
...(excludeDirs.length > 0 ? { excludeDirs } : {}),
|
|
79
79
|
});
|
|
80
|
+
// Surface pack-plane merge notes (missing/invalid pack policy files, dropped
|
|
81
|
+
// collisions) in the same diagnostics array the engine already emits.
|
|
82
|
+
const report = planeDiagnostics.length > 0
|
|
83
|
+
? { ...reportRaw, diagnostics: [...reportRaw.diagnostics, ...planeDiagnostics] }
|
|
84
|
+
: reportRaw;
|
|
80
85
|
if (wantJson) {
|
|
81
86
|
process.stdout.write(asJson(report) + '\n');
|
|
82
87
|
return report.verdict === 'errors' ? 1 : 0;
|
|
83
88
|
}
|
|
84
89
|
process.stdout.write(header('Policy lint'));
|
|
85
|
-
|
|
90
|
+
// `evaluated` counts rules that actually scanned ≥1 file. When 0 rules
|
|
91
|
+
// evaluated but rules ARE configured, say so loudly — "scanned nothing" must
|
|
92
|
+
// never read as the green "no policy violations" pass.
|
|
93
|
+
if (report.evaluated === 0) {
|
|
94
|
+
process.stdout.write(` ! Nothing evaluated — ${report.rules.length} rule(s) configured but none matched files in scope` +
|
|
95
|
+
(changedOnly || since ? ' (changed-only).\n' : '.\n'));
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
process.stdout.write(kv('rules evaluated', `${report.evaluated} of ${report.rules.length}`) + '\n');
|
|
86
99
|
const errors = report.findings.filter((f) => f.severity === 'error').length;
|
|
87
100
|
const warnings = report.findings.filter((f) => f.severity === 'warning').length;
|
|
88
101
|
process.stdout.write(kv('findings', `${errors} error(s), ${warnings} warning(s)`) + '\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recommend.command.d.ts","sourceRoot":"","sources":["../../src/commands/recommend.command.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAShC,eAAO,MAAM,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"recommend.command.d.ts","sourceRoot":"","sources":["../../src/commands/recommend.command.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAShC,eAAO,MAAM,gBAAgB,EAAE,eA4P9B,CAAC;AA0HF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAY3D;AAcD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAcxD"}
|