@outfitter/tooling 0.3.5 → 0.3.6-canary-20260317163745

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -11,8 +11,8 @@ import {
11
11
  } from "../shared/@outfitter/tooling-9ram55dd.js";
12
12
  import {
13
13
  runPrePush
14
- } from "../shared/@outfitter/tooling-2vv5y3s4.js";
15
- import"../shared/@outfitter/tooling-c8q6mj8z.js";
14
+ } from "../shared/@outfitter/tooling-j7bczn7t.js";
15
+ import"../shared/@outfitter/tooling-jyanp8q2.js";
16
16
  import {
17
17
  runFix
18
18
  } from "../shared/@outfitter/tooling-1hez6j9d.js";
@@ -1,2 +1,2 @@
1
- import { BunVersionCheckResult, PushChangedFiles, VerificationPlan, areFilesTestOnly, canBypassRedPhaseByChangedFiles, checkBunVersion, createVerificationPlan, getChangedFilesForPush, getCurrentBranch, hasPackageSourceChanges, hasRedPhaseBranchInContext, isRedPhaseBranch, isReleaseBranch, isScaffoldBranch, isTestOnlyPath, printTsdocSummary, readPackageScripts, runGit } from "../../shared/@outfitter/tooling-8qcwr06t.js";
2
- export { runGit, readPackageScripts, printTsdocSummary, isTestOnlyPath, isScaffoldBranch, isReleaseBranch, isRedPhaseBranch, hasRedPhaseBranchInContext, hasPackageSourceChanges, getCurrentBranch, getChangedFilesForPush, createVerificationPlan, checkBunVersion, canBypassRedPhaseByChangedFiles, areFilesTestOnly, VerificationPlan, PushChangedFiles, BunVersionCheckResult };
1
+ import { BunVersionCheckResult, ChangeCategory, ChangeScope, PushChangedFiles, VerificationPlan, areFilesTestOnly, canBypassRedPhaseByChangedFiles, categorizeChangedFiles, checkBunVersion, createVerificationPlan, getChangedFilesForPush, getCurrentBranch, hasPackageSourceChanges, hasRedPhaseBranchInContext, isRedPhaseBranch, isReleaseBranch, isScaffoldBranch, isTestOnlyPath, printTsdocSummary, readPackageScripts, runGit } from "../../shared/@outfitter/tooling-h6br5wds.js";
2
+ export { runGit, readPackageScripts, printTsdocSummary, isTestOnlyPath, isScaffoldBranch, isReleaseBranch, isRedPhaseBranch, hasRedPhaseBranchInContext, hasPackageSourceChanges, getCurrentBranch, getChangedFilesForPush, createVerificationPlan, checkBunVersion, categorizeChangedFiles, canBypassRedPhaseByChangedFiles, areFilesTestOnly, VerificationPlan, PushChangedFiles, ChangeScope, ChangeCategory, BunVersionCheckResult };
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  areFilesTestOnly,
4
4
  canBypassRedPhaseByChangedFiles,
5
+ categorizeChangedFiles,
5
6
  checkBunVersion,
6
7
  createVerificationPlan,
7
8
  getChangedFilesForPush,
@@ -15,7 +16,7 @@ import {
15
16
  printTsdocSummary,
16
17
  readPackageScripts,
17
18
  runGit
18
- } from "../../shared/@outfitter/tooling-c8q6mj8z.js";
19
+ } from "../../shared/@outfitter/tooling-jyanp8q2.js";
19
20
  import"../../shared/@outfitter/tooling-5xxctk9b.js";
20
21
  import"../../shared/@outfitter/tooling-jnrs9rqd.js";
21
22
  export {
@@ -32,6 +33,7 @@ export {
32
33
  getChangedFilesForPush,
33
34
  createVerificationPlan,
34
35
  checkBunVersion,
36
+ categorizeChangedFiles,
35
37
  canBypassRedPhaseByChangedFiles,
36
38
  areFilesTestOnly
37
39
  };
@@ -1,7 +1,7 @@
1
- import { BunVersionCheckResult, PushChangedFiles, VerificationPlan, areFilesTestOnly, canBypassRedPhaseByChangedFiles, checkBunVersion, createVerificationPlan, hasPackageSourceChanges, isRedPhaseBranch, isReleaseBranch, isScaffoldBranch, isTestOnlyPath } from "../shared/@outfitter/tooling-8qcwr06t.js";
1
+ import { BunVersionCheckResult, ChangeCategory, ChangeScope, PushChangedFiles, VerificationPlan, areFilesTestOnly, canBypassRedPhaseByChangedFiles, categorizeChangedFiles, checkBunVersion, createVerificationPlan, hasPackageSourceChanges, isRedPhaseBranch, isReleaseBranch, isScaffoldBranch, isTestOnlyPath } from "../shared/@outfitter/tooling-h6br5wds.js";
2
2
  interface PrePushOptions {
3
3
  force?: boolean;
4
4
  }
5
5
  /** Main pre-push command */
6
6
  declare function runPrePush(options?: PrePushOptions): Promise<void>;
7
- export { runPrePush, isTestOnlyPath, isScaffoldBranch, isReleaseBranch, isRedPhaseBranch, hasPackageSourceChanges, createVerificationPlan, checkBunVersion, canBypassRedPhaseByChangedFiles, areFilesTestOnly, VerificationPlan, PushChangedFiles, PrePushOptions, BunVersionCheckResult };
7
+ export { runPrePush, isTestOnlyPath, isScaffoldBranch, isReleaseBranch, isRedPhaseBranch, hasPackageSourceChanges, createVerificationPlan, checkBunVersion, categorizeChangedFiles, canBypassRedPhaseByChangedFiles, areFilesTestOnly, VerificationPlan, PushChangedFiles, PrePushOptions, ChangeScope, ChangeCategory, BunVersionCheckResult };
@@ -1,10 +1,11 @@
1
1
  // @bun
2
2
  import {
3
3
  runPrePush
4
- } from "../shared/@outfitter/tooling-2vv5y3s4.js";
4
+ } from "../shared/@outfitter/tooling-j7bczn7t.js";
5
5
  import {
6
6
  areFilesTestOnly,
7
7
  canBypassRedPhaseByChangedFiles,
8
+ categorizeChangedFiles,
8
9
  checkBunVersion,
9
10
  createVerificationPlan,
10
11
  hasPackageSourceChanges,
@@ -12,7 +13,7 @@ import {
12
13
  isReleaseBranch,
13
14
  isScaffoldBranch,
14
15
  isTestOnlyPath
15
- } from "../shared/@outfitter/tooling-c8q6mj8z.js";
16
+ } from "../shared/@outfitter/tooling-jyanp8q2.js";
16
17
  import"../shared/@outfitter/tooling-5xxctk9b.js";
17
18
  import"../shared/@outfitter/tooling-jnrs9rqd.js";
18
19
  export {
@@ -24,6 +25,7 @@ export {
24
25
  hasPackageSourceChanges,
25
26
  createVerificationPlan,
26
27
  checkBunVersion,
28
+ categorizeChangedFiles,
27
29
  canBypassRedPhaseByChangedFiles,
28
30
  areFilesTestOnly
29
31
  };
@@ -29,6 +29,45 @@ declare function canBypassRedPhaseByChangedFiles(changedFiles: PushChangedFiles)
29
29
  declare function hasPackageSourceChanges(changedFiles: PushChangedFiles): boolean;
30
30
  /** Determine which files have changed for the current push */
31
31
  declare function getChangedFilesForPush(): PushChangedFiles;
32
+ /**
33
+ * Change scope categories from most to least impactful.
34
+ *
35
+ * - `core`: Foundation packages (contracts, types) — full suite required
36
+ * - `runtime`: Active packages (cli, mcp, config, etc.) — full suite required
37
+ * - `ci`: CI/workflow config — full suite required
38
+ * - `tooling`: Tooling package itself — full suite required
39
+ * - `app`: apps/ changes — full suite required (actions, commands)
40
+ * - `template`: Preset templates only — lightweight lint/format + full test suite
41
+ * - `docs`: Docs, plugins, READMEs — lightweight checks only (lint, format)
42
+ * - `config`: Root config files (.lefthook.yml, etc.) — lightweight checks
43
+ */
44
+ type ChangeScope = "core" | "runtime" | "ci" | "tooling" | "app" | "template" | "docs" | "config";
45
+ /** Result of categorizing changed files by their impact on verification scope. */
46
+ interface ChangeCategory {
47
+ /** The highest-impact scope found among the changed files. */
48
+ readonly scope: ChangeScope;
49
+ /** Whether any changed file requires running the full verification suite. */
50
+ readonly requiresFullSuite: boolean;
51
+ }
52
+ /**
53
+ * Categorize changed files to determine verification scope.
54
+ *
55
+ * Returns the highest-impact category found. If any file requires the
56
+ * full suite, the entire push gets full verification.
57
+ *
58
+ * @param changedFiles - Files changed in the current push
59
+ * @returns Category with scope name and whether full suite is required
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const changed = getChangedFilesForPush();
64
+ * const category = categorizeChangedFiles(changed);
65
+ * if (!category.requiresFullSuite) {
66
+ * // Run lightweight checks only
67
+ * }
68
+ * ```
69
+ */
70
+ declare function categorizeChangedFiles(changedFiles: PushChangedFiles): ChangeCategory;
32
71
  /** Check if any branch in context is a RED phase branch */
33
72
  declare function hasRedPhaseBranchInContext(currentBranch: string): boolean;
34
73
  type ScriptMap = Readonly<Record<string, string | undefined>>;
@@ -71,4 +110,4 @@ declare function checkBunVersion(projectRoot?: string): BunVersionCheckResult;
71
110
  * only -- the result does not affect the exit code.
72
111
  */
73
112
  declare function printTsdocSummary(log: (msg: string) => void): Promise<void>;
74
- export { getCurrentBranch, runGit, isRedPhaseBranch, isScaffoldBranch, isReleaseBranch, isTestOnlyPath, areFilesTestOnly, PushChangedFiles, canBypassRedPhaseByChangedFiles, hasPackageSourceChanges, getChangedFilesForPush, hasRedPhaseBranchInContext, VerificationPlan, createVerificationPlan, readPackageScripts, BunVersionCheckResult, checkBunVersion, printTsdocSummary };
113
+ export { getCurrentBranch, runGit, isRedPhaseBranch, isScaffoldBranch, isReleaseBranch, isTestOnlyPath, areFilesTestOnly, PushChangedFiles, canBypassRedPhaseByChangedFiles, hasPackageSourceChanges, getChangedFilesForPush, ChangeScope, ChangeCategory, categorizeChangedFiles, hasRedPhaseBranchInContext, VerificationPlan, createVerificationPlan, readPackageScripts, BunVersionCheckResult, checkBunVersion, printTsdocSummary };
@@ -1,6 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  canBypassRedPhaseByChangedFiles,
4
+ categorizeChangedFiles,
4
5
  checkBunVersion,
5
6
  createVerificationPlan,
6
7
  getChangedFilesForPush,
@@ -12,7 +13,7 @@ import {
12
13
  isScaffoldBranch,
13
14
  printTsdocSummary,
14
15
  readPackageScripts
15
- } from "./tooling-c8q6mj8z.js";
16
+ } from "./tooling-jyanp8q2.js";
16
17
  // packages/tooling/src/cli/pre-push.ts
17
18
  var COLORS = {
18
19
  reset: "\x1B[0m",
@@ -33,8 +34,7 @@ function runScript(scriptName) {
33
34
  });
34
35
  return result.exitCode === 0;
35
36
  }
36
- function maybeSkipForRedPhase(reason, branch) {
37
- const changedFiles = getChangedFilesForPush();
37
+ function maybeSkipForRedPhase(reason, branch, changedFiles) {
38
38
  if (!changedFiles.deterministic) {
39
39
  log(`${COLORS.yellow}RED-phase bypass denied${COLORS.reset}: could not determine full push diff range`);
40
40
  log("Running strict verification.");
@@ -85,21 +85,63 @@ async function runPrePush(options = {}) {
85
85
  process.exitCode = 0;
86
86
  return;
87
87
  }
88
+ const changedFiles = getChangedFilesForPush();
88
89
  if (isRedPhaseBranch(branch)) {
89
- if (maybeSkipForRedPhase("branch", branch)) {
90
+ if (maybeSkipForRedPhase("branch", branch, changedFiles)) {
90
91
  process.exitCode = 0;
91
92
  return;
92
93
  }
93
94
  }
94
95
  if (isScaffoldBranch(branch)) {
95
96
  if (hasRedPhaseBranchInContext(branch)) {
96
- if (maybeSkipForRedPhase("context", branch)) {
97
+ if (maybeSkipForRedPhase("context", branch, changedFiles)) {
97
98
  process.exitCode = 0;
98
99
  return;
99
100
  }
100
101
  }
101
102
  }
102
- const plan = createVerificationPlan(readPackageScripts());
103
+ const changeCategory = categorizeChangedFiles(changedFiles);
104
+ const scripts = readPackageScripts();
105
+ if (!changeCategory.requiresFullSuite) {
106
+ log(`${COLORS.yellow}Scoped verification${COLORS.reset}: changes are ${COLORS.blue}${changeCategory.scope}${COLORS.reset}-only`);
107
+ log(`Changed files (${changedFiles.files.length}): ${changedFiles.files.slice(0, 5).join(", ")}${changedFiles.files.length > 5 ? ` ...+${changedFiles.files.length - 5} more` : ""}`);
108
+ log("");
109
+ const lightweightScripts = [];
110
+ if (scripts["check"]) {
111
+ lightweightScripts.push("check");
112
+ } else if (scripts["lint"]) {
113
+ lightweightScripts.push("lint");
114
+ }
115
+ if (lightweightScripts.length === 0) {
116
+ log(`${COLORS.yellow}No lint/format script found${COLORS.reset} \u2014 falling through to full verification`);
117
+ } else {
118
+ if (changeCategory.scope === "template") {
119
+ if (scripts["test"]) {
120
+ lightweightScripts.push("test");
121
+ log(`Running: lint/format + tests (template changes need template guardrails)`);
122
+ } else {
123
+ log(`${COLORS.yellow}Warning${COLORS.reset}: no \`test\` script found \u2014 template guardrail tests will not run`);
124
+ log(`Running: lint/format only`);
125
+ }
126
+ } else {
127
+ log(`Running: lint/format only`);
128
+ }
129
+ for (const scriptName of lightweightScripts) {
130
+ if (runScript(scriptName)) {
131
+ continue;
132
+ }
133
+ log("");
134
+ log(`${COLORS.red}Scoped verification failed${COLORS.reset} on: ${scriptName}`);
135
+ process.exitCode = 1;
136
+ return;
137
+ }
138
+ log("");
139
+ log(`${COLORS.green}Scoped verification passed${COLORS.reset} (${changeCategory.scope}-only changes)`);
140
+ process.exitCode = 0;
141
+ return;
142
+ }
143
+ }
144
+ const plan = createVerificationPlan(scripts);
103
145
  if (!plan.ok) {
104
146
  log(`${COLORS.red}Strict pre-push verification is not configured${COLORS.reset}`);
105
147
  log(plan.error);
@@ -111,7 +153,10 @@ async function runPrePush(options = {}) {
111
153
  process.exitCode = 1;
112
154
  return;
113
155
  }
114
- log(`Running strict verification for branch: ${COLORS.blue}${branch}${COLORS.reset}`);
156
+ log(`Running ${COLORS.blue}full${COLORS.reset} verification for branch: ${COLORS.blue}${branch}${COLORS.reset}`);
157
+ if (changeCategory.requiresFullSuite) {
158
+ log(`Change scope: ${COLORS.yellow}${changeCategory.scope}${COLORS.reset} (requires full suite)`);
159
+ }
115
160
  if (plan.source === "verify:push" || plan.source === "verify:ci") {
116
161
  log(`Using \`${plan.source}\` script.`);
117
162
  } else {
@@ -131,7 +176,6 @@ async function runPrePush(options = {}) {
131
176
  process.exitCode = 1;
132
177
  return;
133
178
  }
134
- const changedFiles = getChangedFilesForPush();
135
179
  if (hasPackageSourceChanges(changedFiles)) {
136
180
  try {
137
181
  await printTsdocSummary(log);
@@ -117,6 +117,91 @@ function getChangedFilesForPush() {
117
117
  source: "undetermined"
118
118
  };
119
119
  }
120
+ var SCOPE_PATTERNS = [
121
+ { pattern: /^\.github\//, scope: "ci", requiresFullSuite: true },
122
+ { pattern: /^turbo\.json$/, scope: "ci", requiresFullSuite: true },
123
+ {
124
+ pattern: /^packages\/[^/]+\/(README|CHANGELOG|CONTRIBUTING|MIGRATION)\.md$/i,
125
+ scope: "docs",
126
+ requiresFullSuite: false
127
+ },
128
+ { pattern: /^packages\/contracts\//, scope: "core", requiresFullSuite: true },
129
+ { pattern: /^packages\/types\//, scope: "core", requiresFullSuite: true },
130
+ {
131
+ pattern: /^packages\/tooling\//,
132
+ scope: "tooling",
133
+ requiresFullSuite: true
134
+ },
135
+ { pattern: /^apps\//, scope: "app", requiresFullSuite: true },
136
+ {
137
+ pattern: /^packages\/presets\/presets\/.*\.template$/,
138
+ scope: "template",
139
+ requiresFullSuite: false
140
+ },
141
+ {
142
+ pattern: /^packages\/presets\//,
143
+ scope: "runtime",
144
+ requiresFullSuite: true
145
+ },
146
+ {
147
+ pattern: /^packages\//,
148
+ scope: "runtime",
149
+ requiresFullSuite: true
150
+ },
151
+ {
152
+ pattern: /^plugins\/.*\.md$/i,
153
+ scope: "docs",
154
+ requiresFullSuite: false
155
+ },
156
+ {
157
+ pattern: /^plugins\/(?:.+\/)?plugin\.json$/i,
158
+ scope: "docs",
159
+ requiresFullSuite: false
160
+ },
161
+ {
162
+ pattern: /^plugins\//,
163
+ scope: "app",
164
+ requiresFullSuite: true
165
+ },
166
+ { pattern: /^docs\//, scope: "docs", requiresFullSuite: false },
167
+ { pattern: /\.md$/i, scope: "docs", requiresFullSuite: false },
168
+ {
169
+ pattern: /^\.(lefthook|oxlintrc|oxfmtrc|markdownlint)(\.|$)/,
170
+ scope: "config",
171
+ requiresFullSuite: false
172
+ },
173
+ { pattern: /^scripts\//, scope: "config", requiresFullSuite: false }
174
+ ];
175
+ var SCOPE_RANK = {
176
+ template: 2,
177
+ config: 1,
178
+ docs: 0
179
+ };
180
+ function categorizeChangedFiles(changedFiles) {
181
+ if (!changedFiles.deterministic) {
182
+ return { scope: "config", requiresFullSuite: true };
183
+ }
184
+ if (changedFiles.files.length === 0) {
185
+ return { scope: "config", requiresFullSuite: true };
186
+ }
187
+ let highestScope = null;
188
+ let highestRank = -1;
189
+ for (const file of changedFiles.files) {
190
+ const matched = SCOPE_PATTERNS.find((p) => p.pattern.test(file));
191
+ if (!matched) {
192
+ return { scope: "config", requiresFullSuite: true };
193
+ }
194
+ if (matched.requiresFullSuite) {
195
+ return { scope: matched.scope, requiresFullSuite: true };
196
+ }
197
+ const rank = SCOPE_RANK[matched.scope] ?? 0;
198
+ if (rank > highestRank) {
199
+ highestRank = rank;
200
+ highestScope = matched.scope;
201
+ }
202
+ }
203
+ return { scope: highestScope, requiresFullSuite: false };
204
+ }
120
205
  function hasRedPhaseBranchInContext(currentBranch) {
121
206
  let branches = [];
122
207
  try {
@@ -225,4 +310,4 @@ async function printTsdocSummary(log) {
225
310
  log(`${BLUE}TSDoc${RESET}: ${coverage.percentage}% coverage (${parts.join(", ")} of ${coverage.total} total)`);
226
311
  }
227
312
 
228
- export { getCurrentBranch, runGit, isRedPhaseBranch, isScaffoldBranch, isReleaseBranch, isTestOnlyPath, areFilesTestOnly, canBypassRedPhaseByChangedFiles, hasPackageSourceChanges, getChangedFilesForPush, hasRedPhaseBranchInContext, createVerificationPlan, readPackageScripts, checkBunVersion, printTsdocSummary };
313
+ export { getCurrentBranch, runGit, isRedPhaseBranch, isScaffoldBranch, isReleaseBranch, isTestOnlyPath, areFilesTestOnly, canBypassRedPhaseByChangedFiles, hasPackageSourceChanges, getChangedFilesForPush, categorizeChangedFiles, hasRedPhaseBranchInContext, createVerificationPlan, readPackageScripts, checkBunVersion, printTsdocSummary };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@outfitter/tooling",
3
3
  "description": "Dev tooling configuration presets for Outfitter projects (oxlint/oxfmt, typescript, lefthook, markdownlint)",
4
- "version": "0.3.5",
4
+ "version": "0.3.6-canary-20260317163745",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -96,7 +96,7 @@
96
96
  "zod": "^4.3.5"
97
97
  },
98
98
  "devDependencies": {
99
- "@outfitter/presets": "0.3.0",
99
+ "@outfitter/presets": "0.3.1-canary-20260317163745",
100
100
  "@types/bun": "^1.3.9",
101
101
  "yaml": "^2.8.2"
102
102
  },
@@ -46,7 +46,7 @@
46
46
  }
47
47
  ],
48
48
  "devDependencies": {
49
- "@outfitter/tooling": "^0.3.5",
49
+ "@outfitter/tooling": "^0.3.6-canary-20260317163745",
50
50
  "lefthook": "^2.1.1",
51
51
  "ultracite": "7.2.3"
52
52
  }