@shrkcrft/inspector 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-recommender.d.ts.map +1 -1
- package/dist/command-recommender.js +110 -1
- package/dist/context-tuning.d.ts +14 -0
- package/dist/context-tuning.d.ts.map +1 -0
- package/dist/context-tuning.js +38 -0
- package/dist/coverage-report.d.ts.map +1 -1
- package/dist/coverage-report.js +31 -8
- package/dist/git-helpers.d.ts.map +1 -1
- package/dist/git-helpers.js +10 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/knowledge-stale.d.ts +23 -0
- package/dist/knowledge-stale.d.ts.map +1 -1
- package/dist/knowledge-stale.js +58 -9
- package/dist/monorepo-onboarding.d.ts.map +1 -1
- package/dist/monorepo-onboarding.js +122 -12
- package/dist/onboarding.d.ts.map +1 -1
- package/dist/onboarding.js +79 -18
- package/dist/pack-doctor.d.ts +8 -1
- package/dist/pack-doctor.d.ts.map +1 -1
- package/dist/pack-doctor.js +110 -3
- package/dist/pack-quality-score.d.ts.map +1 -1
- package/dist/pack-quality-score.js +18 -13
- package/dist/pack-release-check.d.ts.map +1 -1
- package/dist/pack-release-check.js +23 -20
- package/dist/pack-signature-status.d.ts +9 -3
- package/dist/pack-signature-status.d.ts.map +1 -1
- package/dist/pack-signature-status.js +13 -2
- package/dist/plan-simulation.d.ts.map +1 -1
- package/dist/plan-simulation.js +26 -12
- package/dist/registry-lifecycle.d.ts +8 -0
- package/dist/registry-lifecycle.d.ts.map +1 -1
- package/dist/registry-lifecycle.js +10 -2
- package/dist/resolve-project-config.d.ts +26 -0
- package/dist/resolve-project-config.d.ts.map +1 -0
- package/dist/resolve-project-config.js +136 -0
- package/dist/resolve-verification-commands.d.ts +7 -0
- package/dist/resolve-verification-commands.d.ts.map +1 -1
- package/dist/resolve-verification-commands.js +53 -4
- package/dist/review-packet.d.ts +8 -0
- package/dist/review-packet.d.ts.map +1 -1
- package/dist/review-packet.js +11 -12
- package/dist/sharkcraft-inspector.d.ts.map +1 -1
- package/dist/sharkcraft-inspector.js +28 -2
- package/dist/spec/__tests__/spec-evidence.test.d.ts +2 -0
- package/dist/spec/__tests__/spec-evidence.test.d.ts.map +1 -0
- package/dist/spec/__tests__/spec-evidence.test.js +81 -0
- package/dist/spec/spec-evidence.d.ts +62 -0
- package/dist/spec/spec-evidence.d.ts.map +1 -0
- package/dist/spec/spec-evidence.js +215 -0
- package/dist/spec/spec-review.d.ts +1 -0
- package/dist/spec/spec-review.d.ts.map +1 -1
- package/dist/spec/spec-review.js +5 -0
- package/dist/symbol-index.d.ts +9 -0
- package/dist/symbol-index.d.ts.map +1 -1
- package/dist/symbol-index.js +6 -0
- package/dist/task-packet.d.ts.map +1 -1
- package/dist/task-packet.js +6 -1
- package/dist/task-ranker.d.ts +2 -1
- package/dist/task-ranker.d.ts.map +1 -1
- package/dist/task-ranker.js +30 -5
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +3 -0
- package/package.json +17 -17
|
@@ -31,17 +31,42 @@ export function buildMonorepoSummary(ws, subDirs) {
|
|
|
31
31
|
libs: [],
|
|
32
32
|
};
|
|
33
33
|
let scanned = 0;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
if (workspacesArr.length > 0) {
|
|
35
|
+
// Drive discovery from the declared `workspaces` globs so non-standard
|
|
36
|
+
// layouts (modules/*) and grouped layouts (packages/*/*) resolve to the
|
|
37
|
+
// real package directories rather than the hardcoded apps|packages|libs
|
|
38
|
+
// immediate children.
|
|
39
|
+
const seenPaths = new Set();
|
|
40
|
+
outer: for (const glob of workspacesArr) {
|
|
41
|
+
const leadSegment = glob.replace(/\\/g, '/').split('/').filter((s) => s.length > 0)[0] ?? '';
|
|
42
|
+
const groupKey = classifyGroup(leadSegment);
|
|
43
|
+
for (const rel of expandWorkspaceGlob(ws.projectRoot, glob)) {
|
|
44
|
+
if (scanned >= PACKAGE_LIMIT)
|
|
45
|
+
break outer;
|
|
46
|
+
if (seenPaths.has(rel))
|
|
47
|
+
continue;
|
|
48
|
+
seenPaths.add(rel);
|
|
49
|
+
const full = nodePath.join(ws.projectRoot, ...rel.split('/'));
|
|
50
|
+
groups[groupKey].push(describePackage(full, rel, groupKey));
|
|
51
|
+
scanned += 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// No `workspaces` field (e.g. Nx) — fall back to scanning the
|
|
57
|
+
// conventional apps|packages|libs immediate children.
|
|
58
|
+
for (const g of GROUPS) {
|
|
59
|
+
const children = subDirs.get(g.dir) ?? [];
|
|
60
|
+
for (const child of children) {
|
|
61
|
+
if (scanned >= PACKAGE_LIMIT)
|
|
62
|
+
break;
|
|
63
|
+
const rel = nodePath.posix.join(g.dir, child);
|
|
64
|
+
const full = nodePath.join(ws.projectRoot, g.dir, child);
|
|
65
|
+
if (!statSafeIsDir(full))
|
|
66
|
+
continue;
|
|
67
|
+
groups[g.key].push(describePackage(full, rel, g.key));
|
|
68
|
+
scanned += 1;
|
|
69
|
+
}
|
|
45
70
|
}
|
|
46
71
|
}
|
|
47
72
|
const rootVerificationCommands = buildRootVerificationCommands(ws);
|
|
@@ -78,6 +103,89 @@ export function buildMonorepoSummary(ws, subDirs) {
|
|
|
78
103
|
};
|
|
79
104
|
}
|
|
80
105
|
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
106
|
+
const WORKSPACE_IGNORE_DIRS = new Set([
|
|
107
|
+
'node_modules',
|
|
108
|
+
'.git',
|
|
109
|
+
'dist',
|
|
110
|
+
'build',
|
|
111
|
+
'.cache',
|
|
112
|
+
'.nx',
|
|
113
|
+
'.turbo',
|
|
114
|
+
'coverage',
|
|
115
|
+
]);
|
|
116
|
+
/** Classify a workspace glob into a group by its leading path segment. */
|
|
117
|
+
function classifyGroup(leadSegment) {
|
|
118
|
+
if (leadSegment === 'apps')
|
|
119
|
+
return 'apps';
|
|
120
|
+
if (leadSegment === 'libs')
|
|
121
|
+
return 'libs';
|
|
122
|
+
return 'packages';
|
|
123
|
+
}
|
|
124
|
+
function listChildDirs(dir) {
|
|
125
|
+
try {
|
|
126
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
127
|
+
.filter((e) => e.isDirectory() && !WORKSPACE_IGNORE_DIRS.has(e.name))
|
|
128
|
+
.map((e) => e.name)
|
|
129
|
+
.sort(); // deterministic regardless of filesystem order
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function hasPackageJson(dir) {
|
|
136
|
+
try {
|
|
137
|
+
return existsSync(nodePath.join(dir, 'package.json'));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Expand a `workspaces` glob to the directories that actually contain a
|
|
145
|
+
* `package.json`. Handles the simple cases the overwhelming majority of
|
|
146
|
+
* monorepos use: an exact path, a one-level wildcard ("dir" + slash + star),
|
|
147
|
+
* and a two-level wildcard (grouped packages). Returns relative posix-style
|
|
148
|
+
* paths. Anything more exotic (recursive globstars, or fixed segments after a
|
|
149
|
+
* wildcard) is skipped rather than guessed.
|
|
150
|
+
*/
|
|
151
|
+
function expandWorkspaceGlob(projectRoot, glob) {
|
|
152
|
+
const segments = glob
|
|
153
|
+
.replace(/\\/g, '/')
|
|
154
|
+
.replace(/\/+$/, '')
|
|
155
|
+
.split('/')
|
|
156
|
+
.filter((s) => s.length > 0);
|
|
157
|
+
if (segments.length === 0)
|
|
158
|
+
return [];
|
|
159
|
+
const starIdx = segments.findIndex((s) => s.includes('*'));
|
|
160
|
+
if (starIdx === -1) {
|
|
161
|
+
// Exact path, e.g. "packages/core".
|
|
162
|
+
const rel = segments.join('/');
|
|
163
|
+
return hasPackageJson(nodePath.join(projectRoot, ...segments)) ? [rel] : [];
|
|
164
|
+
}
|
|
165
|
+
const wildcardSegments = segments.slice(starIdx);
|
|
166
|
+
// Only the pure trailing-wildcard cases `dir/*` and `dir/*/*`.
|
|
167
|
+
if (wildcardSegments.length > 2 || !wildcardSegments.every((s) => s === '*')) {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
const baseSegments = segments.slice(0, starIdx);
|
|
171
|
+
const baseDir = nodePath.join(projectRoot, ...baseSegments);
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const first of listChildDirs(baseDir)) {
|
|
174
|
+
if (wildcardSegments.length === 1) {
|
|
175
|
+
if (hasPackageJson(nodePath.join(baseDir, first))) {
|
|
176
|
+
out.push([...baseSegments, first].join('/'));
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const mid = nodePath.join(baseDir, first);
|
|
181
|
+
for (const second of listChildDirs(mid)) {
|
|
182
|
+
if (hasPackageJson(nodePath.join(mid, second))) {
|
|
183
|
+
out.push([...baseSegments, first, second].join('/'));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
81
189
|
function statSafeIsDir(p) {
|
|
82
190
|
try {
|
|
83
191
|
return existsSync(p) && statSync(p).isDirectory();
|
|
@@ -229,7 +337,9 @@ function buildNotes(ws, groups) {
|
|
|
229
337
|
out.push('package.json workspaces detected.');
|
|
230
338
|
}
|
|
231
339
|
if (groups.apps.length === 0 && groups.packages.length === 0 && groups.libs.length === 0) {
|
|
232
|
-
|
|
340
|
+
// Discovery truly found nothing — no workspace package carried a
|
|
341
|
+
// package.json under any declared glob (or the GROUPS fallback scan).
|
|
342
|
+
out.push('Monorepo signals detected but no workspace packages with a package.json were discovered — keep onboarding bounded.');
|
|
233
343
|
}
|
|
234
344
|
return out;
|
|
235
345
|
}
|
package/dist/onboarding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"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"}
|
package/dist/onboarding.js
CHANGED
|
@@ -11,7 +11,6 @@ export function buildOnboardingPlan(inspection, options = {}) {
|
|
|
11
11
|
const subDirs = listSubDirs(ws.projectRoot);
|
|
12
12
|
const inferredPathConventions = inferPathConventions(ws, subDirs);
|
|
13
13
|
const inferredVerificationCommands = inferVerificationCommands(ws);
|
|
14
|
-
const inferredBoundaryRules = inferBoundaryRules(ws, subDirs);
|
|
15
14
|
const inferredTemplateCandidates = inferTemplateCandidates(ws, {
|
|
16
15
|
scaffoldTemplates: options.scaffoldTemplates === true,
|
|
17
16
|
});
|
|
@@ -19,6 +18,12 @@ export function buildOnboardingPlan(inspection, options = {}) {
|
|
|
19
18
|
const inferredPipelines = inferPipelines(ws, inferredVerificationCommands);
|
|
20
19
|
const detectedInstructionFiles = detectInstructionFiles(ws.projectRoot);
|
|
21
20
|
const monorepoSummary = buildMonorepoSummary(ws, subDirs);
|
|
21
|
+
// Boundary rules: the non-monorepo guess intentionally returns [] (SharkCraft
|
|
22
|
+
// does not invent boundaries from folder structure). Monorepo layouts, by
|
|
23
|
+
// contrast, surface real enforceable candidates (e.g. packages/* must not
|
|
24
|
+
// import apps/*) — merge them in so they reach the boundaries draft and
|
|
25
|
+
// `init --infer`.
|
|
26
|
+
const inferredBoundaryRules = mergeMonorepoBoundaryRules(inferBoundaryRules(ws, subDirs), monorepoSummary);
|
|
22
27
|
// RecommendPresets now applies a miss penalty (-3 per missing
|
|
23
28
|
// appliesTo profile), so a preferred preset that previously ranked just
|
|
24
29
|
// inside the top 5 can drop out on minor profile changes. Use a wider
|
|
@@ -221,11 +226,40 @@ function packageManagerRunPrefix(manager) {
|
|
|
221
226
|
}
|
|
222
227
|
// ─── Boundary rule candidates ────────────────────────────────────────────────
|
|
223
228
|
export function inferBoundaryRules(_ws, _subDirs) {
|
|
224
|
-
// SharkCraft does not guess your boundary rules
|
|
225
|
-
// explicitly in `sharkcraft/boundaries.ts` once the repo's
|
|
226
|
-
// import directions are known.
|
|
229
|
+
// SharkCraft does not guess your boundary rules from folder structure.
|
|
230
|
+
// Author them explicitly in `sharkcraft/boundaries.ts` once the repo's
|
|
231
|
+
// actual import directions are known. Monorepo-derived candidates are
|
|
232
|
+
// merged in separately via `mergeMonorepoBoundaryRules`.
|
|
227
233
|
return [];
|
|
228
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Merge the monorepo summary's computed boundary candidates into the
|
|
237
|
+
* inferred boundary-rule list. These candidates are derived from the actual
|
|
238
|
+
* workspace layout (apps / packages / libs), so they are enforceable and
|
|
239
|
+
* should reach the boundaries draft + `init --infer`. Deduped by id;
|
|
240
|
+
* existing entries win.
|
|
241
|
+
*/
|
|
242
|
+
function mergeMonorepoBoundaryRules(base, monorepoSummary) {
|
|
243
|
+
const out = [...base];
|
|
244
|
+
if (!monorepoSummary)
|
|
245
|
+
return out;
|
|
246
|
+
const seen = new Set(out.map((b) => b.id));
|
|
247
|
+
for (const c of monorepoSummary.boundaryCandidates) {
|
|
248
|
+
if (seen.has(c.id))
|
|
249
|
+
continue;
|
|
250
|
+
seen.add(c.id);
|
|
251
|
+
out.push({
|
|
252
|
+
id: c.id,
|
|
253
|
+
title: c.title,
|
|
254
|
+
severity: 'warning',
|
|
255
|
+
from: c.from,
|
|
256
|
+
forbiddenImports: c.forbiddenImports,
|
|
257
|
+
suggestedFix: `Author this boundary in sharkcraft/boundaries.ts: ${c.from.join(', ')} must not import ${c.forbiddenImports.join(', ')}.`,
|
|
258
|
+
reason: c.reason,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
229
263
|
const TEMPLATE_PATTERNS = [
|
|
230
264
|
{
|
|
231
265
|
id: 'inferred.service',
|
|
@@ -319,21 +353,48 @@ export function inferRules(ws) {
|
|
|
319
353
|
reason: evidence,
|
|
320
354
|
});
|
|
321
355
|
}
|
|
322
|
-
// TypeScript.
|
|
356
|
+
// TypeScript. Resolve strict as a tri-state: confirmed-on, confirmed-off,
|
|
357
|
+
// or unknown (the tsconfig extends a base we cannot resolve — e.g. an npm
|
|
358
|
+
// package — so we must NOT assert that strict is off and bake a wrong rule).
|
|
323
359
|
if (ws.hasTypeScript) {
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
: '
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
360
|
+
const ts = ws.tsConfig;
|
|
361
|
+
const strictState = ts && ts.strictResolvable
|
|
362
|
+
? ts.strict === true
|
|
363
|
+
? 'on'
|
|
364
|
+
: 'off'
|
|
365
|
+
: 'unknown';
|
|
366
|
+
if (strictState === 'on') {
|
|
367
|
+
out.push({
|
|
368
|
+
id: 'typescript.strict-mode',
|
|
369
|
+
title: 'TypeScript strict mode enabled',
|
|
370
|
+
content: 'TypeScript strict mode is enabled. Keep it that way — do not weaken `strict` to land code.',
|
|
371
|
+
priority: 'high',
|
|
372
|
+
source: 'tsconfig',
|
|
373
|
+
reason: 'tsconfig strict=true',
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
else if (strictState === 'off') {
|
|
377
|
+
out.push({
|
|
378
|
+
id: 'typescript.strict-mode',
|
|
379
|
+
title: 'Enable TypeScript strict mode',
|
|
380
|
+
content: 'TypeScript strict mode is OFF. Turning it on is a one-line tsconfig change and significantly improves type safety. Plan a separate task for the cleanup.',
|
|
381
|
+
priority: 'medium',
|
|
382
|
+
source: 'tsconfig',
|
|
383
|
+
reason: 'tsconfig strict=false',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
// Unknown: advisory only — do NOT emit the imperative "turn it on"
|
|
388
|
+
// rule (low priority keeps it out of high-confidence auto-adoption).
|
|
389
|
+
out.push({
|
|
390
|
+
id: 'typescript.strict-mode-unconfirmed',
|
|
391
|
+
title: 'Could not confirm TypeScript strict mode — verify manually',
|
|
392
|
+
content: 'TypeScript `strict` is not set directly and the extended base config could not be resolved (it may be an npm package such as `@tsconfig/strictest`). Confirm whether strict is enabled before relying on it; do not assume it is off.',
|
|
393
|
+
priority: 'low',
|
|
394
|
+
source: 'tsconfig',
|
|
395
|
+
reason: 'tsconfig strict unresolved (extends a non-relative / missing base)',
|
|
396
|
+
});
|
|
397
|
+
}
|
|
337
398
|
}
|
|
338
399
|
// Test runner.
|
|
339
400
|
if (ws.profiles.includes(WorkspaceProfile.HasBunTest)) {
|
package/dist/pack-doctor.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type IPackReleaseCheck } from './pack-release-check.js';
|
|
|
3
3
|
export interface IPackDoctorIssue {
|
|
4
4
|
severity: 'error' | 'warning' | 'info';
|
|
5
5
|
packageName: string;
|
|
6
|
-
code: 'invalid-manifest' | 'missing-contribution-file' | 'empty-resolved-contributions' | 'duplicate-id-local' | 'duplicate-id-pack-internal' | 'template-no-description' | 'pipeline-no-steps' | 'critical-rule-no-hints' | 'docs-file-missing' | 'unsigned-pack' | 'tampered-pack' | 'preset-composition-cycle' | 'preset-composed-not-found' | 'preset-missing-ref' | 'preset-no-includes' | 'release-manifest-issue' | 'release-contribution-issue' | 'release-signature-issue' | 'release-files-issue' | 'release-readiness-issue';
|
|
6
|
+
code: 'invalid-manifest' | 'missing-contribution-file' | 'empty-resolved-contributions' | 'duplicate-id-local' | 'duplicate-id-pack-internal' | 'template-no-description' | 'pipeline-no-steps' | 'critical-rule-no-hints' | 'docs-file-missing' | 'unsigned-pack' | 'tampered-pack' | 'signature-unverifiable' | 'dev-signature-not-trusted' | 'preset-composition-cycle' | 'preset-composed-not-found' | 'preset-missing-ref' | 'preset-no-includes' | 'pack-verification-pm-mismatch' | 'release-manifest-issue' | 'release-contribution-issue' | 'release-signature-issue' | 'release-files-issue' | 'release-readiness-issue';
|
|
7
7
|
message: string;
|
|
8
8
|
/** Free-form suggestion. */
|
|
9
9
|
suggestion?: string;
|
|
@@ -25,6 +25,13 @@ export interface IPackDoctorReport {
|
|
|
25
25
|
export interface IPackDoctorOptions {
|
|
26
26
|
/** When true, unsigned packs surface as `unsigned-pack` warnings. */
|
|
27
27
|
requireSignatures?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Opt in to trusting dev signatures. By default a dev-signed pack fails
|
|
30
|
+
* `--require-signatures` with `dev-signature-not-trusted`; when true, dev
|
|
31
|
+
* signatures are re-verified against the well-known dev secret and accepted
|
|
32
|
+
* (a tampered dev signature still surfaces as `tampered-pack`).
|
|
33
|
+
*/
|
|
34
|
+
allowDevSignatures?: boolean;
|
|
28
35
|
/** When true, also run pack-release-check per pack and fold findings into issues. */
|
|
29
36
|
release?: boolean;
|
|
30
37
|
/** When true, release-check warnings escalate to errors. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pack-doctor.d.ts","sourceRoot":"","sources":["../src/pack-doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"pack-doctor.d.ts","sourceRoot":"","sources":["../src/pack-doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAWvE,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEtF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EACA,kBAAkB,GAClB,2BAA2B,GAC3B,8BAA8B,GAC9B,oBAAoB,GACpB,4BAA4B,GAC5B,yBAAyB,GACzB,mBAAmB,GACnB,wBAAwB,GACxB,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,wBAAwB,GACxB,2BAA2B,GAC3B,0BAA0B,GAC1B,2BAA2B,GAC3B,oBAAoB,GACpB,oBAAoB,GACpB,+BAA+B,GAC/B,wBAAwB,GACxB,4BAA4B,GAC5B,yBAAyB,GACzB,qBAAqB,GACrB,yBAAyB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,qFAAqF;IACrF,aAAa,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,qFAAqF;IACrF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAmED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,qBAAqB,EACjC,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAoRnB;AAgBD;wEACwE;AACxE,wBAAsB,6BAA6B,CACjD,UAAU,EAAE,qBAAqB,GAChC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAa9B;AAED;8CAC8C;AAC9C,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,qBAAqB,EACjC,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,SAAS,iBAAiB,EAAE,EAC3C,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,iBAAiB,CAoCnB"}
|
package/dist/pack-doctor.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { hasActionHints } from '@shrkcrft/knowledge';
|
|
2
|
+
import { PackageManager, WorkspaceProfile, } from '@shrkcrft/workspace';
|
|
3
|
+
import { PipelineStepType } from '@shrkcrft/pipelines';
|
|
2
4
|
import { resolvePreset, resolvePresetReferences } from '@shrkcrft/presets';
|
|
5
|
+
import { PACK_SECRET_ENV, verifyPackManifest } from '@shrkcrft/plugin-api';
|
|
3
6
|
import { inspectionReferenceLookup } from "./reference-lookup.js";
|
|
4
7
|
import { runPackReleaseCheck } from "./pack-release-check.js";
|
|
5
8
|
const GEN_KEYWORDS = ['generate', 'create', 'add', 'refactor', 'test', 'review'];
|
|
@@ -15,6 +18,52 @@ function isCriticalOrHigh(e) {
|
|
|
15
18
|
const p = String(e.priority);
|
|
16
19
|
return p === 'critical' || p === 'high';
|
|
17
20
|
}
|
|
21
|
+
/** A leading package-manager / runner literal a pack verification command may
|
|
22
|
+
* bake in. Order longest-first is not required — these tokens are disjoint. */
|
|
23
|
+
const PM_LITERAL_MANAGERS = [
|
|
24
|
+
['bun ', PackageManager.Bun],
|
|
25
|
+
['pnpm ', PackageManager.Pnpm],
|
|
26
|
+
['yarn ', PackageManager.Yarn],
|
|
27
|
+
['npm ', PackageManager.Npm],
|
|
28
|
+
];
|
|
29
|
+
/** The package manager a command hard-codes as its leading runner, or null when
|
|
30
|
+
* it uses none (e.g. `make test`, a `<pm-run> test` placeholder, `shrk ...`). */
|
|
31
|
+
function literalPackageManager(command) {
|
|
32
|
+
const c = command.trim();
|
|
33
|
+
for (const [literal, manager] of PM_LITERAL_MANAGERS) {
|
|
34
|
+
if (c.startsWith(literal))
|
|
35
|
+
return manager;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/** The project's detected package manager (concrete only). Returns Unknown when
|
|
40
|
+
* no lockfile / packageManager field / bun profile signal is present — in that
|
|
41
|
+
* case we cannot claim a mismatch, so the lint stays silent. */
|
|
42
|
+
function detectedProjectManager(ws) {
|
|
43
|
+
const m = ws?.packageManager?.manager;
|
|
44
|
+
if (m && m !== PackageManager.Unknown)
|
|
45
|
+
return m;
|
|
46
|
+
if (ws?.profiles?.includes(WorkspaceProfile.HasBun))
|
|
47
|
+
return PackageManager.Bun;
|
|
48
|
+
return PackageManager.Unknown;
|
|
49
|
+
}
|
|
50
|
+
/** Push a warning when `command` bakes in a runner that disagrees with the
|
|
51
|
+
* project's detected toolchain. No-op when the command is templated, runner-
|
|
52
|
+
* agnostic, or already agrees with the detected manager. */
|
|
53
|
+
function pushPmMismatch(issues, packageName, origin, command, detected) {
|
|
54
|
+
if (detected === PackageManager.Unknown)
|
|
55
|
+
return;
|
|
56
|
+
const literal = literalPackageManager(command);
|
|
57
|
+
if (!literal || literal === detected)
|
|
58
|
+
return;
|
|
59
|
+
issues.push({
|
|
60
|
+
severity: 'warning',
|
|
61
|
+
packageName,
|
|
62
|
+
code: 'pack-verification-pm-mismatch',
|
|
63
|
+
message: `Verification command in ${origin} hard-codes \`${literal}\` but this project uses \`${detected}\`: "${command}".`,
|
|
64
|
+
suggestion: 'Use the `<pm-run>`/`<pm>` placeholder (resolved to the detected package manager at consume time) instead of a hard-coded runner.',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
18
67
|
/**
|
|
19
68
|
* Full structural + quality audit of every discovered pack. Returns a list of
|
|
20
69
|
* issues (error / warning / info) plus an aggregated pass flag.
|
|
@@ -28,6 +77,7 @@ export function buildPackDoctorReport(inspection, options = {}) {
|
|
|
28
77
|
const localKnowledgeIds = new Set(inspection.knowledgeEntries
|
|
29
78
|
.filter((e) => inspection.entrySources.get(e.id)?.type === 'local')
|
|
30
79
|
.map((e) => e.id));
|
|
80
|
+
const detectedPm = detectedProjectManager(inspection.workspace);
|
|
31
81
|
for (const pack of inspection.packs.invalidPacks) {
|
|
32
82
|
issues.push({
|
|
33
83
|
severity: 'error',
|
|
@@ -116,6 +166,30 @@ export function buildPackDoctorReport(inspection, options = {}) {
|
|
|
116
166
|
});
|
|
117
167
|
}
|
|
118
168
|
}
|
|
169
|
+
// Verification commands that bake in a *foreign* package manager / runner.
|
|
170
|
+
// A pack playbook that ships `bun test` contradicts an npm/pnpm/yarn target;
|
|
171
|
+
// prefer the `<pm-run>`/`<pm>` placeholder. Warning, never a hard fail.
|
|
172
|
+
for (const entry of inspection.knowledgeEntries) {
|
|
173
|
+
const src = inspection.entrySources.get(entry.id);
|
|
174
|
+
if (src?.type !== 'pack' || src.packageName !== pack.packageName)
|
|
175
|
+
continue;
|
|
176
|
+
const hints = entry.actionHints;
|
|
177
|
+
for (const cmd of hints?.verificationCommands ?? []) {
|
|
178
|
+
pushPmMismatch(issues, pack.packageName, `knowledge "${entry.id}"`, cmd, detectedPm);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const p of inspection.pipelines) {
|
|
182
|
+
const src = inspection.pipelineSources.get(p.id);
|
|
183
|
+
if (src?.type !== 'pack' || src.packageName !== pack.packageName)
|
|
184
|
+
continue;
|
|
185
|
+
for (const step of p.steps ?? []) {
|
|
186
|
+
if (step.type !== PipelineStepType.Command)
|
|
187
|
+
continue;
|
|
188
|
+
for (const cmd of step.cliCommands ?? []) {
|
|
189
|
+
pushPmMismatch(issues, pack.packageName, `pipeline "${p.id}" step "${step.id}"`, cmd, detectedPm);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
119
193
|
// Local-vs-pack duplicate ids.
|
|
120
194
|
for (const entry of inspection.knowledgeEntries) {
|
|
121
195
|
const src = inspection.entrySources.get(entry.id);
|
|
@@ -131,9 +205,18 @@ export function buildPackDoctorReport(inspection, options = {}) {
|
|
|
131
205
|
}
|
|
132
206
|
}
|
|
133
207
|
// Signature gating.
|
|
208
|
+
//
|
|
209
|
+
// The inspector verifies with dev signatures DISALLOWED, so a dev-signed
|
|
210
|
+
// pack arrives here as `dev-signature`. With --allow-dev-signature we
|
|
211
|
+
// re-run the HMAC against the well-known dev secret so a *tampered* dev
|
|
212
|
+
// signature still surfaces as invalid rather than being blindly trusted.
|
|
213
|
+
let sigStatus = pack.signatureStatus;
|
|
214
|
+
if (sigStatus === 'dev-signature' && options.allowDevSignatures && pack.manifest) {
|
|
215
|
+
const v = verifyPackManifest(pack.manifest, { allowDev: true });
|
|
216
|
+
sigStatus = v.ok ? 'verified' : v.status;
|
|
217
|
+
}
|
|
134
218
|
if (options.requireSignatures) {
|
|
135
|
-
|
|
136
|
-
if (!status || status === 'missing-signature' || status === 'not-checked') {
|
|
219
|
+
if (!sigStatus || sigStatus === 'missing-signature' || sigStatus === 'not-checked') {
|
|
137
220
|
issues.push({
|
|
138
221
|
severity: 'warning',
|
|
139
222
|
packageName: pack.packageName,
|
|
@@ -142,8 +225,32 @@ export function buildPackDoctorReport(inspection, options = {}) {
|
|
|
142
225
|
suggestion: 'Run `shrk packs sign <pack-dir>` and ship the signed manifest.',
|
|
143
226
|
});
|
|
144
227
|
}
|
|
228
|
+
else if (sigStatus === 'missing-secret') {
|
|
229
|
+
// S3-2: required verification that COULD NOT RUN is a failure, not a
|
|
230
|
+
// pass. A signed pack whose secret is unavailable is unverifiable —
|
|
231
|
+
// never report it as OK.
|
|
232
|
+
issues.push({
|
|
233
|
+
severity: 'error',
|
|
234
|
+
packageName: pack.packageName,
|
|
235
|
+
code: 'signature-unverifiable',
|
|
236
|
+
message: `Pack is signed but ${PACK_SECRET_ENV} is not set, so the signature could not be verified — required verification failed.`,
|
|
237
|
+
suggestion: `Set ${PACK_SECRET_ENV} (or pass --secret) and re-run with --require-signatures.`,
|
|
238
|
+
suggestedCommand: `${PACK_SECRET_ENV}=<secret> shrk packs doctor --require-signatures`,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
else if (sigStatus === 'dev-signature') {
|
|
242
|
+
// S3-1: a dev signature is verified only against the public dev secret
|
|
243
|
+
// and is NOT release-trusted; under --require-signatures it must fail.
|
|
244
|
+
issues.push({
|
|
245
|
+
severity: 'error',
|
|
246
|
+
packageName: pack.packageName,
|
|
247
|
+
code: 'dev-signature-not-trusted',
|
|
248
|
+
message: 'Pack carries a dev signature (not release-trusted) and --require-signatures was set.',
|
|
249
|
+
suggestion: 'Re-sign with the release secret (`shrk packs sign <pack-dir>`), or pass --allow-dev-signature to accept dev signatures for local-only flows.',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
145
252
|
}
|
|
146
|
-
if (
|
|
253
|
+
if (sigStatus === 'invalid-signature') {
|
|
147
254
|
issues.push({
|
|
148
255
|
severity: 'error',
|
|
149
256
|
packageName: pack.packageName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pack-quality-score.d.ts","sourceRoot":"","sources":["../src/pack-quality-score.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pack-quality-score.d.ts","sourceRoot":"","sources":["../src/pack-quality-score.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAIvE,eAAO,MAAM,mBAAmB,qCAAqC,CAAC;AACtE,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,wBAAwB,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9F,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,iBAAiB,GAC1B,gBAAgB,CA2BlB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,aAAa;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,mBAAmB,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC7C,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAMD,wBAAgB,SAAS,CACvB,UAAU,EAAE,qBAAqB,EACjC,IAAI,EAAE,eAAe,GACpB,iBAAiB,CAyInB;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,qBAAqB,GAAG,iBAAiB,EAAE,CAEpF"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CONTRIBUTION_FILE_KEYS } from '@shrkcrft/plugin-api';
|
|
2
|
+
import { hasMeaningfulActionHints, KNOWLEDGE_TYPES_NO_ACTION, } from '@shrkcrft/knowledge';
|
|
1
3
|
import { lintTemplates } from "./template-lint.js";
|
|
2
4
|
import { lintPipelines } from "./pipeline-lint.js";
|
|
3
5
|
export const PACK_QUALITY_SCHEMA = 'sharkcraft.pack-quality-score/v1';
|
|
@@ -68,14 +70,12 @@ export function scorePack(inspection, pack) {
|
|
|
68
70
|
scaffoldPatterns: 0,
|
|
69
71
|
policyChecks: 0,
|
|
70
72
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
pack.contributionCounts.presetFiles +
|
|
78
|
-
pack.contributionCounts.scaffoldPatternFiles;
|
|
73
|
+
// Sum EVERY canonical contribution kind (not just the original 8) so a pack
|
|
74
|
+
// that contributes only "extended" kinds (conventions / helpers / framework
|
|
75
|
+
// extractors / …) is recognised as declaring contributions instead of being
|
|
76
|
+
// scored as "Pack declares no contributions".
|
|
77
|
+
const declaredCounts = pack.contributionCounts;
|
|
78
|
+
const totalDeclared = CONTRIBUTION_FILE_KEYS.reduce((sum, key) => sum + (declaredCounts[key] ?? 0), 0);
|
|
79
79
|
const totalResolved = resolved.knowledgeEntries +
|
|
80
80
|
resolved.rules +
|
|
81
81
|
resolved.pathConventions +
|
|
@@ -118,15 +118,20 @@ export function scorePack(inspection, pack) {
|
|
|
118
118
|
weight: 0.7,
|
|
119
119
|
notes: pipFromPack.length === 0 ? ['No pipelines contributed'] : [],
|
|
120
120
|
});
|
|
121
|
-
// Action hints presence
|
|
121
|
+
// Action hints QUALITY (not mere presence): score only entries that carry a
|
|
122
|
+
// substantive, non-templated hint, and exempt purely-descriptive types
|
|
123
|
+
// (business/decision) from the denominator so a pack can't game the gate with
|
|
124
|
+
// hollow metadata on context-only entries.
|
|
122
125
|
const packEntries = inspection.knowledgeEntries.filter((e) => inspection.entrySources.get(e.id)?.packageName === pack.packageName);
|
|
123
|
-
const
|
|
126
|
+
const actionable = packEntries.filter((e) => e.noAction !== true && !KNOWLEDGE_TYPES_NO_ACTION.has(e.type));
|
|
127
|
+
const entriesWithMeaningfulHints = actionable.filter((e) => hasMeaningfulActionHints(e)).length;
|
|
128
|
+
const hollow = actionable.length - entriesWithMeaningfulHints;
|
|
124
129
|
dims.push({
|
|
125
130
|
id: 'action-hints',
|
|
126
|
-
label: 'Action hints',
|
|
127
|
-
score:
|
|
131
|
+
label: 'Action hints quality',
|
|
132
|
+
score: actionable.length === 0 ? 70 : Math.round((entriesWithMeaningfulHints / actionable.length) * 100),
|
|
128
133
|
weight: 0.6,
|
|
129
|
-
notes: [],
|
|
134
|
+
notes: hollow > 0 ? [`${hollow} entry(ies) lack a meaningful (non-templated) actionHint`] : [],
|
|
130
135
|
});
|
|
131
136
|
// Duplicate ids guard.
|
|
132
137
|
dims.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pack-release-check.d.ts","sourceRoot":"","sources":["../src/pack-release-check.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pack-release-check.d.ts","sourceRoot":"","sources":["../src/pack-release-check.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,yBAAyB,qCAAqC,CAAC;AAE5E,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,yBAAyB,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,MAAM,EAAE,OAAO,CAAC;CACjB;AA6ED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoLtF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import * as nodePath from 'node:path';
|
|
3
|
-
import { validatePackManifest } from '@shrkcrft/plugin-api';
|
|
3
|
+
import { CONTRIBUTION_FILE_KEYS, validatePackManifest, } from '@shrkcrft/plugin-api';
|
|
4
4
|
import { importModuleViaLoader } from '@shrkcrft/core';
|
|
5
5
|
export const PACK_RELEASE_CHECK_SCHEMA = 'sharkcraft.pack-release-check/v1';
|
|
6
6
|
function findManifest(packPath) {
|
|
@@ -158,27 +158,17 @@ export async function runPackReleaseCheck(packPath) {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
// Contribution file existence + load.
|
|
161
|
+
//
|
|
162
|
+
// Iterate the canonical CONTRIBUTION_FILE_KEYS (single source of truth in
|
|
163
|
+
// @shrkcrft/plugin-api) so EVERY consuming contribution slot is verified — a
|
|
164
|
+
// pack shipping a broken framework extractor / convention / helper / decision
|
|
165
|
+
// / etc. now fails the gate instead of slipping through. The two `Future`
|
|
166
|
+
// no-op slots (mcpToolFiles / aiProviderFiles) are intentionally excluded via
|
|
167
|
+
// FUTURE_CONTRIBUTION_FILE_KEYS: nothing loads them yet, so verifying them
|
|
168
|
+
// would be dishonest.
|
|
161
169
|
let contributionsFound = 0;
|
|
162
170
|
const c = manifest.contributions ?? {};
|
|
163
|
-
const
|
|
164
|
-
'knowledgeFiles',
|
|
165
|
-
'ruleFiles',
|
|
166
|
-
'pathFiles',
|
|
167
|
-
'templateFiles',
|
|
168
|
-
'pipelineFiles',
|
|
169
|
-
'docsFiles',
|
|
170
|
-
'presetFiles',
|
|
171
|
-
'boundaryFiles',
|
|
172
|
-
'contextTestFiles',
|
|
173
|
-
'agentTestFiles',
|
|
174
|
-
'scaffoldPatternFiles',
|
|
175
|
-
'policyCheckFiles',
|
|
176
|
-
'constructFiles',
|
|
177
|
-
'constructFacetFiles',
|
|
178
|
-
'playbookFiles',
|
|
179
|
-
'searchTuningFiles',
|
|
180
|
-
];
|
|
181
|
-
for (const key of allBuckets) {
|
|
171
|
+
for (const key of CONTRIBUTION_FILE_KEYS) {
|
|
182
172
|
const list = c[key];
|
|
183
173
|
if (!list || list.length === 0)
|
|
184
174
|
continue;
|
|
@@ -234,6 +224,19 @@ export async function runPackReleaseCheck(packPath) {
|
|
|
234
224
|
file: manifestFile,
|
|
235
225
|
});
|
|
236
226
|
}
|
|
227
|
+
else if (manifest.signature.dev === true) {
|
|
228
|
+
// Dev signatures verify only against the well-known public dev secret and
|
|
229
|
+
// are NOT release-trusted — shipping one in a release is almost certainly a
|
|
230
|
+
// mistake. Warn (not a hard error) so the author re-signs before tagging.
|
|
231
|
+
findings.push({
|
|
232
|
+
code: 'dev-signature',
|
|
233
|
+
severity: 'warning',
|
|
234
|
+
message: 'Manifest carries a dev signature (sig.dev = true) — verified only against the public dev secret and NOT release-trusted. Re-sign with the release secret before publishing.',
|
|
235
|
+
suggestedFix: 'Run `shrk packs sign <manifest.ts>` with SHARKCRAFT_PACK_SECRET set (no --dev) and ship the re-signed manifest.',
|
|
236
|
+
suggestedCommand: 'shrk packs sign ' + manifestFile + ' --verify-after-sign',
|
|
237
|
+
file: manifestFile,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
237
240
|
// Files whitelist sanity.
|
|
238
241
|
const filesFinding = checkFilesWhitelist(pkgFile, manifestFile);
|
|
239
242
|
if (filesFinding)
|