@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.
- package/dist/asset-preview/apply-asset-preview.js +1 -1
- package/dist/authoring/authoring-kit.d.ts +9 -1
- package/dist/authoring/authoring-kit.d.ts.map +1 -1
- package/dist/authoring/authoring-kit.js +26 -13
- package/dist/bootstrap/unlinked-workspace-dependency.d.ts +11 -0
- package/dist/bootstrap/unlinked-workspace-dependency.d.ts.map +1 -0
- package/dist/bootstrap/unlinked-workspace-dependency.js +99 -0
- package/dist/command-registry.d.ts +100 -1
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +130 -3
- package/dist/commands/api-diff.command.d.ts.map +1 -1
- package/dist/commands/api-diff.command.js +12 -0
- package/dist/commands/apply.command.d.ts.map +1 -1
- package/dist/commands/apply.command.js +3 -0
- package/dist/commands/arch.command.d.ts.map +1 -1
- package/dist/commands/arch.command.js +26 -7
- package/dist/commands/architecture.command.d.ts.map +1 -1
- package/dist/commands/architecture.command.js +8 -2
- package/dist/commands/baseline.command.d.ts +54 -3
- package/dist/commands/baseline.command.d.ts.map +1 -1
- package/dist/commands/baseline.command.js +582 -74
- package/dist/commands/biome.command.d.ts.map +1 -1
- package/dist/commands/biome.command.js +20 -0
- package/dist/commands/boundaries.command.d.ts +9 -1
- package/dist/commands/boundaries.command.d.ts.map +1 -1
- package/dist/commands/boundaries.command.js +88 -20
- package/dist/commands/bundle.command.d.ts.map +1 -1
- package/dist/commands/bundle.command.js +57 -11
- package/dist/commands/cache-align.command.d.ts.map +1 -1
- package/dist/commands/cache-align.command.js +3 -1
- package/dist/commands/changelog-data.d.ts.map +1 -1
- package/dist/commands/changelog-data.js +138 -0
- package/dist/commands/changes.command.d.ts.map +1 -1
- package/dist/commands/changes.command.js +15 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +1241 -322
- package/dist/commands/checks.command.d.ts.map +1 -1
- package/dist/commands/checks.command.js +188 -40
- package/dist/commands/ci.command.d.ts.map +1 -1
- package/dist/commands/ci.command.js +35 -2
- package/dist/commands/code-intel.command.d.ts.map +1 -1
- package/dist/commands/code-intel.command.js +3 -0
- package/dist/commands/command-catalog.d.ts +23 -1
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +152 -35
- package/dist/commands/commands.command.d.ts +21 -3
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +172 -27
- package/dist/commands/compress.command.d.ts.map +1 -1
- package/dist/commands/compress.command.js +4 -0
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +50 -12
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +40 -5
- package/dist/commands/contract-templates.command.d.ts.map +1 -1
- package/dist/commands/contract-templates.command.js +9 -1
- package/dist/commands/contract.command.d.ts.map +1 -1
- package/dist/commands/contract.command.js +35 -0
- package/dist/commands/conventions.command.d.ts.map +1 -1
- package/dist/commands/conventions.command.js +189 -29
- package/dist/commands/coverage.command.d.ts.map +1 -1
- package/dist/commands/coverage.command.js +9 -0
- package/dist/commands/daily.commands.d.ts +0 -1
- package/dist/commands/daily.commands.d.ts.map +1 -1
- package/dist/commands/daily.commands.js +12 -76
- package/dist/commands/dashboard.command.d.ts.map +1 -1
- package/dist/commands/dashboard.command.js +10 -0
- package/dist/commands/dev.command.d.ts.map +1 -1
- package/dist/commands/dev.command.js +45 -2
- package/dist/commands/diagnostics.command.d.ts +0 -2
- package/dist/commands/diagnostics.command.d.ts.map +1 -1
- package/dist/commands/diagnostics.command.js +5 -78
- package/dist/commands/diff-check.command.d.ts +6 -4
- package/dist/commands/diff-check.command.d.ts.map +1 -1
- package/dist/commands/diff-check.command.js +190 -103
- package/dist/commands/docs-references.command.d.ts +13 -0
- package/dist/commands/docs-references.command.d.ts.map +1 -1
- package/dist/commands/docs-references.command.js +269 -72
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +58 -4
- package/dist/commands/drift.command.d.ts.map +1 -1
- package/dist/commands/drift.command.js +17 -4
- package/dist/commands/eslint.command.d.ts.map +1 -1
- package/dist/commands/eslint.command.js +20 -0
- package/dist/commands/export.command.d.ts.map +1 -1
- package/dist/commands/export.command.js +23 -1
- package/dist/commands/feedback-dispatch.command.d.ts.map +1 -1
- package/dist/commands/feedback-dispatch.command.js +29 -0
- package/dist/commands/feedback.command.d.ts.map +1 -1
- package/dist/commands/feedback.command.js +6 -1
- package/dist/commands/finish.command.d.ts.map +1 -1
- package/dist/commands/finish.command.js +32 -8
- package/dist/commands/fix.command.d.ts.map +1 -1
- package/dist/commands/fix.command.js +19 -0
- package/dist/commands/gate.command.d.ts +6 -1
- package/dist/commands/gate.command.d.ts.map +1 -1
- package/dist/commands/gate.command.js +50 -124
- package/dist/commands/gates.command.d.ts +52 -0
- package/dist/commands/gates.command.d.ts.map +1 -1
- package/dist/commands/gates.command.js +811 -185
- package/dist/commands/gen.command.d.ts.map +1 -1
- package/dist/commands/gen.command.js +4 -0
- package/dist/commands/generated.command.d.ts +33 -3
- package/dist/commands/generated.command.d.ts.map +1 -1
- package/dist/commands/generated.command.js +499 -147
- package/dist/commands/graph-code-subverbs.d.ts +15 -0
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
- package/dist/commands/graph-code-subverbs.js +198 -24
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +79 -37
- package/dist/commands/help.command.d.ts +30 -6
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +323 -199
- package/dist/commands/helper.command.d.ts +1 -0
- package/dist/commands/helper.command.d.ts.map +1 -1
- package/dist/commands/helper.command.js +195 -56
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +115 -12
- package/dist/commands/infer.command.d.ts.map +1 -1
- package/dist/commands/infer.command.js +20 -45
- package/dist/commands/ingest.command.d.ts +0 -1
- package/dist/commands/ingest.command.d.ts.map +1 -1
- package/dist/commands/ingest.command.js +25 -41
- package/dist/commands/knowledge-author.command.d.ts +0 -3
- package/dist/commands/knowledge-author.command.d.ts.map +1 -1
- package/dist/commands/knowledge-author.command.js +10 -23
- package/dist/commands/knowledge.command.d.ts.map +1 -1
- package/dist/commands/knowledge.command.js +469 -129
- package/dist/commands/languages.command.d.ts.map +1 -1
- package/dist/commands/languages.command.js +27 -0
- package/dist/commands/lint.command.d.ts.map +1 -1
- package/dist/commands/lint.command.js +3 -0
- package/dist/commands/mcp.command.d.ts +18 -0
- package/dist/commands/mcp.command.d.ts.map +1 -1
- package/dist/commands/mcp.command.js +33 -8
- package/dist/commands/onboard.command.d.ts.map +1 -1
- package/dist/commands/onboard.command.js +20 -0
- package/dist/commands/owners.command.d.ts.map +1 -1
- package/dist/commands/owners.command.js +14 -2
- package/dist/commands/packs-new.d.ts +13 -1
- package/dist/commands/packs-new.d.ts.map +1 -1
- package/dist/commands/packs-new.js +500 -335
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +378 -36
- package/dist/commands/paths.command.d.ts.map +1 -1
- package/dist/commands/paths.command.js +9 -1
- package/dist/commands/pipelines.command.d.ts.map +1 -1
- package/dist/commands/pipelines.command.js +7 -1
- package/dist/commands/plan-check.command.d.ts.map +1 -1
- package/dist/commands/plan-check.command.js +3 -0
- package/dist/commands/plan.command.d.ts.map +1 -1
- package/dist/commands/plan.command.js +3 -0
- package/dist/commands/playbooks.command.d.ts.map +1 -1
- package/dist/commands/playbooks.command.js +7 -2
- package/dist/commands/policy-lint.command.d.ts +19 -2
- package/dist/commands/policy-lint.command.d.ts.map +1 -1
- package/dist/commands/policy-lint.command.js +224 -65
- package/dist/commands/policy.command.d.ts.map +1 -1
- package/dist/commands/policy.command.js +6 -1
- package/dist/commands/pr.command.d.ts.map +1 -1
- package/dist/commands/pr.command.js +14 -0
- package/dist/commands/presets.command.d.ts.map +1 -1
- package/dist/commands/presets.command.js +12 -27
- package/dist/commands/profiles.command.d.ts.map +1 -1
- package/dist/commands/profiles.command.js +94 -26
- package/dist/commands/provenance.command.js +1 -1
- package/dist/commands/quality.command.d.ts.map +1 -1
- package/dist/commands/quality.command.js +129 -33
- package/dist/commands/recommend.command.d.ts +20 -4
- package/dist/commands/recommend.command.d.ts.map +1 -1
- package/dist/commands/recommend.command.js +172 -278
- package/dist/commands/registrations.command.d.ts.map +1 -1
- package/dist/commands/registrations.command.js +138 -13
- package/dist/commands/registry-lifecycle-run.d.ts +14 -0
- package/dist/commands/registry-lifecycle-run.d.ts.map +1 -0
- package/dist/commands/registry-lifecycle-run.js +336 -0
- package/dist/commands/registry.command.d.ts.map +1 -1
- package/dist/commands/registry.command.js +235 -62
- package/dist/commands/release.command.d.ts.map +1 -1
- package/dist/commands/release.command.js +99 -20
- package/dist/commands/report.command.d.ts.map +1 -1
- package/dist/commands/report.command.js +38 -2
- package/dist/commands/reuse-coverage.command.d.ts +23 -0
- package/dist/commands/reuse-coverage.command.d.ts.map +1 -0
- package/dist/commands/reuse-coverage.command.js +536 -0
- package/dist/commands/reuse.command.d.ts +3 -18
- package/dist/commands/reuse.command.d.ts.map +1 -1
- package/dist/commands/reuse.command.js +395 -244
- package/dist/commands/review.command.d.ts.map +1 -1
- package/dist/commands/review.command.js +33 -1
- package/dist/commands/rounds.command.d.ts.map +1 -1
- package/dist/commands/rounds.command.js +5 -0
- package/dist/commands/safety.command.d.ts.map +1 -1
- package/dist/commands/safety.command.js +9 -0
- package/dist/commands/scaffolds.command.d.ts.map +1 -1
- package/dist/commands/scaffolds.command.js +97 -25
- package/dist/commands/search.command.d.ts +0 -8
- package/dist/commands/search.command.d.ts.map +1 -1
- package/dist/commands/search.command.js +136 -28
- package/dist/commands/self-config-xrefs.command.d.ts +24 -0
- package/dist/commands/self-config-xrefs.command.d.ts.map +1 -0
- package/dist/commands/self-config-xrefs.command.js +170 -0
- package/dist/commands/self-config.command.d.ts.map +1 -1
- package/dist/commands/self-config.command.js +261 -65
- package/dist/commands/smart-context.command.d.ts.map +1 -1
- package/dist/commands/smart-context.command.js +39 -3
- package/dist/commands/spec.command.d.ts.map +1 -1
- package/dist/commands/spec.command.js +3 -0
- package/dist/commands/stats.command.d.ts.map +1 -1
- package/dist/commands/stats.command.js +3 -0
- package/dist/commands/surface.command.d.ts +6 -1
- package/dist/commands/surface.command.d.ts.map +1 -1
- package/dist/commands/surface.command.js +256 -54
- package/dist/commands/task-context.command.js +1 -1
- package/dist/commands/task.command.d.ts.map +1 -1
- package/dist/commands/task.command.js +22 -1
- package/dist/commands/templates.command.d.ts +4 -0
- package/dist/commands/templates.command.d.ts.map +1 -1
- package/dist/commands/templates.command.js +109 -22
- package/dist/commands/test.command.d.ts.map +1 -1
- package/dist/commands/test.command.js +161 -33
- package/dist/commands/tests.command.d.ts.map +1 -1
- package/dist/commands/tests.command.js +29 -3
- package/dist/commands/trace.command.d.ts.map +1 -1
- package/dist/commands/trace.command.js +29 -4
- package/dist/commands/watch.command.d.ts.map +1 -1
- package/dist/commands/watch.command.js +3 -0
- package/dist/commands/why.command.d.ts.map +1 -1
- package/dist/commands/why.command.js +3 -0
- package/dist/commands/wiring.command.d.ts +18 -4
- package/dist/commands/wiring.command.d.ts.map +1 -1
- package/dist/commands/wiring.command.js +360 -73
- package/dist/dashboard/code-intelligence-data.js +4 -4
- package/dist/diff/deleted-orphan-coverage.d.ts +32 -0
- package/dist/diff/deleted-orphan-coverage.d.ts.map +1 -0
- package/dist/diff/deleted-orphan-coverage.js +107 -0
- package/dist/diff/deleted-orphan-scope-notes.d.ts +23 -0
- package/dist/diff/deleted-orphan-scope-notes.d.ts.map +1 -0
- package/dist/diff/deleted-orphan-scope-notes.js +49 -0
- package/dist/diff/deleted-orphans.d.ts +32 -0
- package/dist/diff/deleted-orphans.d.ts.map +1 -1
- package/dist/diff/deleted-orphans.js +42 -2
- package/dist/dispatch/closest-match.d.ts +15 -0
- package/dist/dispatch/closest-match.d.ts.map +1 -0
- package/dist/dispatch/closest-match.js +47 -0
- package/dist/dispatch/global-flags.d.ts +94 -0
- package/dist/dispatch/global-flags.d.ts.map +1 -0
- package/dist/dispatch/global-flags.js +123 -0
- package/dist/dispatch/guard-invocation-input.d.ts +24 -0
- package/dist/dispatch/guard-invocation-input.d.ts.map +1 -0
- package/dist/dispatch/guard-invocation-input.js +1 -0
- package/dist/dispatch/guard-invocation.d.ts +33 -0
- package/dist/dispatch/guard-invocation.d.ts.map +1 -0
- package/dist/dispatch/guard-invocation.js +183 -0
- package/dist/dispatch/help-intercept.d.ts +22 -0
- package/dist/dispatch/help-intercept.d.ts.map +1 -0
- package/dist/dispatch/help-intercept.js +42 -0
- package/dist/dispatch/i-declared-invocation.d.ts +17 -0
- package/dist/dispatch/i-declared-invocation.d.ts.map +1 -0
- package/dist/dispatch/i-declared-invocation.js +1 -0
- package/dist/dispatch/i-unknown-flag-refusal-input.d.ts +28 -0
- package/dist/dispatch/i-unknown-flag-refusal-input.d.ts.map +1 -0
- package/dist/dispatch/i-unknown-flag-refusal-input.js +1 -0
- package/dist/dispatch/invocation-rejection-kind.d.ts +15 -0
- package/dist/dispatch/invocation-rejection-kind.d.ts.map +1 -0
- package/dist/dispatch/invocation-rejection-kind.js +15 -0
- package/dist/dispatch/invocation-rejection.d.ts +18 -0
- package/dist/dispatch/invocation-rejection.d.ts.map +1 -0
- package/dist/dispatch/invocation-rejection.js +1 -0
- package/dist/dispatch/judge-invocation.d.ts +24 -0
- package/dist/dispatch/judge-invocation.d.ts.map +1 -0
- package/dist/dispatch/judge-invocation.js +50 -0
- package/dist/dispatch/positional-mode.d.ts +18 -0
- package/dist/dispatch/positional-mode.d.ts.map +1 -0
- package/dist/dispatch/positional-mode.js +18 -0
- package/dist/dispatch/read-tracking-map.d.ts +28 -0
- package/dist/dispatch/read-tracking-map.d.ts.map +1 -0
- package/dist/dispatch/read-tracking-map.js +58 -0
- package/dist/dispatch/refuse-sibling-valve-flags.d.ts +30 -0
- package/dist/dispatch/refuse-sibling-valve-flags.d.ts.map +1 -0
- package/dist/dispatch/refuse-sibling-valve-flags.js +65 -0
- package/dist/dispatch/subverb-spec.d.ts +26 -0
- package/dist/dispatch/subverb-spec.d.ts.map +1 -0
- package/dist/dispatch/subverb-spec.js +1 -0
- package/dist/dispatch/undocumented-flag-reads.d.ts +22 -0
- package/dist/dispatch/undocumented-flag-reads.d.ts.map +1 -0
- package/dist/dispatch/undocumented-flag-reads.js +283 -0
- package/dist/dispatch/unknown-flag-refusal-mode.d.ts +13 -0
- package/dist/dispatch/unknown-flag-refusal-mode.d.ts.map +1 -0
- package/dist/dispatch/unknown-flag-refusal-mode.js +13 -0
- package/dist/dispatch/unknown-flag-refusal.d.ts +22 -0
- package/dist/dispatch/unknown-flag-refusal.d.ts.map +1 -0
- package/dist/dispatch/unknown-flag-refusal.js +57 -0
- package/dist/dispatch/unread-flags.d.ts +152 -0
- package/dist/dispatch/unread-flags.d.ts.map +1 -0
- package/dist/dispatch/unread-flags.js +295 -0
- package/dist/dispatch/verdict-valve-flag.d.ts +21 -0
- package/dist/dispatch/verdict-valve-flag.d.ts.map +1 -0
- package/dist/dispatch/verdict-valve-flag.js +21 -0
- package/dist/dispatch/walk-declared-subverbs.d.ts +14 -0
- package/dist/dispatch/walk-declared-subverbs.d.ts.map +1 -0
- package/dist/dispatch/walk-declared-subverbs.js +28 -0
- package/dist/exit-codes.d.ts +41 -3
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +153 -14
- package/dist/finish/run-finish.d.ts +72 -5
- package/dist/finish/run-finish.d.ts.map +1 -1
- package/dist/finish/run-finish.js +453 -104
- package/dist/gates/accepted-empty-note.d.ts +10 -0
- package/dist/gates/accepted-empty-note.d.ts.map +1 -0
- package/dist/gates/accepted-empty-note.js +11 -0
- package/dist/gates/allow-empty.d.ts +25 -0
- package/dist/gates/allow-empty.d.ts.map +1 -0
- package/dist/gates/allow-empty.js +27 -0
- package/dist/gates/asset-doctor-failing-units.d.ts +16 -0
- package/dist/gates/asset-doctor-failing-units.d.ts.map +1 -0
- package/dist/gates/asset-doctor-failing-units.js +14 -0
- package/dist/gates/asset-doctor-failure-line.d.ts +16 -0
- package/dist/gates/asset-doctor-failure-line.d.ts.map +1 -0
- package/dist/gates/asset-doctor-failure-line.js +28 -0
- package/dist/gates/empty-rule-advice-lines.d.ts +15 -0
- package/dist/gates/empty-rule-advice-lines.d.ts.map +1 -0
- package/dist/gates/empty-rule-advice-lines.js +20 -0
- package/dist/gates/gate-envelope.d.ts +85 -8
- package/dist/gates/gate-envelope.d.ts.map +1 -1
- package/dist/gates/gate-envelope.js +56 -7
- package/dist/gates/gate-rule-globs.d.ts +37 -3
- package/dist/gates/gate-rule-globs.d.ts.map +1 -1
- package/dist/gates/gate-rule-globs.js +78 -40
- package/dist/gates/gate-rule-view.d.ts +17 -4
- package/dist/gates/gate-rule-view.d.ts.map +1 -1
- package/dist/gates/gate-rule-view.js +105 -90
- package/dist/gates/i-coverage-dead-glob.d.ts +19 -0
- package/dist/gates/i-coverage-dead-glob.d.ts.map +1 -0
- package/dist/gates/i-coverage-dead-glob.js +1 -0
- package/dist/gates/i-coverage-negation.d.ts +13 -0
- package/dist/gates/i-coverage-negation.d.ts.map +1 -0
- package/dist/gates/i-coverage-negation.js +1 -0
- package/dist/gates/i-registration-query-settle-input.d.ts +18 -0
- package/dist/gates/i-registration-query-settle-input.d.ts.map +1 -0
- package/dist/gates/i-registration-query-settle-input.js +1 -0
- package/dist/gates/i-unit-state-note-row.d.ts +16 -0
- package/dist/gates/i-unit-state-note-row.d.ts.map +1 -0
- package/dist/gates/i-unit-state-note-row.js +1 -0
- package/dist/gates/i-unit-state-notes.d.ts +32 -0
- package/dist/gates/i-unit-state-notes.d.ts.map +1 -0
- package/dist/gates/i-unit-state-notes.js +1 -0
- package/dist/gates/incomplete-registry-inventory.d.ts +19 -0
- package/dist/gates/incomplete-registry-inventory.d.ts.map +1 -0
- package/dist/gates/incomplete-registry-inventory.js +63 -0
- package/dist/gates/measure-registration-roles.d.ts +10 -0
- package/dist/gates/measure-registration-roles.d.ts.map +1 -0
- package/dist/gates/measure-registration-roles.js +9 -0
- package/dist/gates/plane-verdict.d.ts +14 -0
- package/dist/gates/plane-verdict.d.ts.map +1 -0
- package/dist/gates/plane-verdict.js +22 -0
- package/dist/gates/qualify-clean-for-units.d.ts +12 -0
- package/dist/gates/qualify-clean-for-units.d.ts.map +1 -0
- package/dist/gates/qualify-clean-for-units.js +18 -0
- package/dist/gates/registration-graph-verdict.d.ts +34 -0
- package/dist/gates/registration-graph-verdict.d.ts.map +1 -0
- package/dist/gates/registration-graph-verdict.js +70 -0
- package/dist/gates/registration-roles.d.ts +9 -0
- package/dist/gates/registration-roles.d.ts.map +1 -0
- package/dist/gates/registration-roles.js +1 -0
- package/dist/gates/registry-liveness.d.ts +11 -0
- package/dist/gates/registry-liveness.d.ts.map +1 -0
- package/dist/gates/registry-liveness.js +13 -0
- package/dist/gates/rule-coverage.d.ts +187 -4
- package/dist/gates/rule-coverage.d.ts.map +1 -1
- package/dist/gates/rule-coverage.js +501 -49
- package/dist/gates/run-gate-planes.d.ts +14 -0
- package/dist/gates/run-gate-planes.d.ts.map +1 -1
- package/dist/gates/run-gate-planes.js +172 -30
- package/dist/gates/scaffold-selftest.d.ts +56 -0
- package/dist/gates/scaffold-selftest.d.ts.map +1 -0
- package/dist/gates/scaffold-selftest.js +119 -0
- package/dist/gates/seam-rejected-rules.d.ts +28 -0
- package/dist/gates/seam-rejected-rules.d.ts.map +1 -0
- package/dist/gates/seam-rejected-rules.js +95 -0
- package/dist/gates/self-test-check.d.ts +29 -0
- package/dist/gates/self-test-check.d.ts.map +1 -0
- package/dist/gates/self-test-check.js +1 -0
- package/dist/gates/self-test-subject.d.ts +28 -0
- package/dist/gates/self-test-subject.d.ts.map +1 -0
- package/dist/gates/self-test-subject.js +1 -0
- package/dist/gates/self-test.d.ts +20 -0
- package/dist/gates/self-test.d.ts.map +1 -0
- package/dist/gates/self-test.js +82 -0
- package/dist/gates/settle-verdict.d.ts +14 -0
- package/dist/gates/settle-verdict.d.ts.map +1 -0
- package/dist/gates/settle-verdict.js +13 -0
- package/dist/gates/settled-verdict.d.ts +8 -0
- package/dist/gates/settled-verdict.d.ts.map +1 -0
- package/dist/gates/settled-verdict.js +1 -0
- package/dist/gates/template-registry-coverage.d.ts +18 -0
- package/dist/gates/template-registry-coverage.d.ts.map +1 -0
- package/dist/gates/template-registry-coverage.js +24 -0
- package/dist/gates/unit-state-notes.d.ts +40 -0
- package/dist/gates/unit-state-notes.d.ts.map +1 -0
- package/dist/gates/unit-state-notes.js +71 -0
- package/dist/gates/verdict-line.d.ts +18 -0
- package/dist/gates/verdict-line.d.ts.map +1 -0
- package/dist/gates/verdict-line.js +40 -0
- package/dist/graph/graph-reuse-lookup.d.ts +10 -0
- package/dist/graph/graph-reuse-lookup.d.ts.map +1 -0
- package/dist/graph/graph-reuse-lookup.js +41 -0
- package/dist/graph/index-behind-hint.d.ts +11 -0
- package/dist/graph/index-behind-hint.d.ts.map +1 -0
- package/dist/graph/index-behind-hint.js +27 -0
- package/dist/knowledge/knowledge-stale-gate-flags.d.ts +3 -0
- package/dist/knowledge/knowledge-stale-gate-flags.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate-flags.js +1 -0
- package/dist/knowledge/knowledge-stale-gate-input.d.ts +3 -0
- package/dist/knowledge/knowledge-stale-gate-input.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate-input.js +1 -0
- package/dist/knowledge/knowledge-stale-gate-result.d.ts +3 -0
- package/dist/knowledge/knowledge-stale-gate-result.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate-result.js +1 -0
- package/dist/knowledge/knowledge-stale-gate.d.ts +10 -0
- package/dist/knowledge/knowledge-stale-gate.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate.js +9 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +200 -83
- package/dist/output/failure-hints.d.ts.map +1 -1
- package/dist/output/failure-hints.js +11 -9
- package/dist/output/rejected-entries-note.d.ts +57 -0
- package/dist/output/rejected-entries-note.d.ts.map +1 -0
- package/dist/output/rejected-entries-note.js +103 -0
- package/dist/output/stdout-is-pipe.d.ts +13 -0
- package/dist/output/stdout-is-pipe.d.ts.map +1 -0
- package/dist/output/stdout-is-pipe.js +21 -0
- package/dist/packs/stale-build-warning.d.ts +11 -0
- package/dist/packs/stale-build-warning.d.ts.map +1 -0
- package/dist/packs/stale-build-warning.js +23 -0
- package/dist/quality/run-quality.d.ts +123 -0
- package/dist/quality/run-quality.d.ts.map +1 -0
- package/dist/quality/run-quality.js +442 -0
- package/dist/schemas/json-schemas.d.ts +184 -3
- package/dist/schemas/json-schemas.d.ts.map +1 -1
- package/dist/schemas/json-schemas.js +113 -2
- package/dist/shrk.d.ts +3 -0
- package/dist/shrk.d.ts.map +1 -0
- package/dist/shrk.js +30 -0
- package/dist/surface/audience-applicability.d.ts +26 -0
- package/dist/surface/audience-applicability.d.ts.map +1 -0
- package/dist/surface/audience-applicability.js +60 -0
- package/dist/surface/catalog-command-safety.d.ts +18 -0
- package/dist/surface/catalog-command-safety.d.ts.map +1 -0
- package/dist/surface/catalog-command-safety.js +62 -0
- package/dist/surface/cli-command-resolver.d.ts +21 -0
- package/dist/surface/cli-command-resolver.d.ts.map +1 -0
- package/dist/surface/cli-command-resolver.js +87 -0
- package/dist/surface/command-dispatch-kind.d.ts +20 -0
- package/dist/surface/command-dispatch-kind.d.ts.map +1 -0
- package/dist/surface/command-dispatch-kind.js +20 -0
- package/dist/surface/command-index-entry.d.ts +40 -0
- package/dist/surface/command-index-entry.d.ts.map +1 -0
- package/dist/surface/command-index-entry.js +1 -0
- package/dist/surface/command-index.d.ts +77 -0
- package/dist/surface/command-index.d.ts.map +1 -0
- package/dist/surface/command-index.js +323 -0
- package/dist/surface/i-command-index.d.ts +22 -0
- package/dist/surface/i-command-index.d.ts.map +1 -0
- package/dist/surface/i-command-index.js +1 -0
- package/dist/surface/i-command-string-context.d.ts +20 -0
- package/dist/surface/i-command-string-context.d.ts.map +1 -0
- package/dist/surface/i-command-string-context.js +1 -0
- package/dist/surface/i-surface-denial.d.ts +13 -0
- package/dist/surface/i-surface-denial.d.ts.map +1 -0
- package/dist/surface/i-surface-denial.js +1 -0
- package/dist/surface/load-surface-context.d.ts +17 -1
- package/dist/surface/load-surface-context.d.ts.map +1 -1
- package/dist/surface/load-surface-context.js +48 -10
- package/dist/surface/not-enabled-error.d.ts +38 -4
- package/dist/surface/not-enabled-error.d.ts.map +1 -1
- package/dist/surface/not-enabled-error.js +96 -6
- package/dist/surface/profiles.d.ts +7 -0
- package/dist/surface/profiles.d.ts.map +1 -1
- package/dist/surface/resolve-command-string.d.ts +10 -0
- package/dist/surface/resolve-command-string.d.ts.map +1 -0
- package/dist/surface/resolve-command-string.js +572 -0
- package/dist/surface/surface-config-writer.d.ts +7 -2
- package/dist/surface/surface-config-writer.d.ts.map +1 -1
- package/dist/surface/surface-config-writer.js +25 -6
- package/dist/surface/surface-layer.d.ts +11 -0
- package/dist/surface/surface-layer.d.ts.map +1 -0
- package/dist/surface/surface-layer.js +11 -0
- package/dist/surface/surface-refusal-reason.d.ts +16 -0
- package/dist/surface/surface-refusal-reason.d.ts.map +1 -0
- package/dist/surface/surface-refusal-reason.js +16 -0
- package/dist/surface/surface-selector.d.ts +26 -0
- package/dist/surface/surface-selector.d.ts.map +1 -0
- package/dist/surface/surface-selector.js +42 -0
- package/dist/surface/surface-summary.d.ts +61 -18
- package/dist/surface/surface-summary.d.ts.map +1 -1
- package/dist/surface/surface-summary.js +147 -60
- package/dist/surface/tier.d.ts +85 -22
- package/dist/surface/tier.d.ts.map +1 -1
- package/dist/surface/tier.js +132 -30
- package/dist/usage/usage-log.d.ts +6 -1
- package/dist/usage/usage-log.d.ts.map +1 -1
- package/dist/usage/usage-log.js +7 -2
- package/dist/validation/run-validation-loop.d.ts +9 -0
- package/dist/validation/run-validation-loop.d.ts.map +1 -1
- package/dist/validation/run-validation-loop.js +14 -11
- package/dist/validation/typecheck-emitted.d.ts +8 -9
- package/dist/validation/typecheck-emitted.d.ts.map +1 -1
- package/dist/validation/typecheck-emitted.js +17 -97
- 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 {
|
|
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
|
-
/**
|
|
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:
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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: ['
|
|
70
|
+
files: ['src', 'README.md', 'SECURITY.md', 'package.json'],
|
|
37
71
|
};
|
|
38
72
|
const files = [];
|
|
39
|
-
files.push({
|
|
40
|
-
|
|
41
|
-
|
|
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: '
|
|
81
|
+
module: 'ESNext',
|
|
57
82
|
moduleResolution: 'bundler',
|
|
58
83
|
strict: true,
|
|
59
|
-
|
|
60
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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 (
|
|
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/
|
|
100
|
-
body:
|
|
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
|
-
|
|
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
|
|
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 #
|
|
129
|
-
|
|
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
|
-
'
|
|
143
|
-
'shrk
|
|
144
|
-
'shrk
|
|
145
|
-
'shrk
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
if (!existsSync(
|
|
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-
|
|
548
|
-
message:
|
|
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:
|
|
563
|
-
total:
|
|
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
|
-
|
|
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
|
|
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('
|
|
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 (
|
|
587
|
-
process.stdout.write('
|
|
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
|
-
|
|
594
|
-
|
|
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
|
|
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: '
|
|
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
|
-
//
|
|
613
|
-
const entry =
|
|
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
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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:
|
|
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
|
|
804
|
+
message: `failed to import the manifest ${relativePath}: ${e.message}`,
|
|
644
805
|
severity: 'error',
|
|
645
806
|
});
|
|
646
807
|
}
|
|
647
808
|
}
|
|
648
|
-
//
|
|
649
|
-
const
|
|
650
|
-
|
|
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
|
|
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
|
|
669
|
-
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: `${
|
|
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: `${
|
|
866
|
+
message: `${relativePath}[${i}] is missing a string \`id\``,
|
|
697
867
|
severity: 'error',
|
|
698
868
|
});
|
|
699
869
|
break;
|
|
700
870
|
}
|
|
701
871
|
}
|
|
702
|
-
if (
|
|
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
|
|
710
|
-
for (const v of
|
|
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 (
|
|
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 ${
|
|
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)
|