@blzsky/variant 0.2.1 → 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 (47) hide show
  1. package/README.md +110 -56
  2. package/dist/{build-3DSBMMI3.js → build-GKWNLU4Q.js} +9 -8
  3. package/dist/{check-SIQJU6Z2.js → check-KO5VY67Z.js} +6 -6
  4. package/dist/{chunk-FA64KWJ2.js → chunk-7NC7AO75.js} +15 -28
  5. package/dist/{chunk-VYWPQKMS.js → chunk-ACTY42RE.js} +20 -0
  6. package/dist/{chunk-ZH2PQUNL.js → chunk-CN25SRKD.js} +6 -6
  7. package/dist/{chunk-43VLELTL.js → chunk-CWF6QXEW.js} +1 -1
  8. package/dist/{chunk-7OPTWU6E.js → chunk-DMDAJPFJ.js} +1 -1
  9. package/dist/{chunk-RC23JQKI.js → chunk-DSQDMFQ6.js} +9 -1
  10. package/dist/chunk-H5C4SE73.js +184 -0
  11. package/dist/chunk-IFFZ46YS.js +575 -0
  12. package/dist/chunk-IHXJ72XA.js +31 -0
  13. package/dist/chunk-KCEGA56S.js +670 -0
  14. package/dist/{chunk-FZEXZBXB.js → chunk-MAK3HG46.js} +41 -9
  15. package/dist/chunk-MT4BOAAV.js +167 -0
  16. package/dist/{chunk-TCISS2AS.js → chunk-NFFXYYM6.js} +120 -25
  17. package/dist/{chunk-26DHEXGS.js → chunk-RJBFCURK.js} +1 -1
  18. package/dist/{chunk-6BXSZPHC.js → chunk-RSIOYQOQ.js} +67 -8
  19. package/dist/chunk-SGP5JX6I.js +377 -0
  20. package/dist/chunk-X3VVGTEV.js +268 -0
  21. package/dist/{chunk-Q7RLUZSN.js → chunk-YII4TAKS.js} +10 -3
  22. package/dist/cli.js +62 -27
  23. package/dist/{diff-HSJBZFMC.js → diff-A3TQWWUJ.js} +5 -5
  24. package/dist/{doctor-B7MHFIYJ.js → doctor-6ONS6LKY.js} +4 -4
  25. package/dist/{env-I7JZRWDN.js → env-XF2US3CB.js} +6 -6
  26. package/dist/git-W6N3WMYQ.js +24 -0
  27. package/dist/impact-DPZIAJR4.js +263 -0
  28. package/dist/index.js +2 -2
  29. package/dist/init-VNK5E4H3.js +777 -0
  30. package/dist/{insight-JQLTGFYB.js → insight-LNYVMCAE.js} +7 -7
  31. package/dist/pr-6CYMM2XU.js +707 -0
  32. package/dist/{run-C6VAQOVM.js → run-44PKMOV5.js} +9 -8
  33. package/dist/{symbol-graph-E5GXKDFZ.js → symbol-graph-OB4AJCLW.js} +3 -3
  34. package/dist/{tsconfig-paths-E3UEDGXZ.js → tsconfig-paths-SV3PULCW.js} +3 -1
  35. package/dist/vitest.cjs +629 -0
  36. package/dist/vitest.d.cts +181 -0
  37. package/dist/vitest.d.ts +181 -0
  38. package/dist/vitest.js +356 -0
  39. package/dist/workspace-PIQXG4ZD.js +65 -0
  40. package/package.json +13 -2
  41. package/dist/chunk-DRMASVYN.js +0 -25
  42. package/dist/chunk-KSHWC2XN.js +0 -78
  43. package/dist/chunk-XTYVVUWW.js +0 -279
  44. package/dist/impact-PRZ6HEP5.js +0 -735
  45. package/dist/init-O2BY3FOY.js +0 -158
  46. package/dist/pr-Y724QAHE.js +0 -165
  47. package/dist/workspace-HRTFGWKN.js +0 -186
package/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  CliUsageError,
13
13
  VariantError
14
- } from "./chunk-VYWPQKMS.js";
14
+ } from "./chunk-ACTY42RE.js";
15
15
 
16
16
  // src/cli/index.ts
17
17
  import { readFileSync } from "fs";
@@ -120,19 +120,26 @@ 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-PRZ6HEP5.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 },
127
134
  ...opts.json === true && { json: true }
128
135
  });
129
136
  });
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(async (report, _opts, command) => {
133
- const { registerImpactVerifyAction } = await import("./impact-PRZ6HEP5.js");
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");
134
141
  const opts = command.optsWithGlobals();
135
- await registerImpactVerifyAction(report, {
142
+ await registerImpactVerifyAction(reports, {
136
143
  ...opts.headSha !== void 0 && { headSha: opts.headSha },
137
144
  ...opts.json === true && { json: true }
138
145
  });
@@ -140,14 +147,14 @@ impactCmd.command("verify <report>").description(
140
147
  program.command("diff <file>").description(
141
148
  "Classify a TypeScript file change as non-impacting / internal / breaking"
142
149
  ).option("--base <ref>", "git ref to compare against", "HEAD~1").action(async (file, opts) => {
143
- const { registerDiffAction } = await import("./diff-HSJBZFMC.js");
150
+ const { registerDiffAction } = await import("./diff-A3TQWWUJ.js");
144
151
  await registerDiffAction(file, { base: opts.base });
145
152
  });
146
153
  var workspaceCmd = program.command("workspace").description("Workspace-level analysis commands");
147
154
  workspaceCmd.command("check").description(
148
155
  "Detect packages importing dependencies they do not declare (CI gate: exits 1 on violations)"
149
156
  ).option("--json", "output the result as JSON").action(async (opts) => {
150
- const { registerWorkspaceCheckAction } = await import("./workspace-HRTFGWKN.js");
157
+ const { registerWorkspaceCheckAction } = await import("./workspace-PIQXG4ZD.js");
151
158
  await registerWorkspaceCheckAction({
152
159
  ...opts.json === true && { json: true }
153
160
  });
@@ -155,14 +162,28 @@ workspaceCmd.command("check").description(
155
162
  var prCmd = program.command("pr").description("PR-aware analysis commands");
156
163
  prCmd.command("check").description(
157
164
  "Classify all TypeScript changes in this PR and report a build verdict"
158
- ).option("--base <ref>", "base branch or ref for the PR diff", "main").action(async (opts) => {
159
- const { registerPrCheckAction } = await import("./pr-Y724QAHE.js");
160
- 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
+ });
161
173
  });
162
- 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(
163
180
  async (opts) => {
164
- const { registerPrReportAction } = await import("./pr-Y724QAHE.js");
165
- 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
+ });
166
187
  }
167
188
  );
168
189
  program.command("build").description("Run the build task").option("-c, --concurrency <n>", "max tasks per DAG level").option(
@@ -170,7 +191,7 @@ program.command("build").description("Run the build task").option("-c, --concurr
170
191
  "run only packages affected by changes since baseRef (includes cascade dependents)"
171
192
  ).option("--dry-run", "print the task plan without executing").action(
172
193
  async (opts) => {
173
- const { registerBuildAction } = await import("./build-3DSBMMI3.js");
194
+ const { registerBuildAction } = await import("./build-GKWNLU4Q.js");
174
195
  const concurrency = parseConcurrency(opts);
175
196
  await registerBuildAction({
176
197
  ...concurrency !== void 0 && { concurrency },
@@ -184,7 +205,7 @@ program.command("run <task>").description("Run a named task").option(
184
205
  "max tasks to run concurrently per DAG level"
185
206
  ).option("--dry-run", "print the task plan without executing").action(
186
207
  async (taskName, opts) => {
187
- const { registerRunAction } = await import("./run-C6VAQOVM.js");
208
+ const { registerRunAction } = await import("./run-44PKMOV5.js");
188
209
  const concurrency = parseConcurrency(opts);
189
210
  await registerRunAction(taskName, {
190
211
  ...concurrency !== void 0 && { concurrency },
@@ -192,26 +213,40 @@ program.command("run <task>").description("Run a named task").option(
192
213
  });
193
214
  }
194
215
  );
195
- program.command("init").description("Scaffold variant.config.ts in the current directory").action(async () => {
196
- const { registerInitAction } = await import("./init-O2BY3FOY.js");
197
- await registerInitAction();
198
- });
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
+ );
199
234
  program.command("doctor").description("Validate config and diagnose common issues").action(async () => {
200
- const { registerDoctorAction } = await import("./doctor-B7MHFIYJ.js");
235
+ const { registerDoctorAction } = await import("./doctor-6ONS6LKY.js");
201
236
  await registerDoctorAction();
202
237
  });
203
238
  program.command("insight").description("Show task timing and cache hit stats").action(async () => {
204
- const { registerInsightAction } = await import("./insight-JQLTGFYB.js");
239
+ const { registerInsightAction } = await import("./insight-LNYVMCAE.js");
205
240
  await registerInsightAction();
206
241
  });
207
242
  program.command("env").description(
208
243
  "Show detected environment (runtime, package manager, framework)"
209
244
  ).action(async () => {
210
- const { registerEnvAction } = await import("./env-I7JZRWDN.js");
245
+ const { registerEnvAction } = await import("./env-XF2US3CB.js");
211
246
  await registerEnvAction();
212
247
  });
213
248
  program.command("check").description("Validate config and task graph (no execution)").action(async () => {
214
- const { registerCheckAction } = await import("./check-SIQJU6Z2.js");
249
+ const { registerCheckAction } = await import("./check-KO5VY67Z.js");
215
250
  await registerCheckAction();
216
251
  });
217
252
  for (const stream of [process.stdout, process.stderr]) {
@@ -2,19 +2,19 @@ import {
2
2
  DEFAULT_DIFF_BASE_REF,
3
3
  classifyFileAgainstRef,
4
4
  toWorkspaceRelative
5
- } from "./chunk-Q7RLUZSN.js";
5
+ } from "./chunk-YII4TAKS.js";
6
6
  import {
7
7
  assertBaseRef
8
- } from "./chunk-FZEXZBXB.js";
9
- import "./chunk-6BXSZPHC.js";
8
+ } from "./chunk-MAK3HG46.js";
10
9
  import {
11
10
  lines
12
11
  } from "./chunk-6J4XDG44.js";
13
12
  import {
14
13
  getPrinter
15
14
  } from "./chunk-EISTWXDW.js";
16
- import "./chunk-RC23JQKI.js";
17
- import "./chunk-VYWPQKMS.js";
15
+ import "./chunk-RSIOYQOQ.js";
16
+ import "./chunk-DSQDMFQ6.js";
17
+ import "./chunk-ACTY42RE.js";
18
18
 
19
19
  // src/cli/render/diff.ts
20
20
  var CLASS_MEANING = {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  findVariantConfigPath,
3
3
  loadConfig
4
- } from "./chunk-26DHEXGS.js";
4
+ } from "./chunk-RJBFCURK.js";
5
5
  import {
6
6
  getColors
7
7
  } from "./chunk-YYGCSKIQ.js";
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  getPrinter
13
13
  } from "./chunk-EISTWXDW.js";
14
- import "./chunk-VYWPQKMS.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
  }
@@ -3,16 +3,16 @@ import {
3
3
  } from "./chunk-QRPI6TJE.js";
4
4
  import {
5
5
  createContext
6
- } from "./chunk-ZH2PQUNL.js";
6
+ } from "./chunk-CN25SRKD.js";
7
7
  import "./chunk-V5CTGXMM.js";
8
- import "./chunk-26DHEXGS.js";
9
- import "./chunk-YYGCSKIQ.js";
10
- import "./chunk-7OPTWU6E.js";
8
+ import "./chunk-RJBFCURK.js";
9
+ import "./chunk-DMDAJPFJ.js";
11
10
  import "./chunk-3E5LBU3O.js";
12
- import "./chunk-6BXSZPHC.js";
11
+ import "./chunk-YYGCSKIQ.js";
13
12
  import "./chunk-6J4XDG44.js";
14
13
  import "./chunk-EISTWXDW.js";
15
- import "./chunk-VYWPQKMS.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-26DHEXGS.js";
4
- import "./chunk-VYWPQKMS.js";
3
+ } from "./chunk-RJBFCURK.js";
4
+ import "./chunk-ACTY42RE.js";
5
5
  export {
6
6
  defineConfig
7
7
  };