@ontrails/regrade 1.0.0-beta.30 → 1.0.0-beta.39

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 CHANGED
@@ -1,5 +1,104 @@
1
1
  # @ontrails/regrade
2
2
 
3
+ ## 1.0.0-beta.39
4
+
5
+ ### Minor Changes
6
+
7
+ - [`65b362f`](https://github.com/outfitter-dev/trails/commit/65b362f65fa2a75e2121d1a8b31882d52fa0376b): Regrade history consolidates per transition, append-only: `regrade apply` appends a run entry stamped `{ planContentHash, lockHashAtRun }` to `.trails/regrade/history/<transition>.json` instead of overwriting lockhash-named files, identical re-runs are recognized as replays, the artifact carries a stable internal transition `id`, and `regrade check <transition>` verifies each recorded run at its own stamped lock. The report `history.status` union widens to `applied | checked | replay`.
8
+ - [`d3f7a25`](https://github.com/outfitter-dev/trails/commit/d3f7a25e66b6f0149ab2f33ac31ae1458860dce3): Regrade review entries carry structured review detail through Warden-backed and AST-backed classes: matched form, candidate replacement, machine-readable signals, preserve cautions, and a `judgment` field that distinguishes unresolved occurrence judgment from completed preserve/rewrite verdicts. Safe rewrite entries keep their existing report shape.
9
+
10
+ ### Patch Changes
11
+
12
+ - [`b077fb7`](https://github.com/outfitter-dev/trails/commit/b077fb7ba6d9724cac6f0e59bc3fec9aec28984c): Add the export-restructure Regrade class family (TRL-1210). `export-restructure:cli-aliases` inverts legacy `cliAliases`/`trailsCliAliases` exports into `surfaceOverlay({ cli })` bindings inside the module's `trailsOverlays` export — adding the `@ontrails/core` import, deleting the legacy export, and routing anything it cannot prove safe (computed keys, spreads, in-module `aliases:` references) to `needs-review` with the exact target shape named. `export-restructure:mcp-trailheads` projects call-site MCP trailhead maps into `surfaceOverlay({ mcp })` group bindings: it rewrites in place when the same module exports `trailsOverlays`, and otherwise emits a classified `needs-review` handoff naming the module-overlay target while the call-site map stays as the richer-metadata override-in-context. Warden's fix-class union grows to `'export-restructure' | 'term-rewrite'`, `no-legacy-cli-alias-export` now advertises the `export-restructure` class, and `loadWardenRegradeClasses` supersedes `loadWardenTermRewriteClasses` (still exported) as the full Warden-routed class loader. Class-mode Regrade also gains the full plan lifecycle: `trails regrade plan --type class --class-ids ...` writes a `.trails/regrade/<slug>.json` plan carrying class ids, scope, and intent, `regrade check` re-runs the dry run and gates on outstanding rewrites or review, and `regrade apply` applies and graduates the plan to `.trails/regrade/history/<slug>-<hash>.json` — the same plan → check → apply → history evidence trail vocabulary regrades already had, now available to structural transforms. The class family ships for downstream apps bridging the pre-1.0 gap, so pre-cutover alias exports and trailhead maps migrate mechanically instead of by hand.
13
+
14
+ ## 1.0.0-beta.38
15
+
16
+ ## 1.0.0-beta.37
17
+
18
+ ### Patch Changes
19
+
20
+ - [`09f15de`](https://github.com/outfitter-dev/trails/commit/09f15def8fcc8c28b0d604f436e6eeed46da8f37): Stage wide-net Regrade expansion candidates as structured plan review inventory with evidence, status, and pending counts.
21
+
22
+ ## 1.0.0-beta.36
23
+
24
+ ### Patch Changes
25
+
26
+ - [`6e63e48`](https://github.com/outfitter-dev/trails/commit/6e63e483617b84cb6868d0c4d58d5b5a8d3b9ed2): Complete the v1 grouped surface-entry vocabulary cutover from facet to trailhead, including Regrade dogfood support for governed string literal renames and composed AST rewrite application.
27
+ - [`26786a1`](https://github.com/outfitter-dev/trails/commit/26786a14acbe9ed03f69adbdac22968891e33df1): Persist vocabulary Regrade plan artifacts, expose plan/check/preview/apply flows across CLI and MCP, and write applied plan history for reviewed vocabulary migrations.
28
+
29
+ ## 1.0.0-beta.35
30
+
31
+ ### Patch Changes
32
+
33
+ - [`3842160`](https://github.com/outfitter-dev/trails/commit/3842160ac4030807cb2dfca8e1da75e03febccf2): Teach vocabulary regrades to discover simple prose morphology as deferred
34
+ review inventory so authored plans surface forms like `blazed` and `blazing`.
35
+
36
+ ## 1.0.0-beta.34
37
+
38
+ ### Patch Changes
39
+
40
+ - [`d67558b`](https://github.com/outfitter-dev/trails/commit/d67558bea3bfa363ed57e0f4091b6eccbf2a7710): Run governed AST symbol renames from the registry-backed `trails regrade` command path, including MCP parity, while preserving derived live API forms.
41
+
42
+ ## 1.0.0-beta.33
43
+
44
+ ### Patch Changes
45
+
46
+ - [`0c40138`](https://github.com/outfitter-dev/trails/commit/0c40138efb962e779710daa172bdfd756d9d992f): Harden vocabulary regrades by deferring Markdown code contexts for review and
47
+ exposing structured preserve rules through the Trails `regrade` command.
48
+ - [`7d65189`](https://github.com/outfitter-dev/trails/commit/7d65189cc33408755fe07a0b5679f1ed01123455): Expose derived live-API preserve inventory in vocabulary Regrade runs and have the Trails operator regrade surface derive current facet API preserves from live topo and MCP surface facts.
49
+ - [`fc002d5`](https://github.com/outfitter-dev/trails/commit/fc002d5669f4303427e99f45f9998fd0b0172bdb): Add governed AST identifier rename helpers and Warden residue detection for
50
+ active vocabulary symbol transitions.
51
+ - [`6ca0d8f`](https://github.com/outfitter-dev/trails/commit/6ca0d8f776801eee71ddd86cb88c198eaf5815fd): Add a typed governed-vocabulary transition registry that Warden owns and Regrade
52
+ can consume for migration planning.
53
+ - [`04bb8a4`](https://github.com/outfitter-dev/trails/commit/04bb8a42af4ada51a74b1d8c83697db92035b5e9): Expose vocabulary Regrade occurrence dispositions and disposition summary counts alongside mechanical verdicts.
54
+
55
+ Accept explicit preserve-rule dispositions through the `trails regrade` CLI/MCP contract.
56
+
57
+ ## 1.0.0-beta.32
58
+
59
+ ### Patch Changes
60
+
61
+ - f3c4fef: Export a shared `escapeRegExp` helper from core and migrate first-party callers off local copies.
62
+ - fe72b84: Fold remaining Regrade and Warden scan-target surfaces onto the shared path-scope vocabulary.
63
+ - Updated dependencies [3e5c0fc]
64
+ - Updated dependencies [f3c4fef]
65
+ - Updated dependencies [cb0a9d8]
66
+ - Updated dependencies [21c6dda]
67
+ - Updated dependencies [fe72b84]
68
+ - @ontrails/core@1.0.0-beta.32
69
+ - @ontrails/warden@1.0.0-beta.32
70
+
71
+ ## 1.0.0-beta.31
72
+
73
+ ### Patch Changes
74
+
75
+ - e2f3d23: Default Regrade reports to actionable entries, add skip counts grouped by
76
+ reason, and expose an `includeEntries` option for full report inventories.
77
+ - 9be2b7e: Load project-local Warden term-rewrite rules from the Regrade root so repo-owned
78
+ migration classes can run through `trails regrade`.
79
+ - 47f782c: Add occurrence-level vocabulary regrade reports with plan, ledger,
80
+ and completion-gate facts. The Trails `regrade` operator command now supports
81
+ positional `<from> <to>` regrade runs and exposes the same capability through
82
+ the curated MCP surface.
83
+ - ee9f3ae: Let Warden fix capabilities declare downstream scan targets and have Regrade
84
+ honor those targets for Warden-backed term-rewrite classes.
85
+
86
+ Dogfood the first safe facet-to-trailhead prose rewrite through project-local
87
+ Warden rules and Regrade.
88
+
89
+ - 982a4d7: Add Regrade path-scope exclusion globs for vocabulary runs and expose them
90
+ through the `trails regrade` CLI/MCP contract.
91
+ - 1540233: Add Regrade scan inventory summaries that group matched files by extension and
92
+ top-level path, with occurrence counts for vocabulary regrade reports.
93
+ - a079073: Rename Regrade path-scope scan controls from `ignore` to `exclude` across CLI, MCP, and project config.
94
+ - Updated dependencies [ee9f3ae]
95
+ - Updated dependencies [a0126d9]
96
+ - Updated dependencies [4cd5d4e]
97
+ - Updated dependencies [6a26a08]
98
+ - Updated dependencies [38907cc]
99
+ - @ontrails/warden@1.0.0-beta.31
100
+ - @ontrails/core@1.0.0-beta.31
101
+
3
102
  ## 1.0.0-beta.30
4
103
 
5
104
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/regrade",
3
- "version": "1.0.0-beta.30",
3
+ "version": "1.0.0-beta.39",
4
4
  "description": "Downstream migration reporting and safe rewrite helpers for Trails.",
5
5
  "files": [
6
6
  "src/**/*.ts",
@@ -22,12 +22,12 @@
22
22
  "clean": "rm -rf dist *.tsbuildinfo"
23
23
  },
24
24
  "dependencies": {
25
- "@ontrails/core": "^1.0.0-beta.30",
26
- "@ontrails/warden": "^1.0.0-beta.30",
25
+ "@ontrails/core": "^1.0.0-beta.39",
26
+ "@ontrails/warden": "^1.0.0-beta.39",
27
27
  "zod": "^4.3.5"
28
28
  },
29
29
  "devDependencies": {
30
- "@ontrails/testing": "^1.0.0-beta.30",
31
- "@ontrails/topographer": "^1.0.0-beta.30"
30
+ "@ontrails/testing": "^1.0.0-beta.39",
31
+ "@ontrails/topographer": "^1.0.0-beta.39"
32
32
  }
33
33
  }
@@ -6,12 +6,15 @@ import type {
6
6
  import {
7
7
  applySourceEdits,
8
8
  createSourceEdit,
9
+ getStringValue,
9
10
  identifierName,
11
+ isStringLiteral,
10
12
  offsetToLineColumn,
11
13
  parseWithDiagnostics,
12
14
  validateSourceEdits,
13
15
  walkWithScopeContext,
14
16
  } from '@ontrails/warden/ast';
17
+ import type { GovernedVocabularyTransition } from '@ontrails/warden';
15
18
 
16
19
  import type {
17
20
  RegradeClass,
@@ -231,12 +234,45 @@ export interface AstIdentifierRenameClassOptions {
231
234
  readonly from: string;
232
235
  readonly id?: string;
233
236
  readonly reviewDeclarationTypes?: ReadonlySet<string>;
237
+ readonly shouldPreserve?: (
238
+ occurrence: AstIdentifierRenameOccurrence
239
+ ) => boolean;
240
+ readonly to: string;
241
+ }
242
+
243
+ export interface AstIdentifierRenameOccurrence {
244
+ readonly end: number;
245
+ readonly from: string;
246
+ readonly path: string;
247
+ readonly source: string;
248
+ readonly start: number;
249
+ readonly to: string;
250
+ }
251
+
252
+ export interface AstStringLiteralRenameClassOptions {
253
+ readonly describe?: string;
254
+ readonly from: string;
255
+ readonly id?: string;
256
+ readonly shouldPreserve?: (
257
+ occurrence: AstIdentifierRenameOccurrence
258
+ ) => boolean;
234
259
  readonly to: string;
235
260
  }
236
261
 
237
262
  const isIdentifierNamed = (node: AstNode, name: string): boolean =>
238
263
  node.type === 'Identifier' && identifierName(node) === name;
239
264
 
265
+ const identifierTokenSpan = (
266
+ node: AstNode,
267
+ source: string,
268
+ name: string
269
+ ): { readonly end: number; readonly start: number } | null => {
270
+ const end = node.start + name.length;
271
+ return source.slice(node.start, end) === name
272
+ ? { end, start: node.start }
273
+ : null;
274
+ };
275
+
240
276
  export const createAstIdentifierRenameClass = (
241
277
  options: AstIdentifierRenameClassOptions
242
278
  ): RegradeClass => {
@@ -253,14 +289,20 @@ export const createAstIdentifierRenameClass = (
253
289
  return null;
254
290
  }
255
291
 
256
- const declaration = context.getDeclaration(options.from);
257
- if (declaration && reviewDeclarationTypes.has(declaration.type)) {
292
+ const span = identifierTokenSpan(node, context.source, options.from);
293
+ if (span === null) {
258
294
  const location = offsetToLineColumn(context.source, node.start);
295
+ const caution = `Identifier "${options.from}" token span could not be verified; routed to review.`;
259
296
  return {
260
297
  detail: {
298
+ candidateReplacement: options.to,
261
299
  expectedTarget: `Rename identifier "${options.from}" to "${options.to}".`,
300
+ judgment: 'unresolved',
301
+ matchedForm: options.from,
262
302
  nodeKind: node.type,
263
- reason: 'ast-identifier-review-declaration',
303
+ preserveCautions: [caution],
304
+ reason: 'ast-identifier-token-span-unverified',
305
+ signals: ['ast:identifier-rename'],
264
306
  span: {
265
307
  column: location.column,
266
308
  end: node.end,
@@ -271,16 +313,169 @@ export const createAstIdentifierRenameClass = (
271
313
  symbol: options.from,
272
314
  },
273
315
  kind: 'review',
274
- note: `Identifier "${options.from}" resolves to ${declaration.type}; routed to review.`,
316
+ note: caution,
317
+ reason: 'ast-identifier-token-span-unverified',
318
+ };
319
+ }
320
+
321
+ if (
322
+ options.shouldPreserve?.({
323
+ end: span.end,
324
+ from: options.from,
325
+ path: context.path,
326
+ source: context.source,
327
+ start: span.start,
328
+ to: options.to,
329
+ }) === true
330
+ ) {
331
+ return null;
332
+ }
333
+
334
+ const declaration = context.getDeclaration(options.from);
335
+ if (declaration && reviewDeclarationTypes.has(declaration.type)) {
336
+ const location = offsetToLineColumn(context.source, span.start);
337
+ const caution = `Identifier "${options.from}" resolves to ${declaration.type}; routed to review.`;
338
+ return {
339
+ detail: {
340
+ candidateReplacement: options.to,
341
+ expectedTarget: `Rename identifier "${options.from}" to "${options.to}".`,
342
+ judgment: 'unresolved',
343
+ matchedForm: options.from,
344
+ nodeKind: node.type,
345
+ preserveCautions: [caution],
346
+ reason: 'ast-identifier-review-declaration',
347
+ signals: ['ast:identifier-rename'],
348
+ span: {
349
+ column: location.column,
350
+ end: span.end,
351
+ line: location.line,
352
+ start: span.start,
353
+ },
354
+ suggestedValidation: 'bun run typecheck',
355
+ symbol: options.from,
356
+ },
357
+ kind: 'review',
358
+ note: caution,
275
359
  reason: 'ast-identifier-review-declaration',
276
360
  };
277
361
  }
278
362
 
279
363
  return {
280
- edit: createSourceEdit(node.start, node.end, options.to),
364
+ edit: createSourceEdit(span.start, span.end, options.to),
281
365
  kind: 'edit',
282
366
  note: `Renamed identifier "${options.from}" to "${options.to}".`,
283
367
  };
284
368
  },
285
369
  });
286
370
  };
371
+
372
+ const stringLiteralValueSpan = (
373
+ node: AstNode,
374
+ source: string,
375
+ value: string
376
+ ): { readonly end: number; readonly start: number } | null => {
377
+ const raw = source.slice(node.start, node.end);
378
+ const relativeStart = raw.indexOf(value);
379
+ if (relativeStart === -1 || raw.includes(value, relativeStart + 1)) {
380
+ return null;
381
+ }
382
+ const start = node.start + relativeStart;
383
+ return { end: start + value.length, start };
384
+ };
385
+
386
+ export const createAstStringLiteralRenameClass = (
387
+ options: AstStringLiteralRenameClassOptions
388
+ ): RegradeClass =>
389
+ createAstRewriteClass({
390
+ describe:
391
+ options.describe ??
392
+ `Rename string literal "${options.from}" to "${options.to}".`,
393
+ id:
394
+ options.id ?? `ast-string-literal-rename:${options.from}->${options.to}`,
395
+ visit: (node, context) => {
396
+ if (!isStringLiteral(node) || getStringValue(node) !== options.from) {
397
+ return null;
398
+ }
399
+
400
+ const span = stringLiteralValueSpan(node, context.source, options.from);
401
+ if (span === null) {
402
+ const location = offsetToLineColumn(context.source, node.start);
403
+ const caution = `String literal "${options.from}" token span could not be verified; routed to review.`;
404
+ return {
405
+ detail: {
406
+ candidateReplacement: options.to,
407
+ expectedTarget: `Rename string literal "${options.from}" to "${options.to}".`,
408
+ judgment: 'unresolved',
409
+ matchedForm: options.from,
410
+ nodeKind: node.type,
411
+ preserveCautions: [caution],
412
+ reason: 'ast-string-literal-token-span-unverified',
413
+ signals: ['ast:string-literal-rename'],
414
+ span: {
415
+ column: location.column,
416
+ end: node.end,
417
+ line: location.line,
418
+ start: node.start,
419
+ },
420
+ suggestedValidation: 'bun run typecheck',
421
+ symbol: options.from,
422
+ },
423
+ kind: 'review',
424
+ note: caution,
425
+ reason: 'ast-string-literal-token-span-unverified',
426
+ };
427
+ }
428
+
429
+ if (
430
+ options.shouldPreserve?.({
431
+ end: span.end,
432
+ from: options.from,
433
+ path: context.path,
434
+ source: context.source,
435
+ start: span.start,
436
+ to: options.to,
437
+ }) === true
438
+ ) {
439
+ return null;
440
+ }
441
+
442
+ return {
443
+ edit: createSourceEdit(span.start, span.end, options.to),
444
+ kind: 'edit',
445
+ note: `Renamed string literal "${options.from}" to "${options.to}".`,
446
+ };
447
+ },
448
+ });
449
+
450
+ export const createGovernedAstIdentifierRenameClasses = (
451
+ transition: GovernedVocabularyTransition,
452
+ options: {
453
+ readonly shouldPreserve?: (
454
+ occurrence: AstIdentifierRenameOccurrence
455
+ ) => boolean;
456
+ } = {}
457
+ ): readonly RegradeClass[] => [
458
+ ...transition.symbolRenames.map((rename) =>
459
+ createAstIdentifierRenameClass({
460
+ describe: `Rename governed symbol "${rename.from}" to "${rename.to}" for ${transition.id}.`,
461
+ from: rename.from,
462
+ id: `ast-symbol-rename:${transition.id}:${rename.from}->${rename.to}`,
463
+ reviewDeclarationTypes: new Set(rename.reviewDeclarationTypes),
464
+ ...(options.shouldPreserve === undefined
465
+ ? {}
466
+ : { shouldPreserve: options.shouldPreserve }),
467
+ to: rename.to,
468
+ })
469
+ ),
470
+ ...transition.stringLiteralRenames.map((rename) =>
471
+ createAstStringLiteralRenameClass({
472
+ describe: `Rename governed string literal "${rename.from}" to "${rename.to}" for ${transition.id}.`,
473
+ from: rename.from,
474
+ id: `ast-string-literal-rename:${transition.id}:${rename.from}->${rename.to}`,
475
+ ...(options.shouldPreserve === undefined
476
+ ? {}
477
+ : { shouldPreserve: options.shouldPreserve }),
478
+ to: rename.to,
479
+ })
480
+ ),
481
+ ];
@@ -1,4 +1,9 @@
1
- import { NotFoundError, Result, trail } from '@ontrails/core';
1
+ import {
2
+ NotFoundError,
3
+ Result,
4
+ matchesAnyPathGlob,
5
+ trail,
6
+ } from '@ontrails/core';
2
7
  import { readdirSync } from 'node:fs';
3
8
  import { join, posix, relative, resolve, sep } from 'node:path';
4
9
  import { z } from 'zod';
@@ -66,6 +71,10 @@ export interface DownstreamCollectionOptions {
66
71
  readonly ignoredDirectories?: readonly string[];
67
72
  /** Source extensions to collect. Defaults to {@link DEFAULT_SOURCE_EXTENSIONS}. */
68
73
  readonly extensions?: readonly string[];
74
+ /** Root-relative path globs to skip before collection. */
75
+ readonly exclude?: readonly string[];
76
+ /** Root-relative path globs to collect. Omit to collect all matching files. */
77
+ readonly include?: readonly string[];
69
78
  }
70
79
 
71
80
  /** Outcome of classifying a single directory entry. */
@@ -79,6 +88,16 @@ const extensionOf = (name: string): string => {
79
88
  return dot <= 0 ? '' : name.slice(dot);
80
89
  };
81
90
 
91
+ const normalizeExtension = (extension: string): string =>
92
+ extension === '' || extension.startsWith('.') ? extension : `.${extension}`;
93
+
94
+ const collectionExtensions = (
95
+ extensions: readonly string[] | undefined
96
+ ): readonly string[] =>
97
+ extensions === undefined
98
+ ? DEFAULT_SOURCE_EXTENSIONS
99
+ : extensions.map(normalizeExtension);
100
+
82
101
  /**
83
102
  * Decide what to do with a single directory entry. Pure: no filesystem access,
84
103
  * so collection policy can be tested directly with synthetic entries.
@@ -90,7 +109,7 @@ export const classifyDownstreamEntry = (
90
109
  ): DownstreamEntryClassification => {
91
110
  const ignoredDirectories =
92
111
  options.ignoredDirectories ?? DEFAULT_IGNORED_DIRECTORIES;
93
- const extensions = options.extensions ?? DEFAULT_SOURCE_EXTENSIONS;
112
+ const extensions = collectionExtensions(options.extensions);
94
113
 
95
114
  if (kind === 'directory') {
96
115
  return ignoredDirectories.includes(name)
@@ -100,7 +119,7 @@ export const classifyDownstreamEntry = (
100
119
  if (kind === 'other') {
101
120
  return { action: 'skip', reason: 'unsupported-entry' };
102
121
  }
103
- return extensions.includes(extensionOf(name))
122
+ return extensions.length === 0 || extensions.includes(extensionOf(name))
104
123
  ? { action: 'collect' }
105
124
  : { action: 'skip', reason: 'unsupported-extension' };
106
125
  };
@@ -177,13 +196,25 @@ export const collectDownstreamSources = (
177
196
  for (const entry of read.entries) {
178
197
  const absolutePath = join(current, entry.name);
179
198
  const path = toPosixRelative(absoluteRoot, absolutePath);
199
+ if (matchesAnyPathGlob(path, options.exclude)) {
200
+ skipped.push({ path, reason: 'ignored-glob' });
201
+ continue;
202
+ }
180
203
  const classification = classifyDownstreamEntry(
181
204
  entry.name,
182
205
  entry.kind,
183
206
  options
184
207
  );
185
208
  if (classification.action === 'collect') {
186
- files.push({ absolutePath, path });
209
+ if (
210
+ options.include !== undefined &&
211
+ options.include.length > 0 &&
212
+ !matchesAnyPathGlob(path, options.include)
213
+ ) {
214
+ skipped.push({ path, reason: 'not-included-glob' });
215
+ } else {
216
+ files.push({ absolutePath, path });
217
+ }
187
218
  } else if (classification.action === 'recurse') {
188
219
  queue.push(absolutePath);
189
220
  } else {
@@ -198,6 +229,10 @@ export const collectDownstreamSources = (
198
229
  };
199
230
 
200
231
  export const collectDownstreamSourcesInput = z.object({
232
+ exclude: z
233
+ .array(z.string())
234
+ .optional()
235
+ .describe('Root-relative path globs to skip before collection'),
201
236
  extensions: z
202
237
  .array(z.string())
203
238
  .optional()
@@ -206,6 +241,10 @@ export const collectDownstreamSourcesInput = z.object({
206
241
  .array(z.string())
207
242
  .optional()
208
243
  .describe('Directory names to skip during collection'),
244
+ include: z
245
+ .array(z.string())
246
+ .optional()
247
+ .describe('Root-relative path globs to collect'),
209
248
  root: z
210
249
  .string()
211
250
  .describe('Absolute path to the downstream repo root to scan'),
@@ -247,6 +286,8 @@ export const collectDownstreamSourcesTrail = trail(
247
286
  ...(input.extensions === undefined
248
287
  ? {}
249
288
  : { extensions: input.extensions }),
289
+ ...(input.exclude === undefined ? {} : { exclude: input.exclude }),
290
+ ...(input.include === undefined ? {} : { include: input.include }),
250
291
  ...(input.ignoredDirectories === undefined
251
292
  ? {}
252
293
  : { ignoredDirectories: input.ignoredDirectories }),