@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.
Files changed (65) hide show
  1. package/dist/command-recommender.d.ts.map +1 -1
  2. package/dist/command-recommender.js +110 -1
  3. package/dist/context-tuning.d.ts +14 -0
  4. package/dist/context-tuning.d.ts.map +1 -0
  5. package/dist/context-tuning.js +38 -0
  6. package/dist/coverage-report.d.ts.map +1 -1
  7. package/dist/coverage-report.js +31 -8
  8. package/dist/git-helpers.d.ts.map +1 -1
  9. package/dist/git-helpers.js +10 -1
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +2 -0
  13. package/dist/knowledge-stale.d.ts +23 -0
  14. package/dist/knowledge-stale.d.ts.map +1 -1
  15. package/dist/knowledge-stale.js +58 -9
  16. package/dist/monorepo-onboarding.d.ts.map +1 -1
  17. package/dist/monorepo-onboarding.js +122 -12
  18. package/dist/onboarding.d.ts.map +1 -1
  19. package/dist/onboarding.js +79 -18
  20. package/dist/pack-doctor.d.ts +8 -1
  21. package/dist/pack-doctor.d.ts.map +1 -1
  22. package/dist/pack-doctor.js +110 -3
  23. package/dist/pack-quality-score.d.ts.map +1 -1
  24. package/dist/pack-quality-score.js +18 -13
  25. package/dist/pack-release-check.d.ts.map +1 -1
  26. package/dist/pack-release-check.js +23 -20
  27. package/dist/pack-signature-status.d.ts +9 -3
  28. package/dist/pack-signature-status.d.ts.map +1 -1
  29. package/dist/pack-signature-status.js +13 -2
  30. package/dist/plan-simulation.d.ts.map +1 -1
  31. package/dist/plan-simulation.js +26 -12
  32. package/dist/registry-lifecycle.d.ts +8 -0
  33. package/dist/registry-lifecycle.d.ts.map +1 -1
  34. package/dist/registry-lifecycle.js +10 -2
  35. package/dist/resolve-project-config.d.ts +26 -0
  36. package/dist/resolve-project-config.d.ts.map +1 -0
  37. package/dist/resolve-project-config.js +136 -0
  38. package/dist/resolve-verification-commands.d.ts +7 -0
  39. package/dist/resolve-verification-commands.d.ts.map +1 -1
  40. package/dist/resolve-verification-commands.js +53 -4
  41. package/dist/review-packet.d.ts +8 -0
  42. package/dist/review-packet.d.ts.map +1 -1
  43. package/dist/review-packet.js +11 -12
  44. package/dist/sharkcraft-inspector.d.ts.map +1 -1
  45. package/dist/sharkcraft-inspector.js +28 -2
  46. package/dist/spec/__tests__/spec-evidence.test.d.ts +2 -0
  47. package/dist/spec/__tests__/spec-evidence.test.d.ts.map +1 -0
  48. package/dist/spec/__tests__/spec-evidence.test.js +81 -0
  49. package/dist/spec/spec-evidence.d.ts +62 -0
  50. package/dist/spec/spec-evidence.d.ts.map +1 -0
  51. package/dist/spec/spec-evidence.js +215 -0
  52. package/dist/spec/spec-review.d.ts +1 -0
  53. package/dist/spec/spec-review.d.ts.map +1 -1
  54. package/dist/spec/spec-review.js +5 -0
  55. package/dist/symbol-index.d.ts +9 -0
  56. package/dist/symbol-index.d.ts.map +1 -1
  57. package/dist/symbol-index.js +6 -0
  58. package/dist/task-packet.d.ts.map +1 -1
  59. package/dist/task-packet.js +6 -1
  60. package/dist/task-ranker.d.ts +2 -1
  61. package/dist/task-ranker.d.ts.map +1 -1
  62. package/dist/task-ranker.js +30 -5
  63. package/dist/test-runner.d.ts.map +1 -1
  64. package/dist/test-runner.js +3 -0
  65. 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
- for (const g of GROUPS) {
35
- const children = subDirs.get(g.dir) ?? [];
36
- for (const child of children) {
37
- if (scanned >= PACKAGE_LIMIT)
38
- break;
39
- const rel = nodePath.posix.join(g.dir, child);
40
- const full = nodePath.join(ws.projectRoot, g.dir, child);
41
- if (!statSafeIsDir(full))
42
- continue;
43
- groups[g.key].push(describePackage(full, rel, g.key));
44
- scanned += 1;
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
- out.push('Monorepo signals detected but no apps/packages/libs directories keep onboarding bounded.');
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
  }
@@ -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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"}
@@ -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. Author them
225
- // explicitly in `sharkcraft/boundaries.ts` once the repo's actual
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 strict = !!ws.tsConfig?.strict;
325
- out.push({
326
- id: 'typescript.strict-mode',
327
- title: strict
328
- ? 'TypeScript strict mode enabled'
329
- : 'Enable TypeScript strict mode',
330
- content: strict
331
- ? 'TypeScript strict mode is enabled. Keep it that way — do not weaken `strict` to land code.'
332
- : '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.',
333
- priority: strict ? 'high' : 'medium',
334
- source: 'tsconfig',
335
- reason: `tsconfig strict=${strict}`,
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)) {
@@ -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;AAIvE,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,0BAA0B,GAC1B,2BAA2B,GAC3B,oBAAoB,GACpB,oBAAoB,GACpB,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,qFAAqF;IACrF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAeD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,qBAAqB,EACjC,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAqNnB;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"}
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"}
@@ -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
- const status = pack.signatureStatus;
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 (pack.signatureStatus === 'invalid-signature') {
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":"AAAA,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,CAmInB;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,qBAAqB,GAAG,iBAAiB,EAAE,CAEpF"}
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
- const totalDeclared = pack.contributionCounts.knowledgeFiles +
72
- pack.contributionCounts.ruleFiles +
73
- pack.contributionCounts.pathFiles +
74
- pack.contributionCounts.templateFiles +
75
- pack.contributionCounts.pipelineFiles +
76
- pack.contributionCounts.docsFiles +
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 entriesWithHints = packEntries.filter((e) => e.actionHints !== undefined).length;
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: packEntries.length === 0 ? 70 : Math.round((entriesWithHints / packEntries.length) * 100),
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":"AAKA,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,CAgLtF"}
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 allBuckets = [
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)