@shrkcrft/cli 0.1.0-alpha.30 → 0.1.0-alpha.31

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 (510) hide show
  1. package/dist/asset-preview/apply-asset-preview.js +1 -1
  2. package/dist/authoring/authoring-kit.d.ts +9 -1
  3. package/dist/authoring/authoring-kit.d.ts.map +1 -1
  4. package/dist/authoring/authoring-kit.js +26 -13
  5. package/dist/bootstrap/unlinked-workspace-dependency.d.ts +11 -0
  6. package/dist/bootstrap/unlinked-workspace-dependency.d.ts.map +1 -0
  7. package/dist/bootstrap/unlinked-workspace-dependency.js +99 -0
  8. package/dist/command-registry.d.ts +100 -1
  9. package/dist/command-registry.d.ts.map +1 -1
  10. package/dist/command-registry.js +130 -3
  11. package/dist/commands/api-diff.command.d.ts.map +1 -1
  12. package/dist/commands/api-diff.command.js +12 -0
  13. package/dist/commands/apply.command.d.ts.map +1 -1
  14. package/dist/commands/apply.command.js +3 -0
  15. package/dist/commands/arch.command.d.ts.map +1 -1
  16. package/dist/commands/arch.command.js +26 -7
  17. package/dist/commands/architecture.command.d.ts.map +1 -1
  18. package/dist/commands/architecture.command.js +8 -2
  19. package/dist/commands/baseline.command.d.ts +54 -3
  20. package/dist/commands/baseline.command.d.ts.map +1 -1
  21. package/dist/commands/baseline.command.js +582 -74
  22. package/dist/commands/biome.command.d.ts.map +1 -1
  23. package/dist/commands/biome.command.js +20 -0
  24. package/dist/commands/boundaries.command.d.ts +9 -1
  25. package/dist/commands/boundaries.command.d.ts.map +1 -1
  26. package/dist/commands/boundaries.command.js +88 -20
  27. package/dist/commands/bundle.command.d.ts.map +1 -1
  28. package/dist/commands/bundle.command.js +57 -11
  29. package/dist/commands/cache-align.command.d.ts.map +1 -1
  30. package/dist/commands/cache-align.command.js +3 -1
  31. package/dist/commands/changelog-data.d.ts.map +1 -1
  32. package/dist/commands/changelog-data.js +138 -0
  33. package/dist/commands/changes.command.d.ts.map +1 -1
  34. package/dist/commands/changes.command.js +15 -0
  35. package/dist/commands/check.command.d.ts.map +1 -1
  36. package/dist/commands/check.command.js +1241 -322
  37. package/dist/commands/checks.command.d.ts.map +1 -1
  38. package/dist/commands/checks.command.js +188 -40
  39. package/dist/commands/ci.command.d.ts.map +1 -1
  40. package/dist/commands/ci.command.js +35 -2
  41. package/dist/commands/code-intel.command.d.ts.map +1 -1
  42. package/dist/commands/code-intel.command.js +3 -0
  43. package/dist/commands/command-catalog.d.ts +23 -1
  44. package/dist/commands/command-catalog.d.ts.map +1 -1
  45. package/dist/commands/command-catalog.js +152 -35
  46. package/dist/commands/commands.command.d.ts +21 -3
  47. package/dist/commands/commands.command.d.ts.map +1 -1
  48. package/dist/commands/commands.command.js +172 -27
  49. package/dist/commands/compress.command.d.ts.map +1 -1
  50. package/dist/commands/compress.command.js +4 -0
  51. package/dist/commands/constructs.command.d.ts.map +1 -1
  52. package/dist/commands/constructs.command.js +50 -12
  53. package/dist/commands/context.command.d.ts.map +1 -1
  54. package/dist/commands/context.command.js +40 -5
  55. package/dist/commands/contract-templates.command.d.ts.map +1 -1
  56. package/dist/commands/contract-templates.command.js +9 -1
  57. package/dist/commands/contract.command.d.ts.map +1 -1
  58. package/dist/commands/contract.command.js +35 -0
  59. package/dist/commands/conventions.command.d.ts.map +1 -1
  60. package/dist/commands/conventions.command.js +189 -29
  61. package/dist/commands/coverage.command.d.ts.map +1 -1
  62. package/dist/commands/coverage.command.js +9 -0
  63. package/dist/commands/daily.commands.d.ts +0 -1
  64. package/dist/commands/daily.commands.d.ts.map +1 -1
  65. package/dist/commands/daily.commands.js +12 -76
  66. package/dist/commands/dashboard.command.d.ts.map +1 -1
  67. package/dist/commands/dashboard.command.js +10 -0
  68. package/dist/commands/dev.command.d.ts.map +1 -1
  69. package/dist/commands/dev.command.js +45 -2
  70. package/dist/commands/diagnostics.command.d.ts +0 -2
  71. package/dist/commands/diagnostics.command.d.ts.map +1 -1
  72. package/dist/commands/diagnostics.command.js +5 -78
  73. package/dist/commands/diff-check.command.d.ts +6 -4
  74. package/dist/commands/diff-check.command.d.ts.map +1 -1
  75. package/dist/commands/diff-check.command.js +190 -103
  76. package/dist/commands/docs-references.command.d.ts +13 -0
  77. package/dist/commands/docs-references.command.d.ts.map +1 -1
  78. package/dist/commands/docs-references.command.js +269 -72
  79. package/dist/commands/doctor.command.d.ts.map +1 -1
  80. package/dist/commands/doctor.command.js +58 -4
  81. package/dist/commands/drift.command.d.ts.map +1 -1
  82. package/dist/commands/drift.command.js +17 -4
  83. package/dist/commands/eslint.command.d.ts.map +1 -1
  84. package/dist/commands/eslint.command.js +20 -0
  85. package/dist/commands/export.command.d.ts.map +1 -1
  86. package/dist/commands/export.command.js +23 -1
  87. package/dist/commands/feedback-dispatch.command.d.ts.map +1 -1
  88. package/dist/commands/feedback-dispatch.command.js +29 -0
  89. package/dist/commands/feedback.command.d.ts.map +1 -1
  90. package/dist/commands/feedback.command.js +6 -1
  91. package/dist/commands/finish.command.d.ts.map +1 -1
  92. package/dist/commands/finish.command.js +32 -8
  93. package/dist/commands/fix.command.d.ts.map +1 -1
  94. package/dist/commands/fix.command.js +19 -0
  95. package/dist/commands/gate.command.d.ts +6 -1
  96. package/dist/commands/gate.command.d.ts.map +1 -1
  97. package/dist/commands/gate.command.js +50 -124
  98. package/dist/commands/gates.command.d.ts +52 -0
  99. package/dist/commands/gates.command.d.ts.map +1 -1
  100. package/dist/commands/gates.command.js +811 -185
  101. package/dist/commands/gen.command.d.ts.map +1 -1
  102. package/dist/commands/gen.command.js +4 -0
  103. package/dist/commands/generated.command.d.ts +33 -3
  104. package/dist/commands/generated.command.d.ts.map +1 -1
  105. package/dist/commands/generated.command.js +499 -147
  106. package/dist/commands/graph-code-subverbs.d.ts +15 -0
  107. package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
  108. package/dist/commands/graph-code-subverbs.js +198 -24
  109. package/dist/commands/graph.command.d.ts.map +1 -1
  110. package/dist/commands/graph.command.js +79 -37
  111. package/dist/commands/help.command.d.ts +30 -6
  112. package/dist/commands/help.command.d.ts.map +1 -1
  113. package/dist/commands/help.command.js +323 -199
  114. package/dist/commands/helper.command.d.ts +1 -0
  115. package/dist/commands/helper.command.d.ts.map +1 -1
  116. package/dist/commands/helper.command.js +195 -56
  117. package/dist/commands/impact.command.d.ts.map +1 -1
  118. package/dist/commands/impact.command.js +115 -12
  119. package/dist/commands/infer.command.d.ts.map +1 -1
  120. package/dist/commands/infer.command.js +20 -45
  121. package/dist/commands/ingest.command.d.ts +0 -1
  122. package/dist/commands/ingest.command.d.ts.map +1 -1
  123. package/dist/commands/ingest.command.js +25 -41
  124. package/dist/commands/knowledge-author.command.d.ts +0 -3
  125. package/dist/commands/knowledge-author.command.d.ts.map +1 -1
  126. package/dist/commands/knowledge-author.command.js +10 -23
  127. package/dist/commands/knowledge.command.d.ts.map +1 -1
  128. package/dist/commands/knowledge.command.js +469 -129
  129. package/dist/commands/languages.command.d.ts.map +1 -1
  130. package/dist/commands/languages.command.js +27 -0
  131. package/dist/commands/lint.command.d.ts.map +1 -1
  132. package/dist/commands/lint.command.js +3 -0
  133. package/dist/commands/mcp.command.d.ts +18 -0
  134. package/dist/commands/mcp.command.d.ts.map +1 -1
  135. package/dist/commands/mcp.command.js +33 -8
  136. package/dist/commands/onboard.command.d.ts.map +1 -1
  137. package/dist/commands/onboard.command.js +20 -0
  138. package/dist/commands/owners.command.d.ts.map +1 -1
  139. package/dist/commands/owners.command.js +14 -2
  140. package/dist/commands/packs-new.d.ts +13 -1
  141. package/dist/commands/packs-new.d.ts.map +1 -1
  142. package/dist/commands/packs-new.js +500 -335
  143. package/dist/commands/packs.command.d.ts.map +1 -1
  144. package/dist/commands/packs.command.js +378 -36
  145. package/dist/commands/paths.command.d.ts.map +1 -1
  146. package/dist/commands/paths.command.js +9 -1
  147. package/dist/commands/pipelines.command.d.ts.map +1 -1
  148. package/dist/commands/pipelines.command.js +7 -1
  149. package/dist/commands/plan-check.command.d.ts.map +1 -1
  150. package/dist/commands/plan-check.command.js +3 -0
  151. package/dist/commands/plan.command.d.ts.map +1 -1
  152. package/dist/commands/plan.command.js +3 -0
  153. package/dist/commands/playbooks.command.d.ts.map +1 -1
  154. package/dist/commands/playbooks.command.js +7 -2
  155. package/dist/commands/policy-lint.command.d.ts +19 -2
  156. package/dist/commands/policy-lint.command.d.ts.map +1 -1
  157. package/dist/commands/policy-lint.command.js +224 -65
  158. package/dist/commands/policy.command.d.ts.map +1 -1
  159. package/dist/commands/policy.command.js +6 -1
  160. package/dist/commands/pr.command.d.ts.map +1 -1
  161. package/dist/commands/pr.command.js +14 -0
  162. package/dist/commands/presets.command.d.ts.map +1 -1
  163. package/dist/commands/presets.command.js +12 -27
  164. package/dist/commands/profiles.command.d.ts.map +1 -1
  165. package/dist/commands/profiles.command.js +94 -26
  166. package/dist/commands/provenance.command.js +1 -1
  167. package/dist/commands/quality.command.d.ts.map +1 -1
  168. package/dist/commands/quality.command.js +129 -33
  169. package/dist/commands/recommend.command.d.ts +20 -4
  170. package/dist/commands/recommend.command.d.ts.map +1 -1
  171. package/dist/commands/recommend.command.js +172 -278
  172. package/dist/commands/registrations.command.d.ts.map +1 -1
  173. package/dist/commands/registrations.command.js +138 -13
  174. package/dist/commands/registry-lifecycle-run.d.ts +14 -0
  175. package/dist/commands/registry-lifecycle-run.d.ts.map +1 -0
  176. package/dist/commands/registry-lifecycle-run.js +336 -0
  177. package/dist/commands/registry.command.d.ts.map +1 -1
  178. package/dist/commands/registry.command.js +235 -62
  179. package/dist/commands/release.command.d.ts.map +1 -1
  180. package/dist/commands/release.command.js +99 -20
  181. package/dist/commands/report.command.d.ts.map +1 -1
  182. package/dist/commands/report.command.js +38 -2
  183. package/dist/commands/reuse-coverage.command.d.ts +23 -0
  184. package/dist/commands/reuse-coverage.command.d.ts.map +1 -0
  185. package/dist/commands/reuse-coverage.command.js +536 -0
  186. package/dist/commands/reuse.command.d.ts +3 -18
  187. package/dist/commands/reuse.command.d.ts.map +1 -1
  188. package/dist/commands/reuse.command.js +395 -244
  189. package/dist/commands/review.command.d.ts.map +1 -1
  190. package/dist/commands/review.command.js +33 -1
  191. package/dist/commands/rounds.command.d.ts.map +1 -1
  192. package/dist/commands/rounds.command.js +5 -0
  193. package/dist/commands/safety.command.d.ts.map +1 -1
  194. package/dist/commands/safety.command.js +9 -0
  195. package/dist/commands/scaffolds.command.d.ts.map +1 -1
  196. package/dist/commands/scaffolds.command.js +97 -25
  197. package/dist/commands/search.command.d.ts +0 -8
  198. package/dist/commands/search.command.d.ts.map +1 -1
  199. package/dist/commands/search.command.js +136 -28
  200. package/dist/commands/self-config-xrefs.command.d.ts +24 -0
  201. package/dist/commands/self-config-xrefs.command.d.ts.map +1 -0
  202. package/dist/commands/self-config-xrefs.command.js +170 -0
  203. package/dist/commands/self-config.command.d.ts.map +1 -1
  204. package/dist/commands/self-config.command.js +261 -65
  205. package/dist/commands/smart-context.command.d.ts.map +1 -1
  206. package/dist/commands/smart-context.command.js +39 -3
  207. package/dist/commands/spec.command.d.ts.map +1 -1
  208. package/dist/commands/spec.command.js +3 -0
  209. package/dist/commands/stats.command.d.ts.map +1 -1
  210. package/dist/commands/stats.command.js +3 -0
  211. package/dist/commands/surface.command.d.ts +6 -1
  212. package/dist/commands/surface.command.d.ts.map +1 -1
  213. package/dist/commands/surface.command.js +256 -54
  214. package/dist/commands/task-context.command.js +1 -1
  215. package/dist/commands/task.command.d.ts.map +1 -1
  216. package/dist/commands/task.command.js +22 -1
  217. package/dist/commands/templates.command.d.ts +4 -0
  218. package/dist/commands/templates.command.d.ts.map +1 -1
  219. package/dist/commands/templates.command.js +109 -22
  220. package/dist/commands/test.command.d.ts.map +1 -1
  221. package/dist/commands/test.command.js +161 -33
  222. package/dist/commands/tests.command.d.ts.map +1 -1
  223. package/dist/commands/tests.command.js +29 -3
  224. package/dist/commands/trace.command.d.ts.map +1 -1
  225. package/dist/commands/trace.command.js +29 -4
  226. package/dist/commands/watch.command.d.ts.map +1 -1
  227. package/dist/commands/watch.command.js +3 -0
  228. package/dist/commands/why.command.d.ts.map +1 -1
  229. package/dist/commands/why.command.js +3 -0
  230. package/dist/commands/wiring.command.d.ts +18 -4
  231. package/dist/commands/wiring.command.d.ts.map +1 -1
  232. package/dist/commands/wiring.command.js +360 -73
  233. package/dist/dashboard/code-intelligence-data.js +4 -4
  234. package/dist/diff/deleted-orphan-coverage.d.ts +32 -0
  235. package/dist/diff/deleted-orphan-coverage.d.ts.map +1 -0
  236. package/dist/diff/deleted-orphan-coverage.js +107 -0
  237. package/dist/diff/deleted-orphan-scope-notes.d.ts +23 -0
  238. package/dist/diff/deleted-orphan-scope-notes.d.ts.map +1 -0
  239. package/dist/diff/deleted-orphan-scope-notes.js +49 -0
  240. package/dist/diff/deleted-orphans.d.ts +32 -0
  241. package/dist/diff/deleted-orphans.d.ts.map +1 -1
  242. package/dist/diff/deleted-orphans.js +42 -2
  243. package/dist/dispatch/closest-match.d.ts +15 -0
  244. package/dist/dispatch/closest-match.d.ts.map +1 -0
  245. package/dist/dispatch/closest-match.js +47 -0
  246. package/dist/dispatch/global-flags.d.ts +94 -0
  247. package/dist/dispatch/global-flags.d.ts.map +1 -0
  248. package/dist/dispatch/global-flags.js +123 -0
  249. package/dist/dispatch/guard-invocation-input.d.ts +24 -0
  250. package/dist/dispatch/guard-invocation-input.d.ts.map +1 -0
  251. package/dist/dispatch/guard-invocation-input.js +1 -0
  252. package/dist/dispatch/guard-invocation.d.ts +33 -0
  253. package/dist/dispatch/guard-invocation.d.ts.map +1 -0
  254. package/dist/dispatch/guard-invocation.js +183 -0
  255. package/dist/dispatch/help-intercept.d.ts +22 -0
  256. package/dist/dispatch/help-intercept.d.ts.map +1 -0
  257. package/dist/dispatch/help-intercept.js +42 -0
  258. package/dist/dispatch/i-declared-invocation.d.ts +17 -0
  259. package/dist/dispatch/i-declared-invocation.d.ts.map +1 -0
  260. package/dist/dispatch/i-declared-invocation.js +1 -0
  261. package/dist/dispatch/i-unknown-flag-refusal-input.d.ts +28 -0
  262. package/dist/dispatch/i-unknown-flag-refusal-input.d.ts.map +1 -0
  263. package/dist/dispatch/i-unknown-flag-refusal-input.js +1 -0
  264. package/dist/dispatch/invocation-rejection-kind.d.ts +15 -0
  265. package/dist/dispatch/invocation-rejection-kind.d.ts.map +1 -0
  266. package/dist/dispatch/invocation-rejection-kind.js +15 -0
  267. package/dist/dispatch/invocation-rejection.d.ts +18 -0
  268. package/dist/dispatch/invocation-rejection.d.ts.map +1 -0
  269. package/dist/dispatch/invocation-rejection.js +1 -0
  270. package/dist/dispatch/judge-invocation.d.ts +24 -0
  271. package/dist/dispatch/judge-invocation.d.ts.map +1 -0
  272. package/dist/dispatch/judge-invocation.js +50 -0
  273. package/dist/dispatch/positional-mode.d.ts +18 -0
  274. package/dist/dispatch/positional-mode.d.ts.map +1 -0
  275. package/dist/dispatch/positional-mode.js +18 -0
  276. package/dist/dispatch/read-tracking-map.d.ts +28 -0
  277. package/dist/dispatch/read-tracking-map.d.ts.map +1 -0
  278. package/dist/dispatch/read-tracking-map.js +58 -0
  279. package/dist/dispatch/refuse-sibling-valve-flags.d.ts +30 -0
  280. package/dist/dispatch/refuse-sibling-valve-flags.d.ts.map +1 -0
  281. package/dist/dispatch/refuse-sibling-valve-flags.js +65 -0
  282. package/dist/dispatch/subverb-spec.d.ts +26 -0
  283. package/dist/dispatch/subverb-spec.d.ts.map +1 -0
  284. package/dist/dispatch/subverb-spec.js +1 -0
  285. package/dist/dispatch/undocumented-flag-reads.d.ts +22 -0
  286. package/dist/dispatch/undocumented-flag-reads.d.ts.map +1 -0
  287. package/dist/dispatch/undocumented-flag-reads.js +283 -0
  288. package/dist/dispatch/unknown-flag-refusal-mode.d.ts +13 -0
  289. package/dist/dispatch/unknown-flag-refusal-mode.d.ts.map +1 -0
  290. package/dist/dispatch/unknown-flag-refusal-mode.js +13 -0
  291. package/dist/dispatch/unknown-flag-refusal.d.ts +22 -0
  292. package/dist/dispatch/unknown-flag-refusal.d.ts.map +1 -0
  293. package/dist/dispatch/unknown-flag-refusal.js +57 -0
  294. package/dist/dispatch/unread-flags.d.ts +152 -0
  295. package/dist/dispatch/unread-flags.d.ts.map +1 -0
  296. package/dist/dispatch/unread-flags.js +295 -0
  297. package/dist/dispatch/verdict-valve-flag.d.ts +21 -0
  298. package/dist/dispatch/verdict-valve-flag.d.ts.map +1 -0
  299. package/dist/dispatch/verdict-valve-flag.js +21 -0
  300. package/dist/dispatch/walk-declared-subverbs.d.ts +14 -0
  301. package/dist/dispatch/walk-declared-subverbs.d.ts.map +1 -0
  302. package/dist/dispatch/walk-declared-subverbs.js +28 -0
  303. package/dist/exit-codes.d.ts +41 -3
  304. package/dist/exit-codes.d.ts.map +1 -1
  305. package/dist/exit-codes.js +153 -14
  306. package/dist/finish/run-finish.d.ts +72 -5
  307. package/dist/finish/run-finish.d.ts.map +1 -1
  308. package/dist/finish/run-finish.js +453 -104
  309. package/dist/gates/accepted-empty-note.d.ts +10 -0
  310. package/dist/gates/accepted-empty-note.d.ts.map +1 -0
  311. package/dist/gates/accepted-empty-note.js +11 -0
  312. package/dist/gates/allow-empty.d.ts +25 -0
  313. package/dist/gates/allow-empty.d.ts.map +1 -0
  314. package/dist/gates/allow-empty.js +27 -0
  315. package/dist/gates/asset-doctor-failing-units.d.ts +16 -0
  316. package/dist/gates/asset-doctor-failing-units.d.ts.map +1 -0
  317. package/dist/gates/asset-doctor-failing-units.js +14 -0
  318. package/dist/gates/asset-doctor-failure-line.d.ts +16 -0
  319. package/dist/gates/asset-doctor-failure-line.d.ts.map +1 -0
  320. package/dist/gates/asset-doctor-failure-line.js +28 -0
  321. package/dist/gates/empty-rule-advice-lines.d.ts +15 -0
  322. package/dist/gates/empty-rule-advice-lines.d.ts.map +1 -0
  323. package/dist/gates/empty-rule-advice-lines.js +20 -0
  324. package/dist/gates/gate-envelope.d.ts +85 -8
  325. package/dist/gates/gate-envelope.d.ts.map +1 -1
  326. package/dist/gates/gate-envelope.js +56 -7
  327. package/dist/gates/gate-rule-globs.d.ts +37 -3
  328. package/dist/gates/gate-rule-globs.d.ts.map +1 -1
  329. package/dist/gates/gate-rule-globs.js +78 -40
  330. package/dist/gates/gate-rule-view.d.ts +17 -4
  331. package/dist/gates/gate-rule-view.d.ts.map +1 -1
  332. package/dist/gates/gate-rule-view.js +105 -90
  333. package/dist/gates/i-coverage-dead-glob.d.ts +19 -0
  334. package/dist/gates/i-coverage-dead-glob.d.ts.map +1 -0
  335. package/dist/gates/i-coverage-dead-glob.js +1 -0
  336. package/dist/gates/i-coverage-negation.d.ts +13 -0
  337. package/dist/gates/i-coverage-negation.d.ts.map +1 -0
  338. package/dist/gates/i-coverage-negation.js +1 -0
  339. package/dist/gates/i-registration-query-settle-input.d.ts +18 -0
  340. package/dist/gates/i-registration-query-settle-input.d.ts.map +1 -0
  341. package/dist/gates/i-registration-query-settle-input.js +1 -0
  342. package/dist/gates/i-unit-state-note-row.d.ts +16 -0
  343. package/dist/gates/i-unit-state-note-row.d.ts.map +1 -0
  344. package/dist/gates/i-unit-state-note-row.js +1 -0
  345. package/dist/gates/i-unit-state-notes.d.ts +32 -0
  346. package/dist/gates/i-unit-state-notes.d.ts.map +1 -0
  347. package/dist/gates/i-unit-state-notes.js +1 -0
  348. package/dist/gates/incomplete-registry-inventory.d.ts +19 -0
  349. package/dist/gates/incomplete-registry-inventory.d.ts.map +1 -0
  350. package/dist/gates/incomplete-registry-inventory.js +63 -0
  351. package/dist/gates/measure-registration-roles.d.ts +10 -0
  352. package/dist/gates/measure-registration-roles.d.ts.map +1 -0
  353. package/dist/gates/measure-registration-roles.js +9 -0
  354. package/dist/gates/plane-verdict.d.ts +14 -0
  355. package/dist/gates/plane-verdict.d.ts.map +1 -0
  356. package/dist/gates/plane-verdict.js +22 -0
  357. package/dist/gates/qualify-clean-for-units.d.ts +12 -0
  358. package/dist/gates/qualify-clean-for-units.d.ts.map +1 -0
  359. package/dist/gates/qualify-clean-for-units.js +18 -0
  360. package/dist/gates/registration-graph-verdict.d.ts +34 -0
  361. package/dist/gates/registration-graph-verdict.d.ts.map +1 -0
  362. package/dist/gates/registration-graph-verdict.js +70 -0
  363. package/dist/gates/registration-roles.d.ts +9 -0
  364. package/dist/gates/registration-roles.d.ts.map +1 -0
  365. package/dist/gates/registration-roles.js +1 -0
  366. package/dist/gates/registry-liveness.d.ts +11 -0
  367. package/dist/gates/registry-liveness.d.ts.map +1 -0
  368. package/dist/gates/registry-liveness.js +13 -0
  369. package/dist/gates/rule-coverage.d.ts +187 -4
  370. package/dist/gates/rule-coverage.d.ts.map +1 -1
  371. package/dist/gates/rule-coverage.js +501 -49
  372. package/dist/gates/run-gate-planes.d.ts +14 -0
  373. package/dist/gates/run-gate-planes.d.ts.map +1 -1
  374. package/dist/gates/run-gate-planes.js +172 -30
  375. package/dist/gates/scaffold-selftest.d.ts +56 -0
  376. package/dist/gates/scaffold-selftest.d.ts.map +1 -0
  377. package/dist/gates/scaffold-selftest.js +119 -0
  378. package/dist/gates/seam-rejected-rules.d.ts +28 -0
  379. package/dist/gates/seam-rejected-rules.d.ts.map +1 -0
  380. package/dist/gates/seam-rejected-rules.js +95 -0
  381. package/dist/gates/self-test-check.d.ts +29 -0
  382. package/dist/gates/self-test-check.d.ts.map +1 -0
  383. package/dist/gates/self-test-check.js +1 -0
  384. package/dist/gates/self-test-subject.d.ts +28 -0
  385. package/dist/gates/self-test-subject.d.ts.map +1 -0
  386. package/dist/gates/self-test-subject.js +1 -0
  387. package/dist/gates/self-test.d.ts +20 -0
  388. package/dist/gates/self-test.d.ts.map +1 -0
  389. package/dist/gates/self-test.js +82 -0
  390. package/dist/gates/settle-verdict.d.ts +14 -0
  391. package/dist/gates/settle-verdict.d.ts.map +1 -0
  392. package/dist/gates/settle-verdict.js +13 -0
  393. package/dist/gates/settled-verdict.d.ts +8 -0
  394. package/dist/gates/settled-verdict.d.ts.map +1 -0
  395. package/dist/gates/settled-verdict.js +1 -0
  396. package/dist/gates/template-registry-coverage.d.ts +18 -0
  397. package/dist/gates/template-registry-coverage.d.ts.map +1 -0
  398. package/dist/gates/template-registry-coverage.js +24 -0
  399. package/dist/gates/unit-state-notes.d.ts +40 -0
  400. package/dist/gates/unit-state-notes.d.ts.map +1 -0
  401. package/dist/gates/unit-state-notes.js +71 -0
  402. package/dist/gates/verdict-line.d.ts +18 -0
  403. package/dist/gates/verdict-line.d.ts.map +1 -0
  404. package/dist/gates/verdict-line.js +40 -0
  405. package/dist/graph/graph-reuse-lookup.d.ts +10 -0
  406. package/dist/graph/graph-reuse-lookup.d.ts.map +1 -0
  407. package/dist/graph/graph-reuse-lookup.js +41 -0
  408. package/dist/graph/index-behind-hint.d.ts +11 -0
  409. package/dist/graph/index-behind-hint.d.ts.map +1 -0
  410. package/dist/graph/index-behind-hint.js +27 -0
  411. package/dist/knowledge/knowledge-stale-gate-flags.d.ts +3 -0
  412. package/dist/knowledge/knowledge-stale-gate-flags.d.ts.map +1 -0
  413. package/dist/knowledge/knowledge-stale-gate-flags.js +1 -0
  414. package/dist/knowledge/knowledge-stale-gate-input.d.ts +3 -0
  415. package/dist/knowledge/knowledge-stale-gate-input.d.ts.map +1 -0
  416. package/dist/knowledge/knowledge-stale-gate-input.js +1 -0
  417. package/dist/knowledge/knowledge-stale-gate-result.d.ts +3 -0
  418. package/dist/knowledge/knowledge-stale-gate-result.d.ts.map +1 -0
  419. package/dist/knowledge/knowledge-stale-gate-result.js +1 -0
  420. package/dist/knowledge/knowledge-stale-gate.d.ts +10 -0
  421. package/dist/knowledge/knowledge-stale-gate.d.ts.map +1 -0
  422. package/dist/knowledge/knowledge-stale-gate.js +9 -0
  423. package/dist/main.d.ts.map +1 -1
  424. package/dist/main.js +200 -83
  425. package/dist/output/failure-hints.d.ts.map +1 -1
  426. package/dist/output/failure-hints.js +11 -9
  427. package/dist/output/rejected-entries-note.d.ts +57 -0
  428. package/dist/output/rejected-entries-note.d.ts.map +1 -0
  429. package/dist/output/rejected-entries-note.js +103 -0
  430. package/dist/output/stdout-is-pipe.d.ts +13 -0
  431. package/dist/output/stdout-is-pipe.d.ts.map +1 -0
  432. package/dist/output/stdout-is-pipe.js +21 -0
  433. package/dist/packs/stale-build-warning.d.ts +11 -0
  434. package/dist/packs/stale-build-warning.d.ts.map +1 -0
  435. package/dist/packs/stale-build-warning.js +23 -0
  436. package/dist/quality/run-quality.d.ts +123 -0
  437. package/dist/quality/run-quality.d.ts.map +1 -0
  438. package/dist/quality/run-quality.js +442 -0
  439. package/dist/schemas/json-schemas.d.ts +184 -3
  440. package/dist/schemas/json-schemas.d.ts.map +1 -1
  441. package/dist/schemas/json-schemas.js +113 -2
  442. package/dist/shrk.d.ts +3 -0
  443. package/dist/shrk.d.ts.map +1 -0
  444. package/dist/shrk.js +30 -0
  445. package/dist/surface/audience-applicability.d.ts +26 -0
  446. package/dist/surface/audience-applicability.d.ts.map +1 -0
  447. package/dist/surface/audience-applicability.js +60 -0
  448. package/dist/surface/catalog-command-safety.d.ts +18 -0
  449. package/dist/surface/catalog-command-safety.d.ts.map +1 -0
  450. package/dist/surface/catalog-command-safety.js +62 -0
  451. package/dist/surface/cli-command-resolver.d.ts +21 -0
  452. package/dist/surface/cli-command-resolver.d.ts.map +1 -0
  453. package/dist/surface/cli-command-resolver.js +87 -0
  454. package/dist/surface/command-dispatch-kind.d.ts +20 -0
  455. package/dist/surface/command-dispatch-kind.d.ts.map +1 -0
  456. package/dist/surface/command-dispatch-kind.js +20 -0
  457. package/dist/surface/command-index-entry.d.ts +40 -0
  458. package/dist/surface/command-index-entry.d.ts.map +1 -0
  459. package/dist/surface/command-index-entry.js +1 -0
  460. package/dist/surface/command-index.d.ts +77 -0
  461. package/dist/surface/command-index.d.ts.map +1 -0
  462. package/dist/surface/command-index.js +323 -0
  463. package/dist/surface/i-command-index.d.ts +22 -0
  464. package/dist/surface/i-command-index.d.ts.map +1 -0
  465. package/dist/surface/i-command-index.js +1 -0
  466. package/dist/surface/i-command-string-context.d.ts +20 -0
  467. package/dist/surface/i-command-string-context.d.ts.map +1 -0
  468. package/dist/surface/i-command-string-context.js +1 -0
  469. package/dist/surface/i-surface-denial.d.ts +13 -0
  470. package/dist/surface/i-surface-denial.d.ts.map +1 -0
  471. package/dist/surface/i-surface-denial.js +1 -0
  472. package/dist/surface/load-surface-context.d.ts +17 -1
  473. package/dist/surface/load-surface-context.d.ts.map +1 -1
  474. package/dist/surface/load-surface-context.js +48 -10
  475. package/dist/surface/not-enabled-error.d.ts +38 -4
  476. package/dist/surface/not-enabled-error.d.ts.map +1 -1
  477. package/dist/surface/not-enabled-error.js +96 -6
  478. package/dist/surface/profiles.d.ts +7 -0
  479. package/dist/surface/profiles.d.ts.map +1 -1
  480. package/dist/surface/resolve-command-string.d.ts +10 -0
  481. package/dist/surface/resolve-command-string.d.ts.map +1 -0
  482. package/dist/surface/resolve-command-string.js +572 -0
  483. package/dist/surface/surface-config-writer.d.ts +7 -2
  484. package/dist/surface/surface-config-writer.d.ts.map +1 -1
  485. package/dist/surface/surface-config-writer.js +25 -6
  486. package/dist/surface/surface-layer.d.ts +11 -0
  487. package/dist/surface/surface-layer.d.ts.map +1 -0
  488. package/dist/surface/surface-layer.js +11 -0
  489. package/dist/surface/surface-refusal-reason.d.ts +16 -0
  490. package/dist/surface/surface-refusal-reason.d.ts.map +1 -0
  491. package/dist/surface/surface-refusal-reason.js +16 -0
  492. package/dist/surface/surface-selector.d.ts +26 -0
  493. package/dist/surface/surface-selector.d.ts.map +1 -0
  494. package/dist/surface/surface-selector.js +42 -0
  495. package/dist/surface/surface-summary.d.ts +61 -18
  496. package/dist/surface/surface-summary.d.ts.map +1 -1
  497. package/dist/surface/surface-summary.js +147 -60
  498. package/dist/surface/tier.d.ts +85 -22
  499. package/dist/surface/tier.d.ts.map +1 -1
  500. package/dist/surface/tier.js +132 -30
  501. package/dist/usage/usage-log.d.ts +6 -1
  502. package/dist/usage/usage-log.d.ts.map +1 -1
  503. package/dist/usage/usage-log.js +7 -2
  504. package/dist/validation/run-validation-loop.d.ts +9 -0
  505. package/dist/validation/run-validation-loop.d.ts.map +1 -1
  506. package/dist/validation/run-validation-loop.js +14 -11
  507. package/dist/validation/typecheck-emitted.d.ts +8 -9
  508. package/dist/validation/typecheck-emitted.d.ts.map +1 -1
  509. package/dist/validation/typecheck-emitted.js +17 -97
  510. package/package.json +34 -34
@@ -1,118 +1,305 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
2
  import * as nodePath from 'node:path';
3
+ import { importModuleViaLoader } from '@shrkcrft/core';
4
+ import { formatEntryRejection, readPackManifest, rejectedEntryTypecheckHint, typecheckPackAssets, validateContributionFile, } from '@shrkcrft/inspector';
5
+ import { CONTRIBUTION_FILE_KEYS, FUTURE_CONTRIBUTION_FILE_KEYS, validatePackManifest, } from '@shrkcrft/plugin-api';
3
6
  import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
4
7
  import { asJson, header, kv } from "../output/format-output.js";
5
- import { importModuleViaLoader } from '@shrkcrft/core';
8
+ import { settleVerdict } from "../gates/settle-verdict.js";
9
+ import { verdictLine } from "../gates/verdict-line.js";
10
+ import { ALLOW_EMPTY_FLAG, allowEmptyValve } from "../gates/allow-empty.js";
6
11
  const VALID_KINDS = new Set([
7
12
  'generic',
8
13
  'framework',
9
14
  'architecture',
10
15
  'enterprise',
11
16
  ]);
12
- /** Pure: compute the file set to write. No IO. */
17
+ /** The manifest path the scaffold declares in package.json a source-loaded pack. */
18
+ const MANIFEST_REL = './src/sharkcraft.plugin.ts';
19
+ /**
20
+ * Pure: compute the file set to write. No IO.
21
+ *
22
+ * The scaffold is a VALID, discoverable, type-clean pack:
23
+ * - `package.json` `sharkcraft.manifest` points at `src/sharkcraft.plugin.ts`
24
+ * (discovery reads nothing else — without it the pack is INVALID);
25
+ * - that file default-exports a real `ISharkCraftPackManifest` (`satisfies`,
26
+ * with a TYPE-ONLY import, so there is no runtime plugin-api coupling);
27
+ * - every asset it emits is declared in the manifest and non-empty (a file
28
+ * the manifest does not list is never loaded — so none is emitted);
29
+ * - every asset is annotated with `satisfies` against the SDK types, and
30
+ * `tsc -p tsconfig.json` (noEmit) type-checks clean.
31
+ */
13
32
  export function planPackScaffold(input) {
14
33
  const fullName = input.scope ? `${input.scope}/${input.name}` : input.name;
34
+ const withTemplates = input.kind === 'framework' || input.withExamples === true;
35
+ const withBoundaries = input.kind === 'architecture' || input.withExamples === true;
36
+ const knowledgeRows = knowledgeRowsFor(input.kind);
37
+ const contributions = {
38
+ knowledgeFiles: ['./src/assets/knowledge.ts'],
39
+ ruleFiles: ['./src/assets/rules.ts'],
40
+ pathFiles: ['./src/assets/paths.ts'],
41
+ ...(withTemplates
42
+ ? { templateFiles: ['./src/assets/templates.ts'], pipelineFiles: ['./src/assets/pipelines.ts'] }
43
+ : {}),
44
+ ...(input.preset ? { presetFiles: ['./src/assets/presets.ts'] } : {}),
45
+ ...(withBoundaries ? { boundaryFiles: ['./src/assets/boundaries.ts'] } : {}),
46
+ docsFiles: ['./src/assets/docs/overview.md'],
47
+ };
15
48
  const packageJson = {
16
49
  name: fullName,
17
50
  version: '0.0.1',
18
51
  description: `SharkCraft pack scaffolded as ${input.kind}.`,
19
52
  type: 'module',
20
- main: 'dist/sharkcraft.plugin.js',
21
- exports: {
22
- '.': {
23
- types: './dist/sharkcraft.plugin.d.ts',
24
- default: './dist/sharkcraft.plugin.js',
25
- },
26
- },
53
+ main: MANIFEST_REL,
27
54
  sharkcraft: {
28
55
  kind: input.kind,
29
56
  ...(input.preset ? { preset: input.preset } : {}),
57
+ manifest: MANIFEST_REL,
30
58
  },
31
59
  scripts: {
32
- build: 'tsc -p tsconfig.json',
33
- doctor: 'shrk pack doctor .',
34
- test: 'shrk pack test .',
60
+ typecheck: 'tsc -p tsconfig.json',
61
+ test: 'shrk packs test . --load --typecheck',
62
+ 'release-check': 'shrk packs release-check . --typecheck',
63
+ },
64
+ // Type-only dependencies: the assets `import type` from the SDK, which is
65
+ // erased at runtime — they are needed only for `npm run typecheck`.
66
+ devDependencies: {
67
+ '@shrkcrft/plugin-api': '*',
68
+ typescript: '^5.6.0',
35
69
  },
36
- files: ['dist', 'src', 'README.md', 'SECURITY.md', 'package.json'],
70
+ files: ['src', 'README.md', 'SECURITY.md', 'package.json'],
37
71
  };
38
72
  const files = [];
39
- files.push({
40
- relativePath: 'package.json',
41
- body: JSON.stringify(packageJson, null, 2) + '\n',
42
- });
43
- files.push({
44
- relativePath: 'README.md',
45
- body: renderReadme(input, fullName),
46
- });
47
- files.push({
48
- relativePath: 'SECURITY.md',
49
- body: renderSecurity(input),
50
- });
73
+ files.push({ relativePath: 'package.json', body: JSON.stringify(packageJson, null, 2) + '\n' });
74
+ files.push({ relativePath: 'README.md', body: renderReadme(input, fullName, contributions) });
75
+ files.push({ relativePath: 'SECURITY.md', body: renderSecurity(input) });
51
76
  files.push({
52
77
  relativePath: 'tsconfig.json',
53
78
  body: JSON.stringify({
54
79
  compilerOptions: {
55
80
  target: 'ES2022',
56
- module: 'ES2022',
81
+ module: 'ESNext',
57
82
  moduleResolution: 'bundler',
58
83
  strict: true,
59
- declaration: true,
60
- outDir: 'dist',
84
+ noEmit: true,
85
+ allowImportingTsExtensions: true,
86
+ skipLibCheck: true,
61
87
  },
62
88
  include: ['src/**/*.ts'],
63
89
  }, null, 2) + '\n',
64
90
  });
65
- files.push({
66
- relativePath: 'src/sharkcraft.plugin.ts',
67
- body: renderPluginEntry(input, fullName),
68
- });
91
+ files.push({ relativePath: 'src/sharkcraft.plugin.ts', body: renderManifest(input, fullName, contributions) });
69
92
  files.push({
70
93
  relativePath: 'src/assets/knowledge.ts',
71
- body: renderKnowledgeAsset(input),
94
+ body: entriesAsset('Knowledge entries contributed by this pack.', knowledgeRows),
72
95
  });
73
96
  files.push({
74
97
  relativePath: 'src/assets/rules.ts',
75
- body: renderRulesAsset(input),
98
+ body: entriesAsset('Rules contributed by this pack (knowledge entries of type "rule").', rulesRowsFor(input.kind)),
76
99
  });
77
100
  files.push({
78
101
  relativePath: 'src/assets/paths.ts',
79
- body: renderPathsAsset(input),
80
- });
81
- files.push({
82
- relativePath: 'src/assets/templates.ts',
83
- body: renderTemplatesAsset(input),
84
- });
85
- files.push({
86
- relativePath: 'src/assets/pipelines.ts',
87
- body: renderPipelinesAsset(input),
88
- });
89
- files.push({
90
- relativePath: 'src/assets/presets.ts',
91
- body: renderPresetsAsset(input),
92
- });
93
- files.push({
94
- relativePath: 'src/assets/docs/overview.md',
95
- body: renderDocsOverview(input, fullName),
102
+ body: entriesAsset('Path conventions contributed by this pack (knowledge entries of type "path").', [
103
+ {
104
+ id: `${slug(input.name)}.path.source-layout`,
105
+ title: 'Source layout',
106
+ type: 'path',
107
+ priority: 'medium',
108
+ content: 'Source files live under src/; pack assets live under src/assets/.',
109
+ },
110
+ ]),
96
111
  });
97
- if (input.kind === 'architecture' || input.withExamples) {
112
+ if (withTemplates) {
113
+ files.push({ relativePath: 'src/assets/templates.ts', body: renderTemplatesAsset() });
114
+ files.push({ relativePath: 'src/assets/pipelines.ts', body: renderPipelinesAsset() });
115
+ }
116
+ if (input.preset) {
98
117
  files.push({
99
- relativePath: 'src/assets/boundaries.ts',
100
- body: renderBoundariesAsset(),
118
+ relativePath: 'src/assets/presets.ts',
119
+ body: renderPresetsAsset(input.preset, knowledgeRows[0].id),
101
120
  });
102
121
  }
122
+ if (withBoundaries) {
123
+ files.push({ relativePath: 'src/assets/boundaries.ts', body: renderBoundariesAsset() });
124
+ }
125
+ files.push({ relativePath: 'src/assets/docs/overview.md', body: renderDocsOverview(input, fullName) });
103
126
  if (input.kind === 'enterprise') {
104
- files.push({
105
- relativePath: 'docs/review-workflow.md',
106
- body: renderEnterpriseReviewDocs(),
107
- });
108
- files.push({
109
- relativePath: 'docs/security-baseline.md',
110
- body: renderEnterpriseSecurityDocs(),
111
- });
127
+ files.push({ relativePath: 'docs/review-workflow.md', body: renderEnterpriseReviewDocs() });
128
+ files.push({ relativePath: 'docs/security-baseline.md', body: renderEnterpriseSecurityDocs() });
112
129
  }
113
130
  return { files, packageJson, packRoot: input.outDir };
114
131
  }
115
- function renderReadme(input, fullName) {
132
+ function slug(name) {
133
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'pack';
134
+ }
135
+ function knowledgeRowsFor(kind) {
136
+ if (kind === 'enterprise') {
137
+ return [
138
+ { id: 'security.baseline', title: 'Security baseline', type: 'security', priority: 'high', content: 'Document the security baseline this pack enforces.' },
139
+ { id: 'review.workflow', title: 'Code review workflow', type: 'workflow', priority: 'high', content: 'How code review gets done in this organisation.' },
140
+ ];
141
+ }
142
+ if (kind === 'architecture') {
143
+ return [
144
+ { id: 'architecture.layering', title: 'Layering rules', type: 'architecture', priority: 'high', content: 'Describe lower -> higher layers that cannot be inverted.' },
145
+ { id: 'architecture.coverage', title: 'Coverage targets', type: 'architecture', priority: 'medium', content: 'Coverage axes you care about.' },
146
+ ];
147
+ }
148
+ if (kind === 'framework') {
149
+ return [
150
+ { id: 'framework.overview', title: 'Framework overview', type: 'technical', priority: 'high', content: 'What this framework is for.' },
151
+ ];
152
+ }
153
+ return [
154
+ { id: 'pack.overview', title: 'Pack overview', type: 'technical', priority: 'medium', content: 'Short overview of what this pack contributes.' },
155
+ ];
156
+ }
157
+ function rulesRowsFor(kind) {
158
+ if (kind === 'enterprise') {
159
+ return [
160
+ { id: 'rule.review-required', title: 'All changes require code review', type: 'rule', priority: 'critical', content: 'All changes require code review.' },
161
+ { id: 'rule.no-secrets-in-source', title: 'Secrets must never be committed', type: 'rule', priority: 'critical', content: 'Secrets must never be committed.' },
162
+ ];
163
+ }
164
+ if (kind === 'architecture') {
165
+ return [
166
+ { id: 'rule.boundary-enforcement', title: 'Respect layer boundaries', type: 'rule', priority: 'high', content: 'Respect layer boundaries.' },
167
+ ];
168
+ }
169
+ return [{ id: 'rule.example', title: 'Example rule for this pack', type: 'rule', priority: 'medium', content: 'Example rule for this pack.' }];
170
+ }
171
+ /**
172
+ * A knowledge-shaped asset: `import type` (erased at runtime) + `satisfies`, so
173
+ * a misspelled field or a missing required one fails `npm run typecheck` where
174
+ * it is written, while the file still loads against every plugin-api version.
175
+ */
176
+ function entriesAsset(comment, rows) {
177
+ const lines = [];
178
+ lines.push(`// ${comment}`);
179
+ lines.push(`// \`import type\` is erased at runtime; \`satisfies\` type-checks every entry where it is written.`);
180
+ lines.push(`import type { IKnowledgeEntry } from '@shrkcrft/plugin-api';`);
181
+ lines.push('');
182
+ lines.push('export default [');
183
+ for (const r of rows) {
184
+ lines.push(' {');
185
+ lines.push(` id: ${JSON.stringify(r.id)},`);
186
+ lines.push(` title: ${JSON.stringify(r.title)},`);
187
+ lines.push(` type: ${JSON.stringify(r.type)},`);
188
+ lines.push(` priority: ${JSON.stringify(r.priority)},`);
189
+ lines.push(' scope: [],');
190
+ lines.push(' tags: [],');
191
+ lines.push(" appliesWhen: ['onboarding'],");
192
+ lines.push(` content: ${JSON.stringify(r.content)},`);
193
+ lines.push(' },');
194
+ }
195
+ lines.push(`] satisfies readonly Omit<IKnowledgeEntry, 'source'>[];`);
196
+ lines.push('');
197
+ return lines.join('\n');
198
+ }
199
+ function renderManifest(input, fullName, contributions) {
200
+ const lines = [];
201
+ lines.push(`// SharkCraft pack manifest for ${fullName} — scaffolded as kind=${input.kind}.`);
202
+ lines.push('// package.json `sharkcraft.manifest` points here; discovery reads nothing else.');
203
+ lines.push('// Every file listed under `contributions` is loaded; a file NOT listed is never loaded.');
204
+ lines.push('// `import type` is erased at runtime (no plugin-api version coupling);');
205
+ lines.push('// `satisfies` type-checks the manifest where it is written.');
206
+ lines.push(`import type { ISharkCraftPackManifest } from '@shrkcrft/plugin-api';`);
207
+ lines.push('');
208
+ lines.push('export default {');
209
+ lines.push(` schema: 'sharkcraft.pack/v1',`);
210
+ lines.push(' info: {');
211
+ lines.push(` name: ${JSON.stringify(fullName)},`);
212
+ lines.push(` version: '0.0.1',`);
213
+ lines.push(` description: ${JSON.stringify(`SharkCraft pack scaffolded as ${input.kind}.`)},`);
214
+ lines.push(' },');
215
+ lines.push(' contributions: {');
216
+ for (const [key, rels] of Object.entries(contributions)) {
217
+ lines.push(` ${key}: [${rels.map((r) => `'${r}'`).join(', ')}],`);
218
+ }
219
+ lines.push(' },');
220
+ lines.push('} satisfies ISharkCraftPackManifest;');
221
+ lines.push('');
222
+ return lines.join('\n');
223
+ }
224
+ function renderTemplatesAsset() {
225
+ return [
226
+ '// Templates contributed by this pack.',
227
+ '// `satisfies` gives every resolver parameter its type (no implicit any).',
228
+ `import type { ITemplateDefinition } from '@shrkcrft/plugin-api';`,
229
+ '',
230
+ 'export default [',
231
+ ' {',
232
+ ` id: 'pack.example.service',`,
233
+ ` name: 'Example service',`,
234
+ ` description: 'Scaffold an example service for this pack.',`,
235
+ ` tags: ['service'],`,
236
+ ` scope: ['ts'],`,
237
+ ` appliesWhen: ['create-service'],`,
238
+ ` variables: [{ name: 'name', required: true, description: 'Service name in kebab-case.', examples: ['user-profile'] }],`,
239
+ ` targetPath: ({ name }) => 'src/services/' + name + '.service.ts',`,
240
+ ` content: ({ name }) =>`,
241
+ ` 'export class ' +`,
242
+ ` String(name).split('-').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('') +`,
243
+ ` 'Service {}\\n',`,
244
+ ' },',
245
+ '] satisfies readonly ITemplateDefinition[];',
246
+ '',
247
+ ].join('\n');
248
+ }
249
+ function renderPipelinesAsset() {
250
+ return [
251
+ '// Pipelines contributed by this pack (shape: IPipelineDefinition from @shrkcrft/pipelines).',
252
+ 'export default [',
253
+ ' {',
254
+ ` id: 'pack.example.pipeline',`,
255
+ ` title: 'Example pipeline',`,
256
+ ` description: 'Reference pipeline for this pack.',`,
257
+ ` appliesWhen: ['create-service'],`,
258
+ ' steps: [',
259
+ ` { id: 'context', type: 'context', description: 'Load the task context.', cliCommands: ['shrk context --task "<task>"'] },`,
260
+ ` { id: 'plan', type: 'generation-plan', description: 'Plan the change (dry-run).', cliCommands: ['shrk gen pack.example.service --var name=<name> --dry-run'] },`,
261
+ ` { id: 'apply', type: 'apply-plan', description: 'Apply the reviewed plan.', humanReview: true },`,
262
+ ' ],',
263
+ ' },',
264
+ '];',
265
+ '',
266
+ ].join('\n');
267
+ }
268
+ function renderPresetsAsset(preset, knowledgeId) {
269
+ return [
270
+ '// Presets contributed by this pack (shape: IPreset from @shrkcrft/presets).',
271
+ 'export default [',
272
+ ' {',
273
+ ` id: ${JSON.stringify(preset)},`,
274
+ ` title: ${JSON.stringify(preset)},`,
275
+ ` description: 'Preset for this pack.',`,
276
+ ` includes: { knowledgeIds: [${JSON.stringify(knowledgeId)}] },`,
277
+ ' },',
278
+ '];',
279
+ '',
280
+ ].join('\n');
281
+ }
282
+ function renderBoundariesAsset() {
283
+ return [
284
+ '// Architecture boundary rules contributed by this pack (shape: IBoundaryRule',
285
+ '// from @shrkcrft/boundaries). An empty list is valid; add rules as the pack',
286
+ '// learns the layering of the projects that adopt it.',
287
+ 'export default [',
288
+ ' // {',
289
+ ` // id: 'boundary.layer.example',`,
290
+ ` // title: 'Example layer boundary',`,
291
+ ` // severity: 'error',`,
292
+ ` // from: ['packages/lower/**/*.ts'],`,
293
+ ` // forbiddenImports: ['@my/higher'],`,
294
+ ' // },',
295
+ '];',
296
+ '',
297
+ ].join('\n');
298
+ }
299
+ function renderReadme(input, fullName, contributions) {
300
+ const assetLines = Object.values(contributions)
301
+ .flat()
302
+ .map((rel) => ` ${rel.replace(/^\.\/src\//, '')}`);
116
303
  return [
117
304
  `# ${fullName}`,
118
305
  '',
@@ -125,24 +312,29 @@ function renderReadme(input, fullName) {
125
312
  '',
126
313
  '```',
127
314
  'src/',
128
- ' sharkcraft.plugin.ts # entryregisters all assets',
129
- ' assets/',
130
- ' knowledge.ts',
131
- ' rules.ts',
132
- ' paths.ts',
133
- ' templates.ts',
134
- ' pipelines.ts',
135
- ' presets.ts',
136
- ' docs/overview.md',
315
+ ' sharkcraft.plugin.ts # the manifest package.json `sharkcraft.manifest` points here',
316
+ ...assetLines,
137
317
  '```',
138
318
  '',
319
+ 'Every file listed in the manifest `contributions` is loaded; a file that is',
320
+ 'not listed there is never loaded.',
321
+ '',
322
+ '## Group modules',
323
+ '',
324
+ 'Split a large asset into group modules and re-export them from the listed',
325
+ 'file with `export * from \'./group.ts\'` — the loader collects every',
326
+ 'entry-shaped export, so nothing needs to be named twice. (Listing entries by',
327
+ 'hand in an array works too, but an entry added to a group and not to the',
328
+ 'array is invisible — `shrk doctor` / `shrk packs doctor` report it as',
329
+ '`unregistered-export`.)',
330
+ '',
139
331
  '## Validate this pack',
140
332
  '',
141
333
  '```bash',
142
- 'shrk pack doctor .',
143
- 'shrk pack test .',
144
- 'shrk pack sign . --secret "$SHARKCRAFT_PACK_SECRET"',
145
- 'shrk pack verify .',
334
+ 'npm run typecheck # tsc — every asset is `satisfies`-annotated',
335
+ 'shrk packs test . --load --typecheck # manifest + contributions load and type-check',
336
+ 'shrk packs release-check . --typecheck # the pre-publish gate',
337
+ 'SHARKCRAFT_PACK_SECRET=<secret> shrk packs sign .',
146
338
  '```',
147
339
  '',
148
340
  '## Local development',
@@ -153,6 +345,7 @@ function renderReadme(input, fullName) {
153
345
  'cd <target-project>',
154
346
  'npm install --no-save ../path/to/this-pack',
155
347
  'shrk packs list',
348
+ 'shrk packs doctor',
156
349
  '```',
157
350
  '',
158
351
  ].join('\n');
@@ -171,7 +364,7 @@ function renderSecurity(input) {
171
364
  'Packs can ship a signed manifest. Sign locally with:',
172
365
  '',
173
366
  '```bash',
174
- 'shrk pack sign . --secret "$SHARKCRAFT_PACK_SECRET"',
367
+ 'SHARKCRAFT_PACK_SECRET="<secret>" shrk packs sign .',
175
368
  '```',
176
369
  '',
177
370
  'Adopting projects verify the signature via:',
@@ -186,157 +379,6 @@ function renderSecurity(input) {
186
379
  '',
187
380
  ].join('\n');
188
381
  }
189
- function renderPluginEntry(input, fullName) {
190
- return [
191
- `// SharkCraft plugin entry for ${fullName}.`,
192
- `// Scaffolded as kind=${input.kind}. Review every asset before publishing.`,
193
- `import knowledge from './assets/knowledge.ts';`,
194
- `import rules from './assets/rules.ts';`,
195
- `import paths from './assets/paths.ts';`,
196
- `import templates from './assets/templates.ts';`,
197
- `import pipelines from './assets/pipelines.ts';`,
198
- `import presets from './assets/presets.ts';`,
199
- `export default {`,
200
- ` knowledge,`,
201
- ` rules,`,
202
- ` paths,`,
203
- ` templates,`,
204
- ` pipelines,`,
205
- ` presets,`,
206
- `};`,
207
- ``,
208
- ].join('\n');
209
- }
210
- function renderKnowledgeAsset(input) {
211
- if (input.kind === 'enterprise') {
212
- return knowledgeBody([
213
- ['security.baseline', 'Security baseline', 'High', 'Document the security baseline this pack enforces.'],
214
- ['review.workflow', 'Code review workflow', 'High', 'How code review gets done in this organisation.'],
215
- ]);
216
- }
217
- if (input.kind === 'architecture') {
218
- return knowledgeBody([
219
- ['architecture.layering', 'Layering rules', 'High', 'Describe lower → higher layers that cannot be inverted.'],
220
- ['architecture.coverage', 'Coverage targets', 'Medium', 'Coverage axes you care about.'],
221
- ]);
222
- }
223
- if (input.kind === 'framework') {
224
- return knowledgeBody([
225
- ['framework.overview', 'Framework overview', 'High', 'What this framework is for.'],
226
- ]);
227
- }
228
- return knowledgeBody([
229
- ['pack.overview', 'Pack overview', 'Medium', 'Short overview of what this pack contributes.'],
230
- ]);
231
- }
232
- function knowledgeBody(rows) {
233
- const lines = [];
234
- lines.push(`// Knowledge entries contributed by this pack.`);
235
- lines.push(`export default [`);
236
- for (const [id, title, priority, content] of rows) {
237
- lines.push(` { id: '${id}', title: '${title}', type: 'knowledge', priority: '${priority}',`);
238
- lines.push(` summary: '${content}', body: '${content}', tags: [], scope: [] },`);
239
- }
240
- lines.push(`];`);
241
- lines.push('');
242
- return lines.join('\n');
243
- }
244
- function renderRulesAsset(input) {
245
- if (input.kind === 'enterprise') {
246
- return rulesBody([
247
- ['rule.review-required', 'All changes require code review', 'critical'],
248
- ['rule.no-secrets-in-source', 'Secrets must never be committed', 'critical'],
249
- ]);
250
- }
251
- if (input.kind === 'architecture') {
252
- return rulesBody([
253
- ['rule.boundary-enforcement', 'Respect layer boundaries', 'high'],
254
- ]);
255
- }
256
- return rulesBody([
257
- ['rule.example', 'Example rule for this pack', 'medium'],
258
- ]);
259
- }
260
- function rulesBody(rows) {
261
- const lines = [];
262
- lines.push(`export default [`);
263
- for (const [id, title, priority] of rows) {
264
- lines.push(` { id: '${id}', title: '${title}', type: 'rule', priority: '${priority}',`);
265
- lines.push(` summary: '${title}', body: '${title}', tags: [], scope: [] },`);
266
- }
267
- lines.push(`];`);
268
- lines.push('');
269
- return lines.join('\n');
270
- }
271
- function renderPathsAsset(_input) {
272
- return [
273
- `export default [`,
274
- ` // Add path conventions: { id, title, type: 'path', patterns: ['src/**/*.ts'], ... }`,
275
- `];`,
276
- ``,
277
- ].join('\n');
278
- }
279
- function renderTemplatesAsset(input) {
280
- const examples = [];
281
- if (input.kind === 'framework' || input.withExamples) {
282
- examples.push(` {`, ` id: 'pack.example.service',`, ` name: 'Example service',`, ` description: 'Scaffold an example service for this pack.',`, ` tags: ['service'],`, ` scope: ['ts'],`, ` appliesWhen: ['create-service'],`, ` variables: [{ name: 'name', required: true }],`, ` targetPath: ({ name }) => 'src/services/' + name + '.service.ts',`, ` content: ({ name }) => 'export class ' + (name as string).replace(/-(\\w)/g, (_m, c) => c.toUpperCase()) + 'Service {}\\n',`, ` },`);
283
- }
284
- return [
285
- `export default [`,
286
- ...examples,
287
- `];`,
288
- ``,
289
- ].join('\n');
290
- }
291
- function renderPipelinesAsset(input) {
292
- if (input.kind === 'framework' || input.withExamples) {
293
- return [
294
- `export default [`,
295
- ` {`,
296
- ` id: 'pack.example.pipeline',`,
297
- ` title: 'Example pipeline',`,
298
- ` description: 'Reference pipeline for this pack.',`,
299
- ` steps: ['plan', 'review', 'apply', 'verify'],`,
300
- ` appliesWhen: ['create-service'],`,
301
- ` },`,
302
- `];`,
303
- ``,
304
- ].join('\n');
305
- }
306
- return ['export default [];', ''].join('\n');
307
- }
308
- function renderPresetsAsset(input) {
309
- if (input.preset) {
310
- return [
311
- `export default [`,
312
- ` {`,
313
- ` id: '${input.preset}',`,
314
- ` title: '${input.preset}',`,
315
- ` description: 'Preset for this pack.',`,
316
- ` appliesWhen: ['create-service'],`,
317
- ` config: {},`,
318
- ` },`,
319
- `];`,
320
- ``,
321
- ].join('\n');
322
- }
323
- return ['export default [];', ''].join('\n');
324
- }
325
- function renderBoundariesAsset() {
326
- return [
327
- `export default [`,
328
- ` // Example architecture boundary:`,
329
- ` // {`,
330
- ` // id: 'boundary.layer.example',`,
331
- ` // title: 'Example layer boundary',`,
332
- ` // severity: 'error',`,
333
- ` // from: ['packages/lower/**/*.ts'],`,
334
- ` // forbiddenImports: ['@my/higher'],`,
335
- ` // },`,
336
- `];`,
337
- ``,
338
- ].join('\n');
339
- }
340
382
  function renderEnterpriseReviewDocs() {
341
383
  return [
342
384
  `# Review workflow`,
@@ -366,14 +408,9 @@ function renderDocsOverview(input, fullName) {
366
408
  `Kind: \`${input.kind}\``,
367
409
  input.preset ? `Preset: \`${input.preset}\`` : '',
368
410
  '',
369
- 'This pack ships:',
370
- '',
371
- '- knowledge entries',
372
- '- rules',
373
- '- path conventions',
374
- '- templates',
375
- '- pipelines',
376
- '- presets',
411
+ 'This pack ships knowledge entries, rules and path conventions' +
412
+ (input.kind === 'framework' || input.withExamples ? ', templates and pipelines' : '') +
413
+ '.',
377
414
  '',
378
415
  ]
379
416
  .filter(Boolean)
@@ -382,7 +419,7 @@ function renderDocsOverview(input, fullName) {
382
419
  // ─── CLI handler ─────────────────────────────────────────────────────────────
383
420
  export const packsNewCommand = {
384
421
  name: 'new',
385
- description: 'Scaffold a new SharkCraft pack package (rules / paths / templates / pipelines / presets / boundaries). Dry-run by default — pass --write to materialize. No install, no publish, no overwrite without --force.',
422
+ description: 'Scaffold a new SharkCraft pack package — a valid, discoverable, type-clean pack (package.json sharkcraft.manifest a `satisfies ISharkCraftPackManifest` manifest; every asset `satisfies`-annotated). Dry-run by default — pass --write to materialize. No install, no publish, no overwrite without --force.',
386
423
  usage: 'shrk [--cwd <dir>] packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise] [--with-examples] [--write] [--force] [--json]',
387
424
  async run(args) {
388
425
  const name = args.positional[0];
@@ -463,18 +500,57 @@ export const packsNewCommand = {
463
500
  for (const f of result.files) {
464
501
  process.stdout.write(` + ${f.relativePath}\n`);
465
502
  }
466
- process.stdout.write('\nNext: `shrk pack doctor ' + outDir + '` | `shrk pack test ' + outDir + '`\n');
503
+ // Install first: --typecheck resolves the scaffold's `import type from
504
+ // '@shrkcrft/plugin-api'` against the pack's own devDependencies.
505
+ process.stdout.write('\nNext:\n' +
506
+ ` 1. (cd ${outDir} && npm install) # devDependencies: @shrkcrft/plugin-api + typescript\n` +
507
+ ` 2. shrk packs test ${outDir} --load --typecheck\n` +
508
+ ` 3. shrk packs release-check ${outDir} --typecheck\n`);
467
509
  return 0;
468
510
  },
469
511
  };
512
+ /** Slots whose files default-export an array of `{ id }` records. */
513
+ const ARRAY_OF_IDS_SLOTS = {
514
+ knowledgeFiles: 'knowledge',
515
+ ruleFiles: 'rule',
516
+ pathFiles: 'path',
517
+ pathConventionFiles: 'path',
518
+ templateFiles: 'template',
519
+ pipelineFiles: 'pipeline',
520
+ presetFiles: 'preset',
521
+ boundaryFiles: 'boundary',
522
+ };
523
+ const MODULE_FILE = /\.(?:[cm]?[jt]s|tsx)$/;
524
+ function declaredContributions(packRoot, manifest) {
525
+ const out = [];
526
+ const contributions = (manifest?.contributions ?? {});
527
+ for (const key of [...CONTRIBUTION_FILE_KEYS, ...FUTURE_CONTRIBUTION_FILE_KEYS]) {
528
+ for (const rel of contributions[key] ?? []) {
529
+ if (typeof rel !== 'string' || rel.length === 0)
530
+ continue;
531
+ out.push({ key, rel, abs: nodePath.resolve(packRoot, rel) });
532
+ }
533
+ }
534
+ return out;
535
+ }
470
536
  export const packsTestCommand = {
471
537
  name: 'test',
472
- description: 'Validate a pack at the given path: manifest validation, asset references, signature optional. With --load, import contribution files. With --trusted-load, run template renderers. With --cases, run definePackTest test cases.',
473
- usage: 'shrk [--cwd <dir>] packs test <path> [--load] [--trusted-load] [--require-signature] [--cases] [--case <id>] [--update-snapshots] [--json]',
538
+ description: 'Validate a pack at the given path: the manifest package.json points at (sharkcraft.manifest) and every contribution file it declares. With --load, import each declared contribution module and run the loader (or acceptance predicate) the engine applies to its slot at runtime — every entry it would refuse is an `asset-entry-rejected` error, annotated or not. With --trusted-load, run template renderers. With --typecheck, type-check the manifest + every TS contribution (exit 2 when no TS file could be checked). With --cases, run definePackTest test cases.',
539
+ usage: 'shrk [--cwd <dir>] packs test <path> [--load] [--trusted-load] [--typecheck] [--require-signature] [--cases] [--case <id>] [--update-snapshots] [--allow-empty] [--json]',
540
+ booleanFlags: new Set([
541
+ 'load',
542
+ 'trusted-load',
543
+ 'typecheck',
544
+ 'require-signature',
545
+ 'cases',
546
+ 'update-snapshots',
547
+ 'json',
548
+ ALLOW_EMPTY_FLAG,
549
+ ]),
474
550
  async run(args) {
475
551
  const target = args.positional[0];
476
552
  if (!target) {
477
- process.stderr.write('Usage: shrk packs test <path> [--load] [--trusted-load] [--require-signature] [--cases]\n');
553
+ process.stderr.write('Usage: shrk packs test <path> [--load] [--trusted-load] [--typecheck] [--require-signature] [--cases]\n');
478
554
  return 2;
479
555
  }
480
556
  const cwd = resolveCwd(args);
@@ -494,12 +570,28 @@ export const packsTestCommand = {
494
570
  ...(caseId ? { caseId } : {}),
495
571
  ...(updateSnapshots ? { updateSnapshots: true } : {}),
496
572
  });
573
+ // Zero cases ran: nothing was tested. 2, unless --allow-empty accepts it.
574
+ const casesSettled = settleVerdict(report.failed === 0 ? 0 : 1, [
575
+ {
576
+ unit: 'pack test cases',
577
+ expected: report.ran,
578
+ examined: report.ran,
579
+ ...(report.ran === 0 ? { reason: report.testsFile ? 'no case ran' : 'no pack-tests file found' } : {}),
580
+ ...allowEmptyValve(args, report.ran),
581
+ },
582
+ ]);
497
583
  if (flagBool(args, 'json')) {
498
- process.stdout.write(JSON.stringify(report, null, 2) + '\n');
499
- return report.failed === 0 ? 0 : 1;
584
+ process.stdout.write(JSON.stringify({ ...report, exitCode: casesSettled.exit, verdict: casesSettled.verdict, shortfalls: casesSettled.shortfalls }, null, 2) + '\n');
585
+ return casesSettled.exit;
500
586
  }
501
587
  process.stdout.write(renderPackTestReportText(report));
502
- return report.failed === 0 ? 0 : 1;
588
+ const casesLine = verdictLine(casesSettled, '');
589
+ if (casesLine)
590
+ process.stdout.write(`\n${casesLine}\n`);
591
+ if (casesSettled.exit === 2 && report.ran === 0) {
592
+ process.stdout.write(`Pass --${ALLOW_EMPTY_FLAG} to accept a pack with no test case explicitly.\n`);
593
+ }
594
+ return casesSettled.exit;
503
595
  }
504
596
  const pkgPath = nodePath.join(packRoot, 'package.json');
505
597
  if (!existsSync(pkgPath)) {
@@ -522,192 +614,270 @@ export const packsTestCommand = {
522
614
  if (typeof pkg.version !== 'string') {
523
615
  issues.push({ code: 'missing-version', message: 'package.json: version is required', severity: 'error' });
524
616
  }
525
- const requiredAssets = [
526
- 'src/sharkcraft.plugin.ts',
527
- 'src/assets/rules.ts',
528
- 'src/assets/paths.ts',
529
- 'src/assets/templates.ts',
530
- 'src/assets/pipelines.ts',
531
- 'src/assets/presets.ts',
532
- 'src/assets/knowledge.ts',
533
- ];
534
- for (const rel of requiredAssets) {
535
- if (!existsSync(nodePath.join(packRoot, rel))) {
536
- issues.push({
537
- code: 'missing-asset',
538
- message: `Pack is missing expected asset file: ${rel}`,
539
- severity: 'warning',
540
- });
617
+ // The manifest is what discovery reads — from package.json, never a
618
+ // hard-coded list of asset paths.
619
+ const read = await readPackManifest(packRoot);
620
+ if (!read.manifestPath) {
621
+ issues.push({
622
+ code: 'no-manifest',
623
+ message: `package.json does not point at a manifest (${read.error ?? 'sharkcraft.manifest missing'}) — discovery reports this pack INVALID`,
624
+ severity: 'error',
625
+ });
626
+ }
627
+ else if (!read.manifest) {
628
+ issues.push({ code: 'manifest-load-failed', message: read.error ?? 'manifest failed to load', severity: 'error' });
629
+ }
630
+ else {
631
+ for (const i of validatePackManifest(read.manifest).issues) {
632
+ issues.push({ code: 'manifest-invalid', message: `${i.field}: ${i.message}`, severity: 'error' });
541
633
  }
542
634
  }
543
- if (flagBool(args, 'require-signature')) {
544
- const distManifest = nodePath.join(packRoot, 'dist', 'manifest.json');
545
- if (!existsSync(distManifest)) {
635
+ const declared = declaredContributions(packRoot, read.manifest);
636
+ for (const d of declared) {
637
+ if (!existsSync(d.abs)) {
546
638
  issues.push({
547
- code: 'missing-signature',
548
- message: 'Pack is missing dist/manifest.json run `shrk pack sign` first',
639
+ code: 'missing-asset',
640
+ message: `manifest ${d.key} declares ${d.rel}, but the file is missing`,
549
641
  severity: 'error',
550
642
  });
551
643
  }
552
644
  }
645
+ if (flagBool(args, 'require-signature') && !read.manifest?.signature) {
646
+ issues.push({
647
+ code: 'missing-signature',
648
+ message: 'Pack has no signed manifest — run `shrk packs sign <pack>` first',
649
+ severity: 'error',
650
+ });
651
+ }
652
+ const coverage = [];
553
653
  const wantLoad = flagBool(args, 'load') || flagBool(args, 'trusted-load');
554
654
  const trustedLoad = flagBool(args, 'trusted-load');
555
655
  const loadResults = [];
656
+ const rejectedKinds = new Set();
556
657
  if (wantLoad) {
557
- const r = await runRuntimePackTest({ packRoot, trustedLoad });
658
+ const r = await runRuntimePackTest({ packRoot, trustedLoad, manifestPath: read.manifestPath, declared });
558
659
  issues.push(...r.issues);
559
660
  loadResults.push(...r.modules);
661
+ for (const k of r.rejectedKinds)
662
+ rejectedKinds.add(k);
663
+ const contributionModules = r.modules.filter((m) => m.kind !== 'plugin-entry');
664
+ coverage.push({
665
+ unit: 'contribution modules',
666
+ expected: contributionModules.length,
667
+ examined: contributionModules.filter((m) => m.loaded).length,
668
+ reason: contributionModules.length === 0 ? 'the manifest declares no importable contribution file' : 'failed to import',
669
+ ...allowEmptyValve(args, contributionModules.length),
670
+ });
560
671
  }
672
+ let typecheck;
673
+ if (flagBool(args, 'typecheck')) {
674
+ typecheck = typecheckPackAssets({ packageRoot: packRoot, manifestPath: read.manifestPath, manifest: read.manifest });
675
+ for (const e of typecheck.errors) {
676
+ issues.push({
677
+ code: 'typecheck-error',
678
+ message: `${nodePath.relative(packRoot, e.file) || e.file}:${e.line}:${e.column} TS${e.code} ${e.message}`,
679
+ severity: 'error',
680
+ });
681
+ }
682
+ coverage.push({
683
+ unit: 'TS files',
684
+ expected: typecheck.checkedFiles.length,
685
+ examined: typecheck.ran ? typecheck.checkedFiles.length : 0,
686
+ root: packRoot,
687
+ ...(typecheck.note ? { reason: typecheck.note } : {}),
688
+ });
689
+ }
690
+ // What the default run (no --load / --typecheck) examines: every declared
691
+ // contribution file's existence. A manifest declaring none examined
692
+ // nothing: 2, unless --allow-empty accepts it explicitly.
693
+ coverage.push({
694
+ unit: 'declared contribution files',
695
+ expected: declared.length,
696
+ examined: declared.length,
697
+ ...(declared.length === 0
698
+ ? { reason: read.manifest ? 'the manifest declares no contribution file' : 'no manifest to read contributions from' }
699
+ : {}),
700
+ ...allowEmptyValve(args, declared.length),
701
+ });
561
702
  const counts = {
562
- assets: requiredAssets.filter((rel) => existsSync(nodePath.join(packRoot, rel))).length,
563
- total: requiredAssets.length,
703
+ assets: declared.filter((d) => existsSync(d.abs)).length,
704
+ total: declared.length,
564
705
  };
706
+ // The runtime validator caught a rejected entry; an annotated asset makes
707
+ // `--typecheck` catch it at build time (round 12, 12.1f) — say so once per kind.
708
+ const suggestions = rejectedKinds.size > 0 && !flagBool(args, 'typecheck')
709
+ ? [...rejectedKinds].sort().map((k) => rejectedEntryTypecheckHint(k, target))
710
+ : [];
565
711
  const errors = issues.filter((i) => i.severity === 'error');
712
+ const settled = settleVerdict(errors.length === 0 ? 0 : 1, coverage);
566
713
  if (flagBool(args, 'json')) {
567
714
  process.stdout.write(asJson({
568
715
  packRoot,
569
716
  packageName: pkg.name,
717
+ manifest: read.manifestPath,
570
718
  counts,
571
719
  issues,
572
720
  loaded: wantLoad,
573
721
  trustedLoad,
574
722
  modules: loadResults,
575
- passed: errors.length === 0,
723
+ ...(typecheck
724
+ ? {
725
+ typecheck: {
726
+ ran: typecheck.ran,
727
+ checkedFiles: typecheck.checkedFiles.map((f) => nodePath.relative(packRoot, f) || f),
728
+ errors: typecheck.errors.length,
729
+ ...(typecheck.note ? { note: typecheck.note } : {}),
730
+ },
731
+ }
732
+ : {}),
733
+ ...(suggestions.length > 0 ? { suggestions } : {}),
734
+ passed: settled.exit === 0,
735
+ exitCode: settled.exit,
736
+ verdict: settled.verdict,
737
+ shortfalls: settled.shortfalls,
576
738
  }) + '\n');
577
- return errors.length === 0 ? 0 : 1;
739
+ return settled.exit;
578
740
  }
579
741
  process.stdout.write(header(`Pack test: ${pkg.name ?? '(unknown)'}`));
580
742
  process.stdout.write(kv('packRoot', packRoot) + '\n');
581
- process.stdout.write(kv('assets', `${counts.assets}/${counts.total}`) + '\n');
743
+ process.stdout.write(kv('manifest', read.manifestPath ?? '(none)') + '\n');
744
+ process.stdout.write(kv('assets', `${counts.assets}/${counts.total} declared contribution file(s) present`) + '\n');
582
745
  if (wantLoad) {
583
746
  process.stdout.write(kv('mode', trustedLoad ? 'load (trusted)' : 'load (read-only)') + '\n');
584
747
  process.stdout.write(kv('modules', String(loadResults.length)) + '\n');
585
748
  }
586
- if (issues.length === 0) {
587
- process.stdout.write('\nNo issues found.\n');
588
- return 0;
749
+ if (typecheck) {
750
+ process.stdout.write(kv('typecheck', `${typecheck.ran ? typecheck.checkedFiles.length : 0} TS file(s) checked, ${typecheck.errors.length} error(s)`) + '\n');
589
751
  }
590
752
  for (const i of issues) {
591
753
  process.stdout.write(` ${i.severity.toUpperCase().padEnd(8)} ${i.code.padEnd(28)} ${i.message}\n`);
592
754
  }
593
- process.stdout.write(`\nVerdict: ${errors.length === 0 ? 'OK ✓' : 'pack has issues'}\n`);
594
- return errors.length === 0 ? 0 : 1;
755
+ for (const s of suggestions)
756
+ process.stdout.write(` ↳ ${s}\n`);
757
+ if (settled.exit === 1)
758
+ process.stdout.write('\nVerdict: pack has issues\n');
759
+ const line = verdictLine(settled, issues.length === 0 ? '\nNo issues found.' : '\nVerdict: OK ✓');
760
+ if (line)
761
+ process.stdout.write(`${settled.exit === 0 ? '' : '\n'}${line}\n`);
762
+ if (settled.exit === 2 && declared.length === 0) {
763
+ process.stdout.write(`Pass --${ALLOW_EMPTY_FLAG} to accept a pack that declares no contribution file explicitly.\n`);
764
+ }
765
+ return settled.exit;
595
766
  },
596
767
  };
597
768
  async function runRuntimePackTest(input) {
598
769
  const { packRoot, trustedLoad } = input;
599
770
  const issues = [];
600
771
  const modules = [];
601
- const fs = await import('node:fs');
772
+ const rejectedKinds = new Set();
602
773
  // We require Bun for TS module evaluation; document the limitation and bail
603
774
  // gracefully when running under plain Node.
604
775
  const isBun = typeof globalThis.Bun !== 'undefined';
605
776
  if (!isBun) {
606
777
  issues.push({
607
778
  code: 'runtime-load-requires-bun',
608
- message: 'pack test --load can only evaluate raw .ts assets under Bun. Run under Bun (`bun run shrk pack test ...`) or pre-build the pack to dist/.',
779
+ message: 'packs test --load can only evaluate raw .ts assets under Bun. Run under Bun (`bun run shrk packs test ...`) or pre-build the pack to dist/.',
609
780
  severity: 'warning',
610
781
  });
611
782
  }
612
- // Plugin entry: import to ensure asset wiring resolves.
613
- const entry = nodePath.join(packRoot, 'src', 'sharkcraft.plugin.ts');
614
- if (existsSync(entry)) {
783
+ // The manifest module (TS/JS): import it and validate what it exports.
784
+ const entry = input.manifestPath;
785
+ if (entry && !entry.endsWith('.json') && existsSync(entry)) {
786
+ const relativePath = nodePath.relative(packRoot, entry) || entry;
615
787
  try {
616
- const { pathToFileURL } = await import('node:url');
617
788
  const mod = (await importModuleViaLoader(entry));
618
789
  const value = mod.default ?? mod;
619
- const shape = describeShape(value);
620
- modules.push({
621
- relativePath: 'src/sharkcraft.plugin.ts',
622
- kind: 'plugin-entry',
623
- loaded: true,
624
- exportShape: shape,
625
- });
626
- if (typeof value !== 'object' || value === null) {
790
+ modules.push({ relativePath, kind: 'plugin-entry', loaded: true, exportShape: describeShape(value) });
791
+ const v = validatePackManifest(value);
792
+ if (!v.valid) {
627
793
  issues.push({
628
794
  code: 'plugin-entry-shape',
629
- message: 'src/sharkcraft.plugin.ts default export must be an object',
795
+ message: `${relativePath} default export is not a valid pack manifest: ${v.issues.map((i) => `${i.field}: ${i.message}`).join('; ')}`,
630
796
  severity: 'error',
631
797
  });
632
798
  }
633
799
  }
634
800
  catch (e) {
635
- modules.push({
636
- relativePath: 'src/sharkcraft.plugin.ts',
637
- kind: 'plugin-entry',
638
- loaded: false,
639
- error: e.message,
640
- });
801
+ modules.push({ relativePath, kind: 'plugin-entry', loaded: false, error: e.message });
641
802
  issues.push({
642
803
  code: 'plugin-entry-throw',
643
- message: `failed to import plugin entry: ${e.message}`,
804
+ message: `failed to import the manifest ${relativePath}: ${e.message}`,
644
805
  severity: 'error',
645
806
  });
646
807
  }
647
808
  }
648
- // Asset files: every required asset should export an array.
649
- const assetRels = [
650
- { rel: 'src/assets/knowledge.ts', kind: 'knowledge' },
651
- { rel: 'src/assets/rules.ts', kind: 'rule' },
652
- { rel: 'src/assets/paths.ts', kind: 'path' },
653
- { rel: 'src/assets/templates.ts', kind: 'template' },
654
- { rel: 'src/assets/pipelines.ts', kind: 'pipeline' },
655
- { rel: 'src/assets/presets.ts', kind: 'preset' },
656
- { rel: 'src/assets/boundaries.ts', kind: 'boundary' },
657
- ];
658
- for (const a of assetRels) {
659
- const full = nodePath.join(packRoot, a.rel);
660
- if (!existsSync(full))
809
+ // Every declared contribution module (docs / markdown are data, not modules).
810
+ for (const d of input.declared) {
811
+ if (!MODULE_FILE.test(d.rel) || !existsSync(d.abs))
661
812
  continue;
813
+ const kind = ARRAY_OF_IDS_SLOTS[d.key] ?? d.key;
814
+ const relativePath = nodePath.relative(packRoot, d.abs) || d.rel;
662
815
  try {
663
- const { pathToFileURL } = await import('node:url');
664
- const mod = (await importModuleViaLoader(full));
816
+ const mod = (await importModuleViaLoader(d.abs));
665
817
  const value = mod.default;
666
818
  const arr = Array.isArray(value) ? value : null;
819
+ // THE runtime loader (or acceptance predicate) of this slot (round 12,
820
+ // 12.1c / 12.1f): an unannotated literal missing a required field is
821
+ // refused here exactly as at load — the old check saw only `id`.
822
+ const validation = await validateContributionFile(d.key, d.abs);
823
+ const rejectedAt = new Set();
824
+ for (const r of validation.rejected) {
825
+ if (r.exportName === undefined || r.exportName === 'default')
826
+ rejectedAt.add(r.index);
827
+ issues.push({
828
+ code: 'asset-entry-rejected',
829
+ message: `${relativePath} ${formatEntryRejection(r)} — the ${validation.kind ?? d.key} loader refuses it, so it would not take effect`,
830
+ severity: 'error',
831
+ });
832
+ }
833
+ if (validation.rejected.length > 0 && validation.kind)
834
+ rejectedKinds.add(validation.kind);
667
835
  modules.push({
668
- relativePath: a.rel,
669
- kind: a.kind,
836
+ relativePath,
837
+ kind,
670
838
  loaded: true,
671
839
  ...(arr ? { arrayLength: arr.length } : {}),
672
840
  exportShape: describeShape(value),
841
+ ...(validation.unvalidated ? {} : { accepted: validation.accepted, rejected: validation.rejected.length }),
673
842
  });
843
+ if (!(d.key in ARRAY_OF_IDS_SLOTS))
844
+ continue;
674
845
  if (value === undefined) {
675
- issues.push({
676
- code: 'asset-no-default-export',
677
- message: `${a.rel} has no default export`,
678
- severity: 'error',
679
- });
846
+ issues.push({ code: 'asset-no-default-export', message: `${relativePath} has no default export`, severity: 'error' });
680
847
  continue;
681
848
  }
682
849
  if (!arr) {
683
850
  issues.push({
684
851
  code: 'asset-not-array',
685
- message: `${a.rel} default export must be an array, got ${describeShape(value)}`,
852
+ message: `${relativePath} default export must be an array, got ${describeShape(value)}`,
686
853
  severity: 'error',
687
854
  });
688
855
  continue;
689
856
  }
690
- // Validate that each item has an `id` string.
857
+ // Validate that each item has an `id` string (an entry the runtime
858
+ // loader already refused above is reported once, as that).
691
859
  for (let i = 0; i < arr.length; i += 1) {
860
+ if (rejectedAt.has(i))
861
+ continue;
692
862
  const item = arr[i];
693
863
  if (!item || typeof item !== 'object' || typeof item.id !== 'string') {
694
864
  issues.push({
695
865
  code: 'asset-item-missing-id',
696
- message: `${a.rel}[${i}] is missing a string \`id\``,
866
+ message: `${relativePath}[${i}] is missing a string \`id\``,
697
867
  severity: 'error',
698
868
  });
699
869
  break;
700
870
  }
701
871
  }
702
- if (a.kind === 'template' && trustedLoad) {
872
+ if (kind === 'template' && trustedLoad) {
703
873
  // Best-effort: attempt to render each template's targetPath/content with
704
874
  // its default/sample variables. Wrapped in try/catch — any throw is an
705
875
  // error issue.
706
876
  for (const t of arr) {
707
877
  const id = String(t.id ?? '?');
708
878
  const vars = {};
709
- const declared = t.variables ?? [];
710
- for (const v of declared) {
879
+ const declaredVars = t.variables ?? [];
880
+ for (const v of declaredVars) {
711
881
  if (typeof v.name !== 'string')
712
882
  continue;
713
883
  vars[v.name] = v.default ?? defaultVar(v.name);
@@ -729,7 +899,7 @@ async function runRuntimePackTest(input) {
729
899
  }
730
900
  }
731
901
  }
732
- if (a.kind === 'pipeline') {
902
+ if (kind === 'pipeline') {
733
903
  for (const p of arr) {
734
904
  if (!Array.isArray(p.steps) || p.steps.length === 0) {
735
905
  issues.push({
@@ -742,20 +912,15 @@ async function runRuntimePackTest(input) {
742
912
  }
743
913
  }
744
914
  catch (e) {
745
- modules.push({
746
- relativePath: a.rel,
747
- kind: a.kind,
748
- loaded: false,
749
- error: e.message,
750
- });
915
+ modules.push({ relativePath, kind, loaded: false, error: e.message });
751
916
  issues.push({
752
917
  code: 'asset-throw',
753
- message: `failed to import ${a.rel}: ${e.message}`,
918
+ message: `failed to import ${relativePath}: ${e.message}`,
754
919
  severity: 'error',
755
920
  });
756
921
  }
757
922
  }
758
- return { issues, modules };
923
+ return { issues, modules, rejectedKinds: [...rejectedKinds] };
759
924
  }
760
925
  function describeShape(v) {
761
926
  if (v === null)