@workerstack/repo 0.0.0-stage → 0.1.0
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/CHANGELOG.md +45 -0
- package/LICENSE +21 -0
- package/README.md +0 -3
- package/agents/global-settings/antigravity.json +3 -0
- package/agents/global-settings/claude.json +6 -0
- package/agents/global-settings/codex.toml +2 -0
- package/agents/global-settings/cursor.json +3 -0
- package/agents/global-settings/grok.toml +36 -0
- package/agents/global-settings/manifest.json +36 -0
- package/agents/grok/hooks/orchestrate-parent.json +60 -0
- package/agents/grok/hooks/rtk.json +16 -0
- package/agents/grok/hooks/rulesync.json +3 -0
- package/agents/grok/roles/scout.toml +5 -0
- package/agents/hooks.jsonc +67 -0
- package/agents/mcp-policy.jsonc +16 -0
- package/agents/rules/overview.md +21 -0
- package/agents/settings/claude.json +5 -0
- package/agents/skills/ask/SKILL.md +13 -0
- package/agents/skills/auto/SKILL.md +13 -0
- package/agents/skills/away/SKILL.md +19 -0
- package/agents/skills/blast-radius/SKILL.md +54 -0
- package/agents/skills/bro/SKILL.md +9 -0
- package/agents/skills/browser/SKILL.md +16 -0
- package/agents/skills/clasp/SKILL.md +11 -0
- package/agents/skills/commit/SKILL.md +35 -0
- package/agents/skills/commit-and-push/SKILL.md +9 -0
- package/agents/skills/commit-push/SKILL.md +59 -0
- package/agents/skills/commit-push-deploy/SKILL.md +74 -0
- package/agents/skills/commit-push-release/SKILL.md +75 -0
- package/agents/skills/correct/SKILL.md +39 -0
- package/agents/skills/do/SKILL.md +21 -0
- package/agents/skills/explain/SKILL.md +13 -0
- package/agents/skills/google-workspace/SKILL.md +12 -0
- package/agents/skills/handoff/SKILL.md +81 -0
- package/agents/skills/implement-plan/SKILL.md +29 -0
- package/agents/skills/implement-roadmap/SKILL.md +27 -0
- package/agents/skills/jev/SKILL.md +17 -0
- package/agents/skills/listen/SKILL.md +15 -0
- package/agents/skills/pickup/SKILL.md +58 -0
- package/agents/skills/principle-attack-the-premise/SKILL.md +24 -0
- package/agents/skills/principle-boundary-discipline/SKILL.md +38 -0
- package/agents/skills/principle-build-the-lever/SKILL.md +28 -0
- package/agents/skills/principle-encode-lessons-in-structure/SKILL.md +15 -0
- package/agents/skills/principle-exhaust-the-design-space/SKILL.md +12 -0
- package/agents/skills/principle-experience-first/SKILL.md +22 -0
- package/agents/skills/principle-explain-the-number/SKILL.md +21 -0
- package/agents/skills/principle-fix-root-causes/SKILL.md +24 -0
- package/agents/skills/principle-foundational-thinking/SKILL.md +25 -0
- package/agents/skills/principle-guard-the-context-window/SKILL.md +16 -0
- package/agents/skills/principle-laziness-protocol/SKILL.md +13 -0
- package/agents/skills/principle-make-operations-idempotent/SKILL.md +25 -0
- package/agents/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +18 -0
- package/agents/skills/principle-minimize-reader-load/SKILL.md +22 -0
- package/agents/skills/principle-model-the-domain/SKILL.md +26 -0
- package/agents/skills/principle-never-block-on-the-human/SKILL.md +17 -0
- package/agents/skills/principle-outcome-oriented-execution/SKILL.md +25 -0
- package/agents/skills/principle-prove-it-works/SKILL.md +25 -0
- package/agents/skills/principle-redesign-from-first-principles/SKILL.md +18 -0
- package/agents/skills/principle-separate-before-serializing-shared-state/SKILL.md +20 -0
- package/agents/skills/principle-sequence-verifiable-units/SKILL.md +22 -0
- package/agents/skills/principle-subtract-before-you-add/SKILL.md +21 -0
- package/agents/skills/principle-test-behavior-not-implementation/SKILL.md +24 -0
- package/agents/skills/principle-type-system-discipline/SKILL.md +17 -0
- package/agents/skills/review-plan/SKILL.md +28 -0
- package/agents/skills/review-roadmap/SKILL.md +28 -0
- package/agents/skills/show-me/SKILL.md +21 -0
- package/agents/skills/speak/SKILL.md +15 -0
- package/agents/skills/su/SKILL.md +11 -0
- package/agents/skills/teach/SKILL.md +25 -0
- package/agents/skills/tmp/SKILL.md +54 -0
- package/agents/skills/typescript-best-practices/SKILL.md +32 -0
- package/agents/skills/typescript-best-practices/references/patterns.md +328 -0
- package/agents/skills/unslop/SKILL.md +62 -0
- package/agents/skills/write-plan/SKILL.md +33 -0
- package/agents/skills/write-roadmap/SKILL.md +66 -0
- package/agents/subagents/implementer-deep.md +57 -0
- package/agents/subagents/implementer-fast.md +57 -0
- package/agents/subagents/orchestrator.md +43 -0
- package/agents/subagents/researcher.md +47 -0
- package/agents/subagents/scout.md +44 -0
- package/bin/repo.ts +4 -0
- package/cli/actionlint-default.test.ts +38 -0
- package/cli/actionlint-download.test.ts +162 -0
- package/cli/actionlint-real.test.ts +33 -0
- package/cli/actionlint.constants.ts +43 -0
- package/cli/actionlint.test.ts +108 -0
- package/cli/actionlint.ts +178 -0
- package/cli/bin.test.ts +54 -0
- package/cli/bin.ts +40 -0
- package/cli/check-changelog.test.ts +40 -0
- package/cli/check-changelog.ts +12 -0
- package/cli/check-comments.test.ts +157 -0
- package/cli/check-comments.ts +123 -0
- package/cli/check-env-modules.test.ts +85 -0
- package/cli/check-env-modules.ts +56 -0
- package/cli/check-magic.test.ts +139 -0
- package/cli/check-magic.ts +120 -0
- package/cli/check-manifests.constants.ts +4 -0
- package/cli/check-manifests.test.ts +210 -0
- package/cli/check-manifests.ts +71 -0
- package/cli/check-nightly.test.ts +103 -0
- package/cli/check-nightly.ts +37 -0
- package/cli/check-ratchets-missing.test.ts +46 -0
- package/cli/check-ratchets-mutation.test.ts +49 -0
- package/cli/check-ratchets.constants.ts +20 -0
- package/cli/check-ratchets.test.ts +190 -0
- package/cli/check-ratchets.ts +193 -0
- package/cli/check-rejections.test.ts +220 -0
- package/cli/check-rejections.ts +153 -0
- package/cli/check-reports.ts +37 -0
- package/cli/check-tsconfig.test.ts +507 -0
- package/cli/check-tsconfig.ts +205 -0
- package/cli/commands.test.ts +114 -0
- package/cli/commands.ts +218 -0
- package/cli/commit-changelog.ts +27 -0
- package/cli/commit-msg-changelog.test.ts +93 -0
- package/cli/commit-msg-range.test.ts +126 -0
- package/cli/commit-msg-red-proof.test.ts +147 -0
- package/cli/commit-msg-repo.test.ts +153 -0
- package/cli/commit-msg.test.ts +101 -0
- package/cli/commit-msg.ts +194 -0
- package/cli/commit-range.constants.ts +17 -0
- package/cli/commit-range.ts +57 -0
- package/cli/config-knip.test.ts +70 -0
- package/cli/config-nightly.test.ts +84 -0
- package/cli/config-tdd.test.ts +46 -0
- package/cli/config.test.ts +576 -0
- package/cli/config.ts +261 -0
- package/cli/conventional.ts +9 -0
- package/cli/coverage-config.ts +62 -0
- package/cli/coverage.constants.ts +6 -0
- package/cli/coverage.test.ts +199 -0
- package/cli/coverage.ts +127 -0
- package/cli/env.constants.ts +10 -0
- package/cli/env.test.ts +56 -0
- package/cli/env.ts +72 -0
- package/cli/fixtures/base-config.ts +13 -0
- package/cli/fixtures/gate-world.ts +48 -0
- package/cli/fixtures/knip-run.ts +38 -0
- package/cli/fixtures/mutate-fakes.ts +137 -0
- package/cli/fixtures/planted-repo.ts +50 -0
- package/cli/fixtures/publish-world.ts +71 -0
- package/cli/fixtures/red-proof-repo.ts +72 -0
- package/cli/fixtures/release-context.ts +15 -0
- package/cli/fixtures/release-world.ts +111 -0
- package/cli/fixtures/stub-bin.ts +40 -0
- package/cli/guard-diagnostics.test.ts +157 -0
- package/cli/guard-diagnostics.ts +195 -0
- package/cli/init.test.ts +487 -0
- package/cli/init.ts +275 -0
- package/cli/knip-check-ignores.test.ts +106 -0
- package/cli/knip-check-real.test.ts +65 -0
- package/cli/knip-check.constants.ts +46 -0
- package/cli/knip-check.test.ts +114 -0
- package/cli/knip-check.ts +169 -0
- package/cli/knip-schema.ts +67 -0
- package/cli/known-failures.constants.ts +22 -0
- package/cli/known-failures.test.ts +138 -0
- package/cli/known-failures.ts +131 -0
- package/cli/lint/as-needs-reason.ts +41 -0
- package/cli/lint/boundaries.ts +73 -0
- package/cli/lint/class-file.ts +39 -0
- package/cli/lint/config-file.ts +32 -0
- package/cli/lint/entry-file.ts +54 -0
- package/cli/lint/env-in-config.ts +66 -0
- package/cli/lint/filename-matches-export.ts +63 -0
- package/cli/lint/index-file.ts +22 -0
- package/cli/lint/index.ts +47 -0
- package/cli/lint/is-reason.ts +6 -0
- package/cli/lint/manifest.ts +46 -0
- package/cli/lint/names.ts +59 -0
- package/cli/lint/no-commented-out-code.ts +105 -0
- package/cli/lint/no-mock-of-subject.ts +64 -0
- package/cli/lint/no-module-side-effects.ts +94 -0
- package/cli/lint/no-module-state.ts +58 -0
- package/cli/lint/no-side-effect-import.ts +17 -0
- package/cli/lint/no-v8-ignore.ts +18 -0
- package/cli/lint/script-rules.ts +9 -0
- package/cli/lint/skip-on-platform.constants.ts +8 -0
- package/cli/lint/skip-on-platform.ts +118 -0
- package/cli/lint/todo-needs-id.ts +63 -0
- package/cli/lint/types-file.ts +28 -0
- package/cli/lint-schema.ts +32 -0
- package/cli/magic/fixtures/magic-repo.ts +64 -0
- package/cli/magic/magic-hit.ts +2 -0
- package/cli/magic/magic-nodes.ts +44 -0
- package/cli/magic/magic-numbers.test.ts +99 -0
- package/cli/magic/magic-numbers.ts +117 -0
- package/cli/magic/magic-strings.test.ts +38 -0
- package/cli/magic/magic-strings.ts +81 -0
- package/cli/main.test.ts +853 -0
- package/cli/main.ts +292 -0
- package/cli/mutate-args.ts +79 -0
- package/cli/mutate-check.test.ts +147 -0
- package/cli/mutate-deps.ts +42 -0
- package/cli/mutate-edges.test.ts +150 -0
- package/cli/mutate-files.test.ts +100 -0
- package/cli/mutate-files.ts +31 -0
- package/cli/mutate-floors.test.ts +91 -0
- package/cli/mutate-floors.ts +104 -0
- package/cli/mutate-real-deps.ts +57 -0
- package/cli/mutate-real.test.ts +96 -0
- package/cli/mutate-report.ts +96 -0
- package/cli/mutate-run.ts +128 -0
- package/cli/mutate-shards.ts +64 -0
- package/cli/mutate.constants.ts +67 -0
- package/cli/mutate.test.ts +182 -0
- package/cli/mutate.ts +64 -0
- package/cli/nested-vitest-flags.ts +8 -0
- package/cli/nightly-config.constants.ts +14 -0
- package/cli/nightly-config.ts +62 -0
- package/cli/prompt.test.ts +202 -0
- package/cli/prompt.ts +1 -0
- package/cli/red-proof-edges.test.ts +78 -0
- package/cli/red-proof-failures.test.ts +82 -0
- package/cli/red-proof-tree.test.ts +157 -0
- package/cli/red-proof-verdict.test.ts +44 -0
- package/cli/red-proof.constants.ts +14 -0
- package/cli/red-proof.ts +202 -0
- package/cli/release/changelog-parse.ts +83 -0
- package/cli/release/changelog.test.ts +141 -0
- package/cli/release/changelog.ts +106 -0
- package/cli/release/gate-refusals.test.ts +85 -0
- package/cli/release/gate.test.ts +63 -0
- package/cli/release/gate.ts +70 -0
- package/cli/release/git.ts +66 -0
- package/cli/release/github.test.ts +128 -0
- package/cli/release/github.ts +46 -0
- package/cli/release/list-dir.test.ts +11 -0
- package/cli/release/list-dir.ts +6 -0
- package/cli/release/main.test.ts +48 -0
- package/cli/release/main.ts +51 -0
- package/cli/release/manifest.test.ts +24 -0
- package/cli/release/manifest.ts +34 -0
- package/cli/release/pack.test.ts +107 -0
- package/cli/release/pack.ts +76 -0
- package/cli/release/plan.ts +127 -0
- package/cli/release/publish-run.test.ts +98 -0
- package/cli/release/publish.test.ts +127 -0
- package/cli/release/publish.ts +125 -0
- package/cli/release/refuse.ts +5 -0
- package/cli/release/release-messages.constants.ts +96 -0
- package/cli/release/release-refusals.test.ts +149 -0
- package/cli/release/release.constants.ts +128 -0
- package/cli/release/release.test.ts +199 -0
- package/cli/release/release.ts +145 -0
- package/cli/release/version.test.ts +118 -0
- package/cli/release/version.ts +69 -0
- package/cli/release/wait.test.ts +101 -0
- package/cli/release/wait.ts +95 -0
- package/cli/script-diagnostics.test.ts +76 -0
- package/cli/script-diagnostics.ts +62 -0
- package/cli/scripts/board.ts +519 -0
- package/cli/scripts/browser.ts +994 -0
- package/cli/scripts/change-set.ts +105 -0
- package/cli/scripts/check-globals.ts +199 -0
- package/cli/scripts/ci-skips.constants.ts +17 -0
- package/cli/scripts/ci-skips.ts +118 -0
- package/cli/scripts/ci-wait-run.ts +28 -0
- package/cli/scripts/ci-wait.constants.ts +44 -0
- package/cli/scripts/ci-wait.ts +175 -0
- package/cli/scripts/ci.constants.ts +15 -0
- package/cli/scripts/ci.ts +208 -0
- package/cli/scripts/clean.ts +363 -0
- package/cli/scripts/comments-step.ts +15 -0
- package/cli/scripts/doctor.ts +828 -0
- package/cli/scripts/emit-agent-shell-tools.ts +91 -0
- package/cli/scripts/emit-mcp-policy.ts +402 -0
- package/cli/scripts/folder-map.ts +353 -0
- package/cli/scripts/gate-fat-tools.ts +1095 -0
- package/cli/scripts/gitignore-guard.ts +338 -0
- package/cli/scripts/google-workspace.ts +779 -0
- package/cli/scripts/hook-command.ts +72 -0
- package/cli/scripts/hook-input.ts +178 -0
- package/cli/scripts/hook-io.constants.ts +5 -0
- package/cli/scripts/hook-io.ts +46 -0
- package/cli/scripts/hooks-union.ts +469 -0
- package/cli/scripts/interactive.ts +30 -0
- package/cli/scripts/jev.ts +420 -0
- package/cli/scripts/job.ts +934 -0
- package/cli/scripts/jsonc.ts +9 -0
- package/cli/scripts/lessons.ts +564 -0
- package/cli/scripts/lint-baseline.ts +233 -0
- package/cli/scripts/lint-main.ts +24 -0
- package/cli/scripts/lint-skips.ts +139 -0
- package/cli/scripts/lint.constants.ts +6 -0
- package/cli/scripts/lint.ts +142 -0
- package/cli/scripts/listen.ts +962 -0
- package/cli/scripts/managed-text.ts +47 -0
- package/cli/scripts/orchestrate-parent.ts +1155 -0
- package/cli/scripts/parse-need.ts +81 -0
- package/cli/scripts/persist-handoff.ts +228 -0
- package/cli/scripts/persist-implementation.ts +276 -0
- package/cli/scripts/persist-plan.ts +888 -0
- package/cli/scripts/persist-roadmap.ts +478 -0
- package/cli/scripts/persist-tmp.ts +177 -0
- package/cli/scripts/persist.ts +54 -0
- package/cli/scripts/phase-files.ts +121 -0
- package/cli/scripts/prompt.ts +38 -0
- package/cli/scripts/purge-global-mcps.ts +313 -0
- package/cli/scripts/rejections-step.ts +37 -0
- package/cli/scripts/rtk-grok.ts +102 -0
- package/cli/scripts/scrub-spawn-prompt.ts +103 -0
- package/cli/scripts/scrub-text-lib.ts +58 -0
- package/cli/scripts/session-map.ts +69 -0
- package/cli/scripts/setup.ts +862 -0
- package/cli/scripts/skill-conflicts.ts +82 -0
- package/cli/scripts/speak.ts +370 -0
- package/cli/scripts/strategy.ts +168 -0
- package/cli/scripts/sync-global-settings.ts +316 -0
- package/cli/scripts/turn-steps.ts +249 -0
- package/cli/scripts/turn.ts +157 -0
- package/cli/scripts/typecheck-step.ts +43 -0
- package/cli/scripts/vscode-tasks.ts +175 -0
- package/cli/scripts/workspace-root.ts +160 -0
- package/cli/scripts/worktree.ts +806 -0
- package/cli/spawn.test.ts +34 -0
- package/cli/spawn.ts +27 -0
- package/cli/staged-change.constants.ts +11 -0
- package/cli/staged-change.ts +79 -0
- package/cli/stryker-preload.test.ts +50 -0
- package/cli/stryker-preload.ts +9 -0
- package/cli/sync/agents/frontmatter.test.ts +197 -0
- package/cli/sync/agents/frontmatter.ts +140 -0
- package/cli/sync/agents/hooks.test.ts +517 -0
- package/cli/sync/agents/hooks.ts +401 -0
- package/cli/sync/agents/rules.test.ts +229 -0
- package/cli/sync/agents/rules.ts +84 -0
- package/cli/sync/agents/sources.test.ts +375 -0
- package/cli/sync/agents/sources.ts +244 -0
- package/cli/sync/agents/subagents.test.ts +149 -0
- package/cli/sync/agents/subagents.ts +158 -0
- package/cli/sync/agents-text.test.ts +31 -0
- package/cli/sync/agents-text.ts +36 -0
- package/cli/sync/fixtures/copied.README.md +35 -0
- package/cli/sync/fixtures/copied.tgz +0 -0
- package/cli/sync/fixtures/maps/golden/INDEX.md +22 -0
- package/cli/sync/fixtures/maps-ws.tgz +0 -0
- package/cli/sync/git-files.test.ts +340 -0
- package/cli/sync/git-files.ts +197 -0
- package/cli/sync/git-hooks.test.ts +94 -0
- package/cli/sync/golden.test.ts +15 -0
- package/cli/sync/hooks-mode.test.ts +99 -0
- package/cli/sync/hosts.ts +28 -0
- package/cli/sync/knip-config.constants.ts +4 -0
- package/cli/sync/knip-config.test.ts +147 -0
- package/cli/sync/knip-config.ts +108 -0
- package/cli/sync/manifest.test.ts +78 -0
- package/cli/sync/manifest.ts +60 -0
- package/cli/sync/maps.test.ts +304 -0
- package/cli/sync/maps.ts +297 -0
- package/cli/sync/migrate.test.ts +2000 -0
- package/cli/sync/migrate.ts +1104 -0
- package/cli/sync/nightly-jobs.test.ts +63 -0
- package/cli/sync/nightly.constants.ts +48 -0
- package/cli/sync/nightly.test.ts +100 -0
- package/cli/sync/nightly.ts +75 -0
- package/cli/sync/outputs.test.ts +351 -0
- package/cli/sync/outputs.ts +168 -0
- package/cli/sync/oxlint-config.test.ts +178 -0
- package/cli/sync/oxlint-config.ts +168 -0
- package/cli/sync/release-jobs.test.ts +29 -0
- package/cli/sync/release-reports.test.ts +31 -0
- package/cli/sync/release-workflow.constants.ts +61 -0
- package/cli/sync/release-workflow.test.ts +192 -0
- package/cli/sync/release-workflow.ts +86 -0
- package/cli/sync/release.constants.ts +93 -0
- package/cli/sync/release.test.ts +187 -0
- package/cli/sync/release.ts +112 -0
- package/cli/sync/repo-dir.test.ts +371 -0
- package/cli/sync/repo-dir.ts +205 -0
- package/cli/sync/standards.constants.ts +8 -0
- package/cli/sync/standards.test.ts +206 -0
- package/cli/sync/standards.ts +179 -0
- package/cli/sync/stock-history.json +2796 -0
- package/cli/sync/stock-history.test.ts +66 -0
- package/cli/sync/stock-history.ts +84 -0
- package/cli/sync/stock.test.ts +47 -0
- package/cli/sync/stock.ts +24 -0
- package/cli/sync/trees.test.ts +621 -0
- package/cli/sync/trees.ts +280 -0
- package/cli/sync/validate.test.ts +299 -0
- package/cli/sync/validate.ts +402 -0
- package/cli/sync/workspace.test.ts +180 -0
- package/cli/sync/workspace.ts +138 -0
- package/cli/sync/write.test.ts +341 -0
- package/cli/sync/write.ts +231 -0
- package/cli/sync.test.ts +1495 -0
- package/cli/sync.ts +360 -0
- package/cli/tdd-scope.test.ts +36 -0
- package/cli/tdd-scope.ts +41 -0
- package/cli/test-files.ts +16 -0
- package/cli/ts-program.ts +69 -0
- package/cli/tsconfig.constants.ts +8 -0
- package/cli/workerstack-config.types.ts +21 -0
- package/dist/cli/env.d.ts +10 -0
- package/dist/cli/env.js +64 -0
- package/dist/presets/temp-root-env.d.ts +10 -0
- package/dist/presets/temp-root-env.js +20 -0
- package/dist/presets/temp-root.constants.d.ts +6 -0
- package/dist/presets/temp-root.constants.js +6 -0
- package/dist/presets/temp-root.d.ts +2 -0
- package/dist/presets/temp-root.js +60 -0
- package/dist/presets/vitest.constants.d.ts +16 -0
- package/dist/presets/vitest.constants.js +16 -0
- package/dist/presets/vitest.d.ts +11 -0
- package/dist/presets/vitest.js +67 -0
- package/dist/presets/workers-plugin.d.ts +10 -0
- package/dist/presets/workers-plugin.js +54 -0
- package/package.json +141 -4
- package/references/INDEX.md +100 -0
- package/references/browser.md +66 -0
- package/references/cascade.md +15 -0
- package/references/clasp.md +21 -0
- package/references/cloudflare.md +36 -0
- package/references/google-workspace.md +79 -0
- package/references/hooks.md +103 -0
- package/references/implementation-format.md +119 -0
- package/references/jev.md +56 -0
- package/references/lessons.md +73 -0
- package/references/listen.md +80 -0
- package/references/map.md +107 -0
- package/references/output.md +47 -0
- package/references/plan-format.md +264 -0
- package/references/review-loop.md +48 -0
- package/references/roadmap-format.md +104 -0
- package/references/rtk.md +27 -0
- package/references/scope.md +76 -0
- package/references/setup.md +42 -0
- package/references/speak.md +59 -0
- package/tsconfig/base.json +28 -0
- package/tsconfig/bun.d.ts +1 -0
- package/tsconfig/bun.json +8 -0
- package/tsconfig/reset.d.ts +1 -0
- package/tsconfig/strict.json +12 -0
- package/tsconfig/web.json +7 -0
- package/tsconfig/worker.d.ts +1 -0
- package/tsconfig/worker.json +8 -0
package/cli/main.ts
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { init, parsePacksFlag, type InitOptions } from "./init.ts";
|
|
5
|
+
import { confirm, isInteractive, select, spinner } from "./prompt.ts";
|
|
6
|
+
import { runSync } from "./sync.ts";
|
|
7
|
+
import { HOOK_NAMES } from "./scripts/hook-command.ts";
|
|
8
|
+
import { COMMANDS, isCommandName, runCommand } from "./commands.ts";
|
|
9
|
+
import { mapCommand } from "./scripts/folder-map.ts";
|
|
10
|
+
|
|
11
|
+
const PKG_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
|
|
13
|
+
const pad = (name: string) => ` ${name.padEnd(18)}`;
|
|
14
|
+
|
|
15
|
+
const USAGE = `Usage: repo [command]
|
|
16
|
+
|
|
17
|
+
Default (no command): TTY menu; else init — full setup
|
|
18
|
+
|
|
19
|
+
Commands:
|
|
20
|
+
${pad("init")}Full setup: write workerstack.config.ts and package.json, bun install (whose postinstall runs repo sync), repo yolo
|
|
21
|
+
${pad("check")}Tsconfig bases, manifests, disable comments, lint.skip entries, generated files current, lint within .repo/lint-baseline.json, knip, actionlint on workflows, and floors, known failures and the baseline against main
|
|
22
|
+
${pad("sync")}Write the committed generated files from workerstack.config.ts (--check: report drift; --postinstall: never fail)
|
|
23
|
+
${pad("map [dir]")}Code-only folder map (git-tracked, no files); dir for depth
|
|
24
|
+
${Object.entries(COMMANDS)
|
|
25
|
+
.map(([name, entry]) => `${pad(name)}${entry.summary}\n`)
|
|
26
|
+
.join("")}${pad("hook <name>")}Run a hook: ${HOOK_NAMES.join(", ")}
|
|
27
|
+
${pad("help")}Show this help
|
|
28
|
+
${pad("version")}Print package version
|
|
29
|
+
|
|
30
|
+
Flags:
|
|
31
|
+
--skip-globals Skip repo yolo (otherwise init always runs it)
|
|
32
|
+
--yes, -y Non-interactive
|
|
33
|
+
--non-interactive Same as --yes
|
|
34
|
+
--packs <list> init: skill packs written as agents.skills into a new config: google-workspace,listen,speak,clasp or none
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const MENU_OPTIONS = [
|
|
38
|
+
{ value: "init", label: "init" },
|
|
39
|
+
{ value: "check", label: "check" },
|
|
40
|
+
{ value: "yolo", label: "yolo" },
|
|
41
|
+
{ value: "purge-mcps", label: "purge-mcps" },
|
|
42
|
+
{ value: "doctor", label: "doctor" },
|
|
43
|
+
{ value: "clean", label: "clean" },
|
|
44
|
+
{ value: "setup", label: "setup" },
|
|
45
|
+
{ value: "tasks", label: "tasks" },
|
|
46
|
+
{ value: "help", label: "help" },
|
|
47
|
+
{ value: "quit", label: "quit" },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const YES_FLAGS = new Set(["--yes", "-y", "--non-interactive"]);
|
|
51
|
+
|
|
52
|
+
function pkgVersion(): string {
|
|
53
|
+
const pkg: unknown = JSON.parse(readFileSync(path.join(PKG_ROOT, "package.json"), "utf8"));
|
|
54
|
+
if (typeof pkg !== "object" || pkg === null || !("version" in pkg) || typeof pkg.version !== "string") {
|
|
55
|
+
throw new Error("package.json has no version");
|
|
56
|
+
}
|
|
57
|
+
return pkg.version;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The command line, split into the command, its arguments and the flags this CLI knows. */
|
|
61
|
+
export interface Flags {
|
|
62
|
+
cmd: string | undefined;
|
|
63
|
+
args: string[];
|
|
64
|
+
skipGlobals: boolean;
|
|
65
|
+
packs: string[] | undefined;
|
|
66
|
+
packsError: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function parseArgv(argv: string[]): Flags {
|
|
70
|
+
let skipGlobals = false;
|
|
71
|
+
let packs: string[] | undefined;
|
|
72
|
+
let packsError = false;
|
|
73
|
+
const positionals: string[] = [];
|
|
74
|
+
for (let i = 0; i < argv.length; i++) {
|
|
75
|
+
const a = argv[i]!; // i < argv.length
|
|
76
|
+
if (a === "--skip-globals") {
|
|
77
|
+
skipGlobals = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (YES_FLAGS.has(a)) continue;
|
|
81
|
+
if (a === "--packs") {
|
|
82
|
+
const v = argv[i + 1];
|
|
83
|
+
if (v == null || String(v).startsWith("--")) {
|
|
84
|
+
packsError = true;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
packs = parsePacksFlag(v);
|
|
89
|
+
} catch {
|
|
90
|
+
packsError = true;
|
|
91
|
+
}
|
|
92
|
+
i += 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (typeof a === "string" && a.startsWith("--packs=")) {
|
|
96
|
+
try {
|
|
97
|
+
packs = parsePacksFlag(a.slice("--packs=".length));
|
|
98
|
+
} catch {
|
|
99
|
+
packsError = true;
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
positionals.push(a);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
cmd: positionals[0],
|
|
107
|
+
args: positionals.slice(1),
|
|
108
|
+
skipGlobals,
|
|
109
|
+
packs,
|
|
110
|
+
packsError,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function bindSpinner() {
|
|
115
|
+
const spin = spinner();
|
|
116
|
+
return (name: string, action: "start" | "stop") => {
|
|
117
|
+
if (action === "start") spin.start(name);
|
|
118
|
+
else spin.stop(name);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const APPLY_GLOBALS_CONFIRM = "Apply global prompt optimizations? (recommended)";
|
|
123
|
+
|
|
124
|
+
function initOpts(base: InitOptions, packs: string[] | undefined): InitOptions {
|
|
125
|
+
if (packs === undefined) return base;
|
|
126
|
+
return { ...base, packs };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface RunInitOptions {
|
|
130
|
+
skipGlobals: boolean;
|
|
131
|
+
interactive: boolean;
|
|
132
|
+
packs: string[] | undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function runInit(cwd: string, { skipGlobals, interactive, packs }: RunInitOptions) {
|
|
136
|
+
if (!interactive) {
|
|
137
|
+
init(cwd, initOpts({ skipGlobals }, packs));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const applyGlobals = await confirm({
|
|
141
|
+
message: APPLY_GLOBALS_CONFIRM,
|
|
142
|
+
initialValue: !skipGlobals,
|
|
143
|
+
});
|
|
144
|
+
init(
|
|
145
|
+
cwd,
|
|
146
|
+
initOpts(
|
|
147
|
+
{
|
|
148
|
+
skipGlobals: !applyGlobals,
|
|
149
|
+
onPhase: bindSpinner(),
|
|
150
|
+
},
|
|
151
|
+
packs,
|
|
152
|
+
),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const syncIo = () => ({
|
|
157
|
+
cwd: process.cwd(),
|
|
158
|
+
stdout: (s: string) => process.stdout.write(s),
|
|
159
|
+
stderr: (s: string) => process.stderr.write(s),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
/** Floors, known failures and the lint baseline held against main, per the config's `coverage` paths. */
|
|
163
|
+
async function ratchetLines(root: string): Promise<string[]> {
|
|
164
|
+
const { loadConfig } = await import("./config.ts");
|
|
165
|
+
const { DEFAULT_COVERAGE } = await import("./coverage-config.ts");
|
|
166
|
+
const { ratchetProblems } = await import("./check-ratchets.ts");
|
|
167
|
+
const { knownFailures } = await import("./known-failures.ts");
|
|
168
|
+
const { sourceFiles } = await import("./check-comments.ts");
|
|
169
|
+
const loaded = await loadConfig(root);
|
|
170
|
+
const coverage = loaded.ok ? loaded.config.coverage : DEFAULT_COVERAGE;
|
|
171
|
+
const listed = sourceFiles(root);
|
|
172
|
+
// Without a git listing there are no test files to read.
|
|
173
|
+
const files = listed.ok ? listed.files : [];
|
|
174
|
+
return [...ratchetProblems(root, coverage), ...knownFailures(root, files, coverage.knownFailures)];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function dispatch(cmd: string | undefined, flags: Flags, interactive: boolean) {
|
|
178
|
+
if (cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
179
|
+
process.stdout.write(USAGE);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (cmd === "version" || cmd === "--version" || cmd === "-v") {
|
|
183
|
+
process.stdout.write(`${pkgVersion()}\n`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (!cmd || cmd === "init") {
|
|
187
|
+
await runInit(process.cwd(), {
|
|
188
|
+
skipGlobals: flags.skipGlobals,
|
|
189
|
+
interactive: interactive && (!cmd || cmd === "init"),
|
|
190
|
+
packs: flags.packs,
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (cmd === "check") {
|
|
195
|
+
// Loaded here so other commands never load typescript.
|
|
196
|
+
const { checkTsconfigs } = await import("./check-tsconfig.ts");
|
|
197
|
+
const { checkEnvModules } = await import("./check-env-modules.ts");
|
|
198
|
+
const problems = [...checkTsconfigs(process.cwd(), PKG_ROOT), ...checkEnvModules(process.cwd())];
|
|
199
|
+
for (const line of problems) process.stderr.write(`repo check: ${line}\n`);
|
|
200
|
+
const synced = await runSync(["--check"], syncIo());
|
|
201
|
+
const { loadConfig } = await import("./config.ts");
|
|
202
|
+
const { readWorkspace } = await import("./sync/workspace.ts");
|
|
203
|
+
const { checkManifests } = await import("./check-manifests.ts");
|
|
204
|
+
const { checkComments } = await import("./check-comments.ts");
|
|
205
|
+
const loaded = await loadConfig(process.cwd());
|
|
206
|
+
const on = loaded.ok && loaded.config.packs.includes("standards");
|
|
207
|
+
const read = on ? readWorkspace(process.cwd()) : undefined;
|
|
208
|
+
// An unreadable manifest is already reported by sync --check.
|
|
209
|
+
const standardsLines = [
|
|
210
|
+
...(read?.ok ? checkManifests(read.workspace) : []),
|
|
211
|
+
...(on ? checkComments(process.cwd()) : []),
|
|
212
|
+
];
|
|
213
|
+
for (const line of standardsLines) process.stderr.write(`repo check: ${line}\n`);
|
|
214
|
+
// Computed once: the skip report counts every hit, the baseline only those no skip turns off.
|
|
215
|
+
const { checkRejectionsAt, checkScriptsAt } = await import("./script-diagnostics.ts");
|
|
216
|
+
const rejectionLines = (await checkRejectionsAt(process.cwd())).map((line) => `rejections: ${line}`);
|
|
217
|
+
for (const line of rejectionLines) process.stderr.write(`repo check: ${line}\n`);
|
|
218
|
+
const scripts = await checkScriptsAt(process.cwd());
|
|
219
|
+
const { checkLintSkipsAt } = await import("./scripts/lint-skips.ts");
|
|
220
|
+
const skips = await checkLintSkipsAt(process.cwd(), scripts.all);
|
|
221
|
+
for (const line of skips.report) process.stdout.write(`repo check: ${line}\n`);
|
|
222
|
+
const { checkLintBaseline } = await import("./scripts/lint-baseline.ts");
|
|
223
|
+
const baseline = await checkLintBaseline(process.cwd(), scripts.kept);
|
|
224
|
+
const lintLines = [...scripts.problems, ...skips.problems, ...baseline];
|
|
225
|
+
for (const line of lintLines) process.stderr.write(`repo check: ${line}\n`);
|
|
226
|
+
const { checkNightlyAt } = await import("./check-nightly.ts");
|
|
227
|
+
const nightlyLines = await checkNightlyAt(process.cwd());
|
|
228
|
+
for (const line of nightlyLines) process.stderr.write(`repo check: ${line}\n`);
|
|
229
|
+
const { checkChangelog } = await import("./check-changelog.ts");
|
|
230
|
+
const changelogLines = checkChangelog(process.cwd());
|
|
231
|
+
for (const line of changelogLines) process.stderr.write(`repo check: ${line}\n`);
|
|
232
|
+
const ratchets = await ratchetLines(process.cwd());
|
|
233
|
+
for (const line of ratchets) process.stderr.write(`repo check: ${line}\n`);
|
|
234
|
+
const { checkKnipAt } = await import("./knip-check.ts");
|
|
235
|
+
const knip = await checkKnipAt(process.cwd());
|
|
236
|
+
for (const line of knip.report) process.stdout.write(`repo check: ${line}\n`);
|
|
237
|
+
for (const line of knip.problems) process.stderr.write(`repo check: ${line}\n`);
|
|
238
|
+
const { checkActionlintAt } = await import("./actionlint.ts");
|
|
239
|
+
const actionlint = await checkActionlintAt(process.cwd());
|
|
240
|
+
for (const line of actionlint.problems) process.stderr.write(`repo check: ${line}\n`);
|
|
241
|
+
const found = [
|
|
242
|
+
...problems,
|
|
243
|
+
...standardsLines,
|
|
244
|
+
...rejectionLines,
|
|
245
|
+
...lintLines,
|
|
246
|
+
...nightlyLines,
|
|
247
|
+
...changelogLines,
|
|
248
|
+
...ratchets,
|
|
249
|
+
];
|
|
250
|
+
const failing = [...found, ...knip.problems, ...actionlint.problems];
|
|
251
|
+
const code = failing.length > 0 ? 1 : synced;
|
|
252
|
+
if (code !== 0) process.exitCode = code;
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (cmd === "sync") {
|
|
256
|
+
process.exitCode = await runSync(flags.args, syncIo());
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (isCommandName(cmd)) {
|
|
260
|
+
process.exitCode = await runCommand(cmd, flags.args);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (cmd === "map") {
|
|
264
|
+
process.stdout.write(mapCommand(process.cwd(), flags.args[0] || ""));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
process.stderr.write(`repo: unknown command "${cmd}"; run \`repo --help\`\n`);
|
|
268
|
+
process.exitCode = 1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Every command but `hook <name>` and the registry commands, which the bin runs through `dispatch` first. */
|
|
272
|
+
export async function start(): Promise<void> {
|
|
273
|
+
const raw = process.argv.slice(2);
|
|
274
|
+
const flags = parseArgv(raw);
|
|
275
|
+
if (flags.packsError) {
|
|
276
|
+
process.stdout.write(USAGE);
|
|
277
|
+
process.exitCode = 1;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const interactive = isInteractive();
|
|
281
|
+
let cmd = flags.cmd;
|
|
282
|
+
if (!cmd && interactive) {
|
|
283
|
+
const choice = await select({
|
|
284
|
+
message: "Command",
|
|
285
|
+
options: MENU_OPTIONS,
|
|
286
|
+
initialValue: "init",
|
|
287
|
+
});
|
|
288
|
+
if (choice === "quit") return;
|
|
289
|
+
cmd = choice;
|
|
290
|
+
}
|
|
291
|
+
await dispatch(cmd, flags, interactive);
|
|
292
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** The command line of `repo mutate`, parsed at the edge into one of three shapes. */
|
|
2
|
+
import {
|
|
3
|
+
ASSIGN,
|
|
4
|
+
CHECK_COMMAND,
|
|
5
|
+
FIRST_SHARD,
|
|
6
|
+
FLAG_PREFIX,
|
|
7
|
+
FLAG_WORDS,
|
|
8
|
+
REPORT_FLAG,
|
|
9
|
+
SHARD_FLAG,
|
|
10
|
+
SHARD_PATTERN,
|
|
11
|
+
WRITE_FLAG,
|
|
12
|
+
} from "./mutate.constants.ts";
|
|
13
|
+
|
|
14
|
+
/** The `index`th of `total` shards, 1-based. */
|
|
15
|
+
export type Shard = { index: number; total: number };
|
|
16
|
+
|
|
17
|
+
export type Args =
|
|
18
|
+
| { kind: "run"; shard: Shard; report: string | undefined }
|
|
19
|
+
| { kind: "check"; dir: string; write: boolean }
|
|
20
|
+
| { kind: "error"; line: string };
|
|
21
|
+
|
|
22
|
+
const fail = (line: string): Args => ({ kind: "error", line });
|
|
23
|
+
const SHARD_USAGE = `${SHARD_FLAG} takes i/n with 1 <= i <= n, such as 2/8`;
|
|
24
|
+
const REPORT_USAGE = `${REPORT_FLAG} takes a file path`;
|
|
25
|
+
const UNKNOWN = `use ${SHARD_FLAG} i/n, ${REPORT_FLAG} <file> or ${CHECK_COMMAND} <dir>`;
|
|
26
|
+
|
|
27
|
+
/** `i/n` as a shard, or null when it is no shard. */
|
|
28
|
+
function shardOf(text: string): Shard | null {
|
|
29
|
+
const match = SHARD_PATTERN.exec(text);
|
|
30
|
+
const [, first, second] = match ?? [];
|
|
31
|
+
const index = Number(first);
|
|
32
|
+
const total = Number(second);
|
|
33
|
+
return match !== null && index >= FIRST_SHARD && index <= total ? { index, total } : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** `--flag=value` as the two words `--flag value`. */
|
|
37
|
+
function expand(argv: string[]): string[] {
|
|
38
|
+
return argv.flatMap((word) => {
|
|
39
|
+
const at = word.indexOf(ASSIGN);
|
|
40
|
+
return word.startsWith(FLAG_PREFIX) && at > 0 ? [word.slice(0, at), word.slice(at + 1)] : [word];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseCheck(rest: string[]): Args {
|
|
45
|
+
const dir = rest.find((word) => word !== WRITE_FLAG);
|
|
46
|
+
if (dir === undefined) return fail(`${CHECK_COMMAND} takes the dir of the shard reports`);
|
|
47
|
+
return { kind: "check", dir, write: rest.includes(WRITE_FLAG) };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type Run = Extract<Args, { kind: "run" }>;
|
|
51
|
+
|
|
52
|
+
/** Applies one flag and its value to `run`; the line that says what is wrong, or null. */
|
|
53
|
+
function apply(run: Run, flag: string | undefined, value: string | undefined): string | null {
|
|
54
|
+
if (flag === SHARD_FLAG) {
|
|
55
|
+
const shard = shardOf(value ?? "");
|
|
56
|
+
if (shard === null) return SHARD_USAGE;
|
|
57
|
+
run.shard = shard;
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (flag !== REPORT_FLAG) return `unknown argument "${String(flag)}"; ${UNKNOWN}`;
|
|
61
|
+
if (value === undefined || value === "" || value.startsWith(FLAG_PREFIX)) return REPORT_USAGE;
|
|
62
|
+
run.report = value;
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function parseRun(words: string[]): Args {
|
|
67
|
+
const run: Run = { kind: "run", shard: { index: FIRST_SHARD, total: FIRST_SHARD }, report: undefined };
|
|
68
|
+
for (let at = 0; at < words.length; at += FLAG_WORDS) {
|
|
69
|
+
const problem = apply(run, words[at], words[at + 1]);
|
|
70
|
+
if (problem !== null) return fail(problem);
|
|
71
|
+
}
|
|
72
|
+
return run;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The run `argv` asks for, or the line that says what is wrong with it. */
|
|
76
|
+
export function parseArgs(argv: string[]): Args {
|
|
77
|
+
const words = expand(argv);
|
|
78
|
+
return words[0] === CHECK_COMMAND ? parseCheck(words.slice(1)) : parseRun(words);
|
|
79
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { expect, it } from "vitest";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { at, fakes, sim, type Fakes } from "./fixtures/mutate-fakes.ts";
|
|
4
|
+
import { runMutate } from "./mutate.ts";
|
|
5
|
+
import type { ShardReport } from "./mutate-report.ts";
|
|
6
|
+
|
|
7
|
+
const HEAD = "09ed564aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
|
8
|
+
const OTHER = "1a2b3c4bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
|
9
|
+
const survivor = { line: 1, column: 1, mutator: "m", status: "Survived" } as const;
|
|
10
|
+
|
|
11
|
+
function shard(index: number, total: number, files: ShardReport["files"]): ShardReport {
|
|
12
|
+
return { head: HEAD, shard: { index, total }, files };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Reports in /repo/reports; shard 1 killed 9 and let 1 live, shard 2 killed 18 and let 2 live: 27 of 30, 90%. */
|
|
16
|
+
const pair = (): ShardReport[] => [
|
|
17
|
+
shard(1, 2, { "src/a.ts": { killed: 9, survivors: [survivor] } }),
|
|
18
|
+
shard(2, 2, { "packages/p/b.ts": { killed: 18, survivors: [survivor, survivor] } }),
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
function planted(reports: ShardReport[], floors?: Record<string, number>): Fakes {
|
|
22
|
+
const f = fakes(undefined, ["packages/p"]);
|
|
23
|
+
reports.forEach((report, i) => f.files.set(at("reports", `mutation-${i + 1}.json`), JSON.stringify(report)));
|
|
24
|
+
if (floors !== undefined) f.files.set(at("mutation-floors.json"), JSON.stringify(floors));
|
|
25
|
+
return f;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
it("merges the shards, scores each package against its floor and passes at or above them", async () => {
|
|
29
|
+
const f = planted(pair(), { ".": 89, "packages/p": 85 });
|
|
30
|
+
expect(await runMutate(["check", "reports"], f.deps)).toBe(0);
|
|
31
|
+
expect(f.out).toEqual(["mutate: 2 shards of 09ed564; . 90.00% (floor 89); packages/p 90.00% (floor 85)\n"]);
|
|
32
|
+
expect(f.err).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("fails a package below its floor and names the report dir", async () => {
|
|
36
|
+
const f = planted(pair(), { ".": 91 });
|
|
37
|
+
expect(await runMutate(["check", "reports"], f.deps)).toBe(1);
|
|
38
|
+
expect(f.err).toEqual([
|
|
39
|
+
"repo mutate: . scored 90.00%, below its floor of 91 in mutation-floors.json; see the survivors in reports\n",
|
|
40
|
+
]);
|
|
41
|
+
expect(f.files.get(at("mutation-floors.json"))).toBe(JSON.stringify({ ".": 91 }));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("scores a package with no floor against none", async () => {
|
|
45
|
+
const f = planted(pair());
|
|
46
|
+
expect(await runMutate(["check", "reports"], f.deps)).toBe(0);
|
|
47
|
+
expect(f.out[0]).toBe("mutate: 2 shards of 09ed564; . 90.00% (no floor); packages/p 90.00% (no floor)\n");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("refuses a missing shard, naming it", async () => {
|
|
51
|
+
const f = planted([shard(1, 4, {}), shard(4, 4, {})]);
|
|
52
|
+
expect(await runMutate(["check", "reports"], f.deps)).toBe(1);
|
|
53
|
+
expect(f.err.join("")).toBe(
|
|
54
|
+
"repo mutate: reports is missing shards 2, 3 of 4; download every shard's report first\n",
|
|
55
|
+
);
|
|
56
|
+
const one = planted([shard(2, 2, {})]);
|
|
57
|
+
await runMutate(["check", "reports"], one.deps);
|
|
58
|
+
expect(one.err.join("")).toBe("repo mutate: reports is missing shard 1 of 2; download every shard's report first\n");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("refuses reports of different heads, different shard counts and a shard twice", async () => {
|
|
62
|
+
const heads = planted([shard(1, 2, {}), { ...shard(2, 2, {}), head: OTHER }]);
|
|
63
|
+
await runMutate(["check", "reports"], heads.deps);
|
|
64
|
+
expect(heads.err.join("")).toBe("repo mutate: reports holds shards of 09ed564 and 1a2b3c4; use one run's reports\n");
|
|
65
|
+
const totals = planted([shard(1, 2, {}), shard(2, 8, {})]);
|
|
66
|
+
await runMutate(["check", "reports"], totals.deps);
|
|
67
|
+
expect(totals.err.join("")).toBe("repo mutate: reports mixes runs of 2 and 8 shards; use one run's reports\n");
|
|
68
|
+
const twice = planted([shard(1, 2, {}), shard(1, 2, {})]);
|
|
69
|
+
await runMutate(["check", "reports"], twice.deps);
|
|
70
|
+
expect(twice.err.join("")).toBe("repo mutate: reports holds shard 1 twice; use one run's reports\n");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("names a bad file and the reason, and an empty or missing dir", async () => {
|
|
74
|
+
const bad = planted(pair());
|
|
75
|
+
bad.files.set(at("reports", "mutation-2.json"), JSON.stringify({ head: 1 }));
|
|
76
|
+
expect(await runMutate(["check", "reports"], bad.deps)).toBe(1);
|
|
77
|
+
expect(bad.err.join("")).toMatch(
|
|
78
|
+
new RegExp(
|
|
79
|
+
`^repo mutate: ${path.join("reports", "mutation-2.json").replaceAll("\\", "\\\\")} is not a mutation report: head: .+\n$`,
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
const text = planted(pair());
|
|
83
|
+
text.files.set(at("reports", "mutation-2.json"), "{");
|
|
84
|
+
await runMutate(["check", "reports"], text.deps);
|
|
85
|
+
expect(text.err.join("")).toContain(`${path.join("reports", "mutation-2.json")} is not a mutation report: `);
|
|
86
|
+
const none = planted([]);
|
|
87
|
+
expect(await runMutate(["check", "reports"], none.deps)).toBe(1);
|
|
88
|
+
expect(none.err.join("")).toBe(
|
|
89
|
+
"repo mutate: reports holds no mutation report; run `repo mutate --shard i/n --report reports/mutation-i.json` first\n",
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("ignores files in the dir that are not JSON", async () => {
|
|
94
|
+
const f = planted(pair(), { ".": 89 });
|
|
95
|
+
f.files.set(at("reports", "notes.txt"), "hello");
|
|
96
|
+
expect(await runMutate(["check", "reports"], f.deps)).toBe(0);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("raises a floor to the whole score, adds a package, and never lowers one", async () => {
|
|
100
|
+
const f = planted(pair(), { ".": 85, other: 70 });
|
|
101
|
+
expect(await runMutate(["check", "reports", "--write"], f.deps)).toBe(0);
|
|
102
|
+
expect(f.out).toEqual([
|
|
103
|
+
"mutate: 2 shards of 09ed564; . 90.00% (floor 85 → 90); packages/p 90.00% (floor new → 90); wrote mutation-floors.json\n",
|
|
104
|
+
]);
|
|
105
|
+
expect(JSON.parse(f.files.get(at("mutation-floors.json")) ?? "")).toEqual({
|
|
106
|
+
".": 90,
|
|
107
|
+
other: 70,
|
|
108
|
+
"packages/p": 90,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("floors a fractional score, and writes nothing when no floor changes", async () => {
|
|
113
|
+
const f = planted([shard(1, 1, { "a.ts": { killed: 5, survivors: [survivor, survivor, survivor] } })], { ".": 62 });
|
|
114
|
+
expect(await runMutate(["check", "reports", "--write"], f.deps)).toBe(0);
|
|
115
|
+
expect(f.out[0]).toBe("mutate: 1 shard of 09ed564; . 62.50% (floor 62)\n");
|
|
116
|
+
expect(f.files.get(at("mutation-floors.json"))).toBe(JSON.stringify({ ".": 62 }));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("writes nothing and fails when a package is below its floor", async () => {
|
|
120
|
+
const f = planted(pair(), { ".": 95, "packages/p": 10 });
|
|
121
|
+
expect(await runMutate(["check", "reports", "--write"], f.deps)).toBe(1);
|
|
122
|
+
expect(f.files.get(at("mutation-floors.json"))).toBe(JSON.stringify({ ".": 95, "packages/p": 10 }));
|
|
123
|
+
expect(f.out.join("")).not.toContain("wrote");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("prints a config's errors and an unreadable floors file instead of scoring", async () => {
|
|
127
|
+
const config = planted(pair());
|
|
128
|
+
const state = sim({ config: ["workerstack.config.ts: no default export"] });
|
|
129
|
+
const broken = fakes(state);
|
|
130
|
+
expect(await runMutate(["check", "reports"], broken.deps)).toBe(1);
|
|
131
|
+
expect(broken.err.join("")).toBe("repo mutate: workerstack.config.ts: no default export\n");
|
|
132
|
+
config.files.set(at("mutation-floors.json"), "[1]");
|
|
133
|
+
expect(await runMutate(["check", "reports"], config.deps)).toBe(1);
|
|
134
|
+
expect(config.err.join("")).toMatch(/^repo mutate: mutation-floors.json is not a floors file: .+\n$/);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("names a report that is no object, and a listed report that cannot be read", async () => {
|
|
138
|
+
const flat = planted(pair());
|
|
139
|
+
flat.files.set(at("reports", "mutation-2.json"), "5");
|
|
140
|
+
await runMutate(["check", "reports"], flat.deps);
|
|
141
|
+
expect(flat.err.join("")).toMatch(/mutation-2.json is not a mutation report: report: .+\n$/);
|
|
142
|
+
const gone = planted(pair());
|
|
143
|
+
const read = gone.deps.fs.read;
|
|
144
|
+
gone.deps.fs.read = (file) => (file.endsWith("mutation-2.json") ? null : read(file));
|
|
145
|
+
await runMutate(["check", "reports"], gone.deps);
|
|
146
|
+
expect(gone.err.join("")).toContain(`${path.join("reports", "mutation-2.json")} is not a mutation report: `);
|
|
147
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Captured } from "./scripts/lint.ts";
|
|
2
|
+
import { FAILED, NEWLINE } from "./mutate.constants.ts";
|
|
3
|
+
|
|
4
|
+
/** What `repo mutate` reads from workerstack.config.ts: the repo root, the globs to mutate, the floors file. */
|
|
5
|
+
export type MutateConfig = { root: string; mutate: readonly string[]; floors: string };
|
|
6
|
+
|
|
7
|
+
/** The file system `repo mutate` touches; paths are absolute. */
|
|
8
|
+
export type MutateFs = {
|
|
9
|
+
/** A new empty dir named `<prefix><random>` under the OS temp dir. */
|
|
10
|
+
mkdtemp: (prefix: string) => string;
|
|
11
|
+
rm: (target: string) => void;
|
|
12
|
+
/** The text of a file, or null when it is missing. */
|
|
13
|
+
read: (file: string) => string | null;
|
|
14
|
+
/** Writes `text`, creating the parent dirs. */
|
|
15
|
+
write: (file: string, text: string) => void;
|
|
16
|
+
/** The names in a dir, or null when it is missing. */
|
|
17
|
+
list: (dir: string) => string[] | null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Everything `repo mutate` does outside this process, injected so a test runs none of it. */
|
|
21
|
+
export type MutateDeps = {
|
|
22
|
+
cwd: string;
|
|
23
|
+
/** The config of the repo holding `cwd`, or the lines that say why there is none. */
|
|
24
|
+
config: (cwd: string) => Promise<MutateConfig | string[]>;
|
|
25
|
+
/** The workspace package dirs of the root, root-relative. */
|
|
26
|
+
packages: (root: string) => string[];
|
|
27
|
+
/** `git <args>` in `cwd`, run with no `GIT_*` variable. */
|
|
28
|
+
git: (cwd: string, args: string[]) => Captured;
|
|
29
|
+
/** `bun <args>` in `cwd`, run with no `GIT_*` variable. */
|
|
30
|
+
run: (args: string[], cwd: string) => Captured;
|
|
31
|
+
fs: MutateFs;
|
|
32
|
+
/** Absolute path of `stryker-preload.ts`. */
|
|
33
|
+
preload: string;
|
|
34
|
+
out: (text: string) => void;
|
|
35
|
+
err: (text: string) => void;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Prints each line as a `repo mutate:` error and answers with the failing exit code. */
|
|
39
|
+
export function fail(deps: MutateDeps, ...lines: string[]): number {
|
|
40
|
+
for (const line of lines) deps.err(`repo mutate: ${line}${NEWLINE}`);
|
|
41
|
+
return FAILED;
|
|
42
|
+
}
|