@blzsky/variant 0.2.0 → 0.3.0-canary.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.
Files changed (49) hide show
  1. package/README.md +241 -136
  2. package/dist/{build-2LO7GD2S.js → build-GKWNLU4Q.js} +11 -10
  3. package/dist/{check-ZTKGGF4M.js → check-KO5VY67Z.js} +9 -9
  4. package/dist/{chunk-4LLK2S4R.js → chunk-7NC7AO75.js} +19 -32
  5. package/dist/{chunk-RVXUCJYA.js → chunk-ACTY42RE.js} +27 -0
  6. package/dist/{chunk-SWPVR2DF.js → chunk-CN25SRKD.js} +9 -9
  7. package/dist/{chunk-LXKLCLL6.js → chunk-CWF6QXEW.js} +1 -1
  8. package/dist/{chunk-MZZOE7L4.js → chunk-DMDAJPFJ.js} +1 -1
  9. package/dist/{chunk-O4LZKXNZ.js → chunk-DSQDMFQ6.js} +59 -2
  10. package/dist/{chunk-V22IPJ7A.js → chunk-EISTWXDW.js} +5 -1
  11. package/dist/chunk-H5C4SE73.js +184 -0
  12. package/dist/chunk-IFFZ46YS.js +575 -0
  13. package/dist/chunk-IHXJ72XA.js +31 -0
  14. package/dist/chunk-KCEGA56S.js +670 -0
  15. package/dist/{chunk-LJDDS4RR.js → chunk-MAK3HG46.js} +54 -8
  16. package/dist/chunk-MT4BOAAV.js +167 -0
  17. package/dist/{chunk-VTJ67TT5.js → chunk-NFFXYYM6.js} +122 -27
  18. package/dist/{chunk-ISRF5F3Q.js → chunk-QRPI6TJE.js} +1 -1
  19. package/dist/{chunk-RGD422OM.js → chunk-RJBFCURK.js} +1 -1
  20. package/dist/{chunk-XUBWCTJZ.js → chunk-RSIOYQOQ.js} +85 -8
  21. package/dist/chunk-SGP5JX6I.js +377 -0
  22. package/dist/chunk-X3VVGTEV.js +268 -0
  23. package/dist/{chunk-NSNZOH5P.js → chunk-YII4TAKS.js} +10 -3
  24. package/dist/cli.js +76 -37
  25. package/dist/{diff-GFI6RAEA.js → diff-A3TQWWUJ.js} +9 -5
  26. package/dist/{doctor-GH4RZVEH.js → doctor-6ONS6LKY.js} +5 -5
  27. package/dist/{env-QORDD6FQ.js → env-XF2US3CB.js} +8 -8
  28. package/dist/git-W6N3WMYQ.js +24 -0
  29. package/dist/impact-DPZIAJR4.js +263 -0
  30. package/dist/index.js +2 -2
  31. package/dist/init-VNK5E4H3.js +777 -0
  32. package/dist/{insight-DZ5NQY6D.js → insight-LNYVMCAE.js} +9 -9
  33. package/dist/pr-6CYMM2XU.js +707 -0
  34. package/dist/{run-6PNH4QRA.js → run-44PKMOV5.js} +11 -10
  35. package/dist/{symbol-graph-YYUEMZGR.js → symbol-graph-OB4AJCLW.js} +3 -3
  36. package/dist/{tsconfig-paths-E3UEDGXZ.js → tsconfig-paths-SV3PULCW.js} +3 -1
  37. package/dist/vitest.cjs +629 -0
  38. package/dist/vitest.d.cts +181 -0
  39. package/dist/vitest.d.ts +181 -0
  40. package/dist/vitest.js +356 -0
  41. package/dist/workspace-PIQXG4ZD.js +65 -0
  42. package/package.json +15 -4
  43. package/dist/chunk-DRMASVYN.js +0 -25
  44. package/dist/chunk-KSHWC2XN.js +0 -78
  45. package/dist/chunk-XTYVVUWW.js +0 -279
  46. package/dist/impact-AF453PLK.js +0 -702
  47. package/dist/init-KJIUNGAN.js +0 -158
  48. package/dist/pr-UFZRZX6S.js +0 -152
  49. package/dist/workspace-X5FUBTY3.js +0 -186
package/dist/cli.js CHANGED
@@ -4,14 +4,14 @@ import {
4
4
  resolveColorChoice,
5
5
  writeGlobalColorChoice
6
6
  } from "./chunk-YYGCSKIQ.js";
7
- import {
8
- CliUsageError,
9
- VariantError
10
- } from "./chunk-RVXUCJYA.js";
11
7
  import {
12
8
  Printer,
13
9
  setGlobalPrinter
14
- } from "./chunk-V22IPJ7A.js";
10
+ } from "./chunk-EISTWXDW.js";
11
+ import {
12
+ CliUsageError,
13
+ VariantError
14
+ } from "./chunk-ACTY42RE.js";
15
15
 
16
16
  // src/cli/index.ts
17
17
  import { readFileSync } from "fs";
@@ -120,35 +120,41 @@ var program = new Command().name("variant").description(
120
120
  });
121
121
  var impactCmd = program.command("impact").description(
122
122
  "Predict which tests a change requires \u2014 report-only; the full suite should still run"
123
- ).option("--base <ref>", "git ref to compare against", "HEAD~1").option("--json", "output the report as JSON").action(async (opts) => {
124
- const { registerImpactAction } = await import("./impact-AF453PLK.js");
123
+ ).option(
124
+ "--base <ref>",
125
+ "git ref to compare against (default: detected from git and CI)"
126
+ ).option(
127
+ "--head-sha <sha>",
128
+ "record the prediction against this commit instead of HEAD"
129
+ ).option("--json", "output the report as JSON").action(async (opts) => {
130
+ const { registerImpactAction } = await import("./impact-DPZIAJR4.js");
125
131
  await registerImpactAction({
126
- base: opts.base,
132
+ ...opts.base !== void 0 && { base: opts.base },
133
+ ...opts.headSha !== void 0 && { headSha: opts.headSha },
134
+ ...opts.json === true && { json: true }
135
+ });
136
+ });
137
+ impactCmd.command("verify <report...>").description(
138
+ "Reconcile a logged prediction against one or more Vitest/Jest JSON reports \u2014 reports false skips"
139
+ ).option("--head-sha <sha>", "reconcile the prediction made at this commit").option("--json", "output the reconciliation as JSON").action(async (reports, _opts, command) => {
140
+ const { registerImpactVerifyAction } = await import("./impact-DPZIAJR4.js");
141
+ const opts = command.optsWithGlobals();
142
+ await registerImpactVerifyAction(reports, {
143
+ ...opts.headSha !== void 0 && { headSha: opts.headSha },
127
144
  ...opts.json === true && { json: true }
128
145
  });
129
146
  });
130
- impactCmd.command("verify <report>").description(
131
- "Reconcile a logged prediction against a Vitest/Jest JSON report \u2014 reports false skips"
132
- ).option("--head-sha <sha>", "reconcile the prediction made at this commit").option("--json", "output the reconciliation as JSON").action(
133
- async (report, opts) => {
134
- const { registerImpactVerifyAction } = await import("./impact-AF453PLK.js");
135
- await registerImpactVerifyAction(report, {
136
- ...opts.headSha !== void 0 && { headSha: opts.headSha },
137
- ...opts.json === true && { json: true }
138
- });
139
- }
140
- );
141
147
  program.command("diff <file>").description(
142
148
  "Classify a TypeScript file change as non-impacting / internal / breaking"
143
149
  ).option("--base <ref>", "git ref to compare against", "HEAD~1").action(async (file, opts) => {
144
- const { registerDiffAction } = await import("./diff-GFI6RAEA.js");
150
+ const { registerDiffAction } = await import("./diff-A3TQWWUJ.js");
145
151
  await registerDiffAction(file, { base: opts.base });
146
152
  });
147
153
  var workspaceCmd = program.command("workspace").description("Workspace-level analysis commands");
148
154
  workspaceCmd.command("check").description(
149
155
  "Detect packages importing dependencies they do not declare (CI gate: exits 1 on violations)"
150
156
  ).option("--json", "output the result as JSON").action(async (opts) => {
151
- const { registerWorkspaceCheckAction } = await import("./workspace-X5FUBTY3.js");
157
+ const { registerWorkspaceCheckAction } = await import("./workspace-PIQXG4ZD.js");
152
158
  await registerWorkspaceCheckAction({
153
159
  ...opts.json === true && { json: true }
154
160
  });
@@ -156,14 +162,28 @@ workspaceCmd.command("check").description(
156
162
  var prCmd = program.command("pr").description("PR-aware analysis commands");
157
163
  prCmd.command("check").description(
158
164
  "Classify all TypeScript changes in this PR and report a build verdict"
159
- ).option("--base <ref>", "base branch or ref for the PR diff", "main").action(async (opts) => {
160
- const { registerPrCheckAction } = await import("./pr-UFZRZX6S.js");
161
- await registerPrCheckAction({ base: opts.base });
165
+ ).option(
166
+ "--base <ref>",
167
+ "base branch or ref for the PR diff (default: the target or default branch)"
168
+ ).action(async (opts) => {
169
+ const { registerPrCheckAction } = await import("./pr-6CYMM2XU.js");
170
+ await registerPrCheckAction({
171
+ ...opts.base !== void 0 && { base: opts.base }
172
+ });
162
173
  });
163
- prCmd.command("report").description("Render `pr check` as a structured JSON or markdown report").option("--base <ref>", "base branch or ref for the PR diff", "main").option("--markdown", "output a markdown summary instead of JSON").option("--output <file>", "write report to file instead of stdout").action(
174
+ prCmd.command("report").description(
175
+ "Describe what this PR changes and what must be verified before it merges"
176
+ ).option(
177
+ "--base <ref>",
178
+ "base branch or ref for the PR diff (default: the target or default branch)"
179
+ ).option("--markdown", "output a markdown summary instead of JSON").option("--output <file>", "write report to file instead of stdout").action(
164
180
  async (opts) => {
165
- const { registerPrReportAction } = await import("./pr-UFZRZX6S.js");
166
- await registerPrReportAction(opts);
181
+ const { registerPrReportAction } = await import("./pr-6CYMM2XU.js");
182
+ await registerPrReportAction({
183
+ ...opts.base !== void 0 && { base: opts.base },
184
+ ...opts.markdown === true && { markdown: true },
185
+ ...opts.output !== void 0 && { output: opts.output }
186
+ });
167
187
  }
168
188
  );
169
189
  program.command("build").description("Run the build task").option("-c, --concurrency <n>", "max tasks per DAG level").option(
@@ -171,7 +191,7 @@ program.command("build").description("Run the build task").option("-c, --concurr
171
191
  "run only packages affected by changes since baseRef (includes cascade dependents)"
172
192
  ).option("--dry-run", "print the task plan without executing").action(
173
193
  async (opts) => {
174
- const { registerBuildAction } = await import("./build-2LO7GD2S.js");
194
+ const { registerBuildAction } = await import("./build-GKWNLU4Q.js");
175
195
  const concurrency = parseConcurrency(opts);
176
196
  await registerBuildAction({
177
197
  ...concurrency !== void 0 && { concurrency },
@@ -185,7 +205,7 @@ program.command("run <task>").description("Run a named task").option(
185
205
  "max tasks to run concurrently per DAG level"
186
206
  ).option("--dry-run", "print the task plan without executing").action(
187
207
  async (taskName, opts) => {
188
- const { registerRunAction } = await import("./run-6PNH4QRA.js");
208
+ const { registerRunAction } = await import("./run-44PKMOV5.js");
189
209
  const concurrency = parseConcurrency(opts);
190
210
  await registerRunAction(taskName, {
191
211
  ...concurrency !== void 0 && { concurrency },
@@ -193,28 +213,47 @@ program.command("run <task>").description("Run a named task").option(
193
213
  });
194
214
  }
195
215
  );
196
- program.command("init").description("Scaffold variant.config.ts in the current directory").action(async () => {
197
- const { registerInitAction } = await import("./init-KJIUNGAN.js");
198
- await registerInitAction();
199
- });
216
+ program.command("init").description(
217
+ "Set variant up in this repository: detect how it is tested, show the changes, apply them"
218
+ ).option("--dry-run", "show what would change and stop").option("-y, --yes", "apply without asking").option("--no-install", "do not install @blzsky/variant; print the command").option(
219
+ "--package <spec>",
220
+ "install this in place of the running version (a tarball path or a spec)"
221
+ ).option("--tasks", "write variant.config.ts for the task runner instead").action(
222
+ async (opts) => {
223
+ const { registerInitAction } = await import("./init-VNK5E4H3.js");
224
+ await registerInitAction({
225
+ version: _pkg.version,
226
+ ...opts.dryRun === true && { dryRun: true },
227
+ ...opts.yes === true && { yes: true },
228
+ ...opts.install === false && { install: false },
229
+ ...opts.package !== void 0 && { package: opts.package },
230
+ ...opts.tasks === true && { tasks: true }
231
+ });
232
+ }
233
+ );
200
234
  program.command("doctor").description("Validate config and diagnose common issues").action(async () => {
201
- const { registerDoctorAction } = await import("./doctor-GH4RZVEH.js");
235
+ const { registerDoctorAction } = await import("./doctor-6ONS6LKY.js");
202
236
  await registerDoctorAction();
203
237
  });
204
238
  program.command("insight").description("Show task timing and cache hit stats").action(async () => {
205
- const { registerInsightAction } = await import("./insight-DZ5NQY6D.js");
239
+ const { registerInsightAction } = await import("./insight-LNYVMCAE.js");
206
240
  await registerInsightAction();
207
241
  });
208
242
  program.command("env").description(
209
243
  "Show detected environment (runtime, package manager, framework)"
210
244
  ).action(async () => {
211
- const { registerEnvAction } = await import("./env-QORDD6FQ.js");
245
+ const { registerEnvAction } = await import("./env-XF2US3CB.js");
212
246
  await registerEnvAction();
213
247
  });
214
248
  program.command("check").description("Validate config and task graph (no execution)").action(async () => {
215
- const { registerCheckAction } = await import("./check-ZTKGGF4M.js");
249
+ const { registerCheckAction } = await import("./check-KO5VY67Z.js");
216
250
  await registerCheckAction();
217
251
  });
252
+ for (const stream of [process.stdout, process.stderr]) {
253
+ stream.on("error", (error) => {
254
+ if (error.code !== "EPIPE" && error.code !== "EOF") throw error;
255
+ });
256
+ }
218
257
  program.parseAsync(process.argv).catch((err) => {
219
258
  if (err instanceof VariantError) {
220
259
  renderError(err);
@@ -2,16 +2,19 @@ import {
2
2
  DEFAULT_DIFF_BASE_REF,
3
3
  classifyFileAgainstRef,
4
4
  toWorkspaceRelative
5
- } from "./chunk-NSNZOH5P.js";
6
- import "./chunk-LJDDS4RR.js";
7
- import "./chunk-XUBWCTJZ.js";
5
+ } from "./chunk-YII4TAKS.js";
6
+ import {
7
+ assertBaseRef
8
+ } from "./chunk-MAK3HG46.js";
8
9
  import {
9
10
  lines
10
11
  } from "./chunk-6J4XDG44.js";
11
12
  import {
12
13
  getPrinter
13
- } from "./chunk-V22IPJ7A.js";
14
- import "./chunk-O4LZKXNZ.js";
14
+ } from "./chunk-EISTWXDW.js";
15
+ import "./chunk-RSIOYQOQ.js";
16
+ import "./chunk-DSQDMFQ6.js";
17
+ import "./chunk-ACTY42RE.js";
15
18
 
16
19
  // src/cli/render/diff.ts
17
20
  var CLASS_MEANING = {
@@ -50,6 +53,7 @@ async function registerDiffAction(filePath, opts = {}) {
50
53
  const cwd = process.cwd();
51
54
  const baseRef = opts.base ?? DEFAULT_DIFF_BASE_REF;
52
55
  const relPath = toWorkspaceRelative(cwd, filePath);
56
+ await assertBaseRef(cwd, baseRef);
53
57
  renderClassification(
54
58
  await classifyFileAgainstRef(cwd, relPath, baseRef),
55
59
  baseRef
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  findVariantConfigPath,
3
3
  loadConfig
4
- } from "./chunk-RGD422OM.js";
4
+ } from "./chunk-RJBFCURK.js";
5
5
  import {
6
6
  getColors
7
7
  } from "./chunk-YYGCSKIQ.js";
8
- import "./chunk-RVXUCJYA.js";
9
8
  import {
10
9
  lines
11
10
  } from "./chunk-6J4XDG44.js";
12
11
  import {
13
12
  getPrinter
14
- } from "./chunk-V22IPJ7A.js";
13
+ } from "./chunk-EISTWXDW.js";
14
+ import "./chunk-ACTY42RE.js";
15
15
 
16
16
  // src/core/doctor/diagnostics.ts
17
17
  import { readdir, stat } from "fs/promises";
@@ -71,9 +71,9 @@ async function checkConfig(cwd) {
71
71
  if (!configPath) {
72
72
  return [
73
73
  {
74
- level: "error",
74
+ level: "warn",
75
75
  label: "No variant.config.ts found",
76
- detail: "Run `variant init` to create one."
76
+ detail: "`impact`, `diff`, `pr` and `workspace check` work without one. `build` and `run` need it: `variant init --tasks` creates one."
77
77
  }
78
78
  ];
79
79
  }
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  renderEnv
3
- } from "./chunk-ISRF5F3Q.js";
3
+ } from "./chunk-QRPI6TJE.js";
4
4
  import {
5
5
  createContext
6
- } from "./chunk-SWPVR2DF.js";
6
+ } from "./chunk-CN25SRKD.js";
7
7
  import "./chunk-V5CTGXMM.js";
8
- import "./chunk-RGD422OM.js";
9
- import "./chunk-YYGCSKIQ.js";
10
- import "./chunk-MZZOE7L4.js";
8
+ import "./chunk-RJBFCURK.js";
9
+ import "./chunk-DMDAJPFJ.js";
11
10
  import "./chunk-3E5LBU3O.js";
12
- import "./chunk-RVXUCJYA.js";
13
- import "./chunk-XUBWCTJZ.js";
11
+ import "./chunk-YYGCSKIQ.js";
14
12
  import "./chunk-6J4XDG44.js";
15
- import "./chunk-V22IPJ7A.js";
13
+ import "./chunk-EISTWXDW.js";
14
+ import "./chunk-RSIOYQOQ.js";
15
+ import "./chunk-ACTY42RE.js";
16
16
 
17
17
  // src/cli/commands/env.ts
18
18
  async function registerEnvAction() {
@@ -0,0 +1,24 @@
1
+ import {
2
+ defaultBranchRef,
3
+ listChangedFiles,
4
+ listChangedFilesSinceMergeBase,
5
+ listRepositoryFiles,
6
+ mergeBase,
7
+ readFileAtRef,
8
+ readFilesAtRef,
9
+ readHeadSha,
10
+ repositoryRoot,
11
+ resolveCommit
12
+ } from "./chunk-RSIOYQOQ.js";
13
+ export {
14
+ defaultBranchRef,
15
+ listChangedFiles,
16
+ listChangedFilesSinceMergeBase,
17
+ listRepositoryFiles,
18
+ mergeBase,
19
+ readFileAtRef,
20
+ readFilesAtRef,
21
+ readHeadSha,
22
+ repositoryRoot,
23
+ resolveCommit
24
+ };
@@ -0,0 +1,263 @@
1
+ import {
2
+ predictImpact,
3
+ verdictText
4
+ } from "./chunk-KCEGA56S.js";
5
+ import "./chunk-IFFZ46YS.js";
6
+ import "./chunk-GDSZFIHM.js";
7
+ import {
8
+ classificationLabel,
9
+ plural
10
+ } from "./chunk-CXNXZLDG.js";
11
+ import "./chunk-DMDAJPFJ.js";
12
+ import "./chunk-3E5LBU3O.js";
13
+ import {
14
+ getColors
15
+ } from "./chunk-YYGCSKIQ.js";
16
+ import {
17
+ verifyImpact
18
+ } from "./chunk-MT4BOAAV.js";
19
+ import {
20
+ changeBaseDeps,
21
+ detectHeadSha
22
+ } from "./chunk-H5C4SE73.js";
23
+ import "./chunk-X3VVGTEV.js";
24
+ import "./chunk-NFFXYYM6.js";
25
+ import "./chunk-MAK3HG46.js";
26
+ import {
27
+ lines
28
+ } from "./chunk-6J4XDG44.js";
29
+ import {
30
+ getPrinter
31
+ } from "./chunk-EISTWXDW.js";
32
+ import "./chunk-RSIOYQOQ.js";
33
+ import "./chunk-DSQDMFQ6.js";
34
+ import {
35
+ NoBaseCommitError
36
+ } from "./chunk-ACTY42RE.js";
37
+
38
+ // src/cli/render/impact.ts
39
+ var MAX_LISTED_FILES = 20;
40
+ var count = (value) => value.toLocaleString("en-US");
41
+ var NO_PREDICTION_MESSAGE = "impact verify: no logged prediction to reconcile against. Run `variant impact` before the test run, or pass --head-sha for a specific commit.";
42
+ function shortRef(ref) {
43
+ return /^[0-9a-f]{40}$/.test(ref) ? ref.slice(0, 12) : ref;
44
+ }
45
+ function renderNoBaseCommit(reason, json, printer = getPrinter()) {
46
+ lines(
47
+ printer,
48
+ json ? JSON.stringify(
49
+ { prediction: null, code: "NO_BASE_COMMIT", reason },
50
+ null,
51
+ 2
52
+ ) : `impact: ${reason}; no prediction was made.`
53
+ );
54
+ }
55
+ function renderImpactJson(report, printer = getPrinter()) {
56
+ const { result, ...rest } = report;
57
+ lines(
58
+ printer,
59
+ JSON.stringify(
60
+ { ...rest, radius: result.radius, tests: result.tests },
61
+ null,
62
+ 2
63
+ )
64
+ );
65
+ }
66
+ function renderImpact(report, printer = getPrinter()) {
67
+ const { radius, tests } = report.result;
68
+ lines(
69
+ printer,
70
+ "",
71
+ `Base ref: ${shortRef(report.baseRef)}${report.baseLabel === report.baseRef ? "" : ` (${report.baseLabel})`}`,
72
+ `Workspace: ${report.packagesFound === 0 ? "no workspace packages found" : plural(report.packagesFound, "package")}`,
73
+ "",
74
+ `You changed ${plural(radius.changed.length, "file")}.`
75
+ );
76
+ for (const impact of radius.changed.slice(0, MAX_LISTED_FILES)) {
77
+ const symbols = impact.impactedSymbols.length > 0 ? ` (${impact.impactedSymbols.join(", ")})` : "";
78
+ lines(
79
+ printer,
80
+ ` ${classificationLabel(impact.classification)} ${impact.filePath}${symbols}`
81
+ );
82
+ }
83
+ if (radius.changed.length > MAX_LISTED_FILES) {
84
+ lines(
85
+ printer,
86
+ ` \u2026 and ${count(radius.changed.length - MAX_LISTED_FILES)} more`
87
+ );
88
+ }
89
+ const packages = radius.affectedPackages.length > 0 ? `, ${plural(radius.affectedPackages.length, "package")} (${radius.affectedPackages.join(", ")})` : "";
90
+ lines(
91
+ printer,
92
+ "",
93
+ `Impact: ${plural(radius.affectedFiles.length, "file")}${packages}`
94
+ );
95
+ const skipped = tests.totalTests - tests.affectedTests.length;
96
+ lines(
97
+ printer,
98
+ "",
99
+ `Run: ${plural(tests.affectedTests.length, "test file")}`,
100
+ `Skip: ${plural(skipped, "test file")} (of ${count(tests.totalTests)} total)`
101
+ );
102
+ if (tests.unreached.length > 0) {
103
+ const listed = tests.unreached.slice(0, MAX_LISTED_FILES).join(", ");
104
+ const more = tests.unreached.length > MAX_LISTED_FILES ? `, \u2026 ${count(tests.unreached.length - MAX_LISTED_FILES)} more` : "";
105
+ const one = tests.unreached.length === 1;
106
+ lines(
107
+ printer,
108
+ "",
109
+ `${getColors().yellow("Unreached:")} ${plural(tests.unreached.length, "changed file")} no test reaches (${listed}${more}). No test is selected for ${one ? "it: it is" : "them: they are"} untested, or used in a way variant cannot follow.`
110
+ );
111
+ }
112
+ if (tests.unselectedTests.length > 0) {
113
+ const listed = tests.unselectedTests.slice(0, MAX_LISTED_FILES).join(", ");
114
+ const more = tests.unselectedTests.length > MAX_LISTED_FILES ? `, \u2026 ${count(tests.unselectedTests.length - MAX_LISTED_FILES)} more` : "";
115
+ const one = tests.unselectedTests.length === 1;
116
+ lines(
117
+ printer,
118
+ "",
119
+ `${getColors().yellow("Not selected:")} ${plural(tests.unselectedTests.length, "changed test file")} ${one ? "is" : "are"} JavaScript (${listed}${more}). variant does not index JavaScript tests, so it cannot select them; run them yourself.`
120
+ );
121
+ }
122
+ const nothingAnalyzed = !tests.selectAll && radius.changed.length > 0 && radius.changed.every((impact) => impact.classification === "unanalyzed");
123
+ if (nothingAnalyzed) {
124
+ lines(
125
+ printer,
126
+ "",
127
+ "Nothing in this change was analyzed: none of the changed files is TypeScript that variant indexes."
128
+ );
129
+ }
130
+ lines(
131
+ printer,
132
+ "",
133
+ `Verdict: ${verdictText(report.verdict)}`,
134
+ `Confidence: ${tests.resolution} (${Math.round(tests.confidence * 100)}%) (report-only \u2014 run the full suite; skipping unlocks after shadow-mode validation)`
135
+ );
136
+ const notes = [...radius.notes, ...tests.notes];
137
+ if (notes.length > 0) {
138
+ lines(printer, "", "Notes:");
139
+ for (const note of notes) lines(printer, ` - ${note}`);
140
+ }
141
+ const standing = [...radius.repositoryNotes, ...tests.repositoryNotes];
142
+ if (standing.length > 0 && printer.mode === "verbose") {
143
+ lines(printer, "", "Repository (the same whatever changed):");
144
+ for (const note of standing) lines(printer, ` - ${note}`);
145
+ } else if (standing.length > 0) {
146
+ lines(
147
+ printer,
148
+ "",
149
+ `Repository: ${plural(standing.length, "standing note")}, the same whatever changed (-v lists them)`
150
+ );
151
+ }
152
+ if (!report.historyLogged) {
153
+ lines(
154
+ printer,
155
+ "",
156
+ "(warning: could not write .variant/history/impact.jsonl \u2014 shadow-mode logging skipped)"
157
+ );
158
+ }
159
+ }
160
+ function renderImpactVerifyJson(result, printer = getPrinter()) {
161
+ const { prediction, ...rest } = result;
162
+ lines(
163
+ printer,
164
+ JSON.stringify(
165
+ { ...rest, predictedAt: prediction.at, baseRef: prediction.baseRef },
166
+ null,
167
+ 2
168
+ )
169
+ );
170
+ }
171
+ function renderImpactVerify(result, printer = getPrinter()) {
172
+ const { prediction } = result;
173
+ lines(
174
+ printer,
175
+ "",
176
+ `Prediction: ${prediction.at} (${prediction.headSha ?? "no commit recorded"})`,
177
+ `Matched by: ${result.matchedBy === "head-sha" ? "head SHA" : "most recent \u2014 verify this is the right run"}`,
178
+ "",
179
+ `Predicted: ${count(prediction.affectedTests.length)} of ${count(prediction.totalTests)} test files${prediction.selectAll ? " (selected all)" : ""}`,
180
+ `Ran: ${plural(result.ranTests, "test file")}, ${count(result.predictedRan)} of them predicted`,
181
+ `Failed: ${plural(result.failedTests.length, "test file")}`
182
+ );
183
+ if (result.failedTests.length === 0) {
184
+ lines(
185
+ printer,
186
+ "",
187
+ "No failures in this run, so it neither confirms nor refutes the prediction."
188
+ );
189
+ return;
190
+ }
191
+ lines(
192
+ printer,
193
+ "",
194
+ `Caught: ${count(result.caught.length)} (inside the predicted set)`,
195
+ `False skips: ${count(result.falseSkips.length)} (would have been missed)`
196
+ );
197
+ if (result.falseSkips.length > 0) {
198
+ lines(printer, "", "False skips:");
199
+ for (const test of result.falseSkips.slice(0, MAX_LISTED_FILES)) {
200
+ lines(printer, ` ${test}`);
201
+ }
202
+ if (result.falseSkips.length > MAX_LISTED_FILES) {
203
+ lines(
204
+ printer,
205
+ ` \u2026 and ${count(result.falseSkips.length - MAX_LISTED_FILES)} more`
206
+ );
207
+ }
208
+ }
209
+ lines(
210
+ printer,
211
+ "",
212
+ `False-skip rate this run: ${(result.falseSkipRate * 100).toFixed(1)}%`,
213
+ "One run is not a rate. Reconcile many before reading anything into it."
214
+ );
215
+ if (!result.historyLogged) {
216
+ lines(
217
+ printer,
218
+ "",
219
+ "(warning: could not write .variant/history/reconciliation.jsonl)"
220
+ );
221
+ }
222
+ }
223
+
224
+ // src/cli/commands/impact.ts
225
+ async function registerImpactAction(opts = {}) {
226
+ let report;
227
+ try {
228
+ report = await predictImpact(process.cwd(), opts);
229
+ } catch (error) {
230
+ if (!(error instanceof NoBaseCommitError)) throw error;
231
+ renderNoBaseCommit(error.message, opts.json === true);
232
+ return;
233
+ }
234
+ if (opts.json === true) {
235
+ renderImpactJson(report);
236
+ return;
237
+ }
238
+ renderImpact(report);
239
+ }
240
+ async function registerImpactVerifyAction(reportPaths, opts = {}) {
241
+ const cwd = process.cwd();
242
+ const detected = opts.headSha === void 0 ? await detectHeadSha(changeBaseDeps(cwd)) : void 0;
243
+ const headSha = opts.headSha ?? detected;
244
+ let result = await verifyImpact(cwd, reportPaths, {
245
+ ...headSha !== void 0 && { headSha }
246
+ });
247
+ if (result === null && opts.headSha === void 0 && detected !== void 0) {
248
+ result = await verifyImpact(cwd, reportPaths);
249
+ }
250
+ if (result === null) {
251
+ lines(getPrinter(), NO_PREDICTION_MESSAGE);
252
+ return;
253
+ }
254
+ if (opts.json === true) {
255
+ renderImpactVerifyJson(result);
256
+ return;
257
+ }
258
+ renderImpactVerify(result);
259
+ }
260
+ export {
261
+ registerImpactAction,
262
+ registerImpactVerifyAction
263
+ };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineConfig
3
- } from "./chunk-RGD422OM.js";
4
- import "./chunk-RVXUCJYA.js";
3
+ } from "./chunk-RJBFCURK.js";
4
+ import "./chunk-ACTY42RE.js";
5
5
  export {
6
6
  defineConfig
7
7
  };