@@ -7,7 +7,14 @@ import { pathExists } from "./fs.js";
77import { runCommandArgs } from "./exec.js" ;
88import { mapFeatureSeeds , MapResult } from "./mapper.js" ;
99import { FeatureSeed , SeedFileRef , SeedTestRef } from "./mappers/types.js" ;
10- import { isSafeFile , normalize , shouldSkip , walk } from "./mappers/shared.js" ;
10+ import {
11+ applyPathFilters ,
12+ isSafeFile ,
13+ normalize ,
14+ PathFilters ,
15+ shouldSkip ,
16+ walk ,
17+ } from "./mappers/shared.js" ;
1118
1219type AgentMapMode = "heuristic" | "auto" | "agent" ;
1320
@@ -26,15 +33,10 @@ type AgentMapOptions = {
2633 source : AgentMapMode ;
2734 provider : Provider | null ;
2835 providerOptions : ProviderOptions ;
29- inventory ?: InventoryFilters ;
36+ inventory ?: PathFilters ;
3037 onProgress ?: ( event : string , fields : Record < string , string | number | boolean > ) => void ;
3138} ;
3239
33- type InventoryFilters = {
34- include : string [ ] ;
35- exclude : string [ ] ;
36- } ;
37-
3840type RepoInventorySummary = {
3941 files : number ;
4042 sourceFiles : number ;
@@ -148,6 +150,7 @@ export async function mapWithSource(
148150 options . provider ,
149151 options . providerOptions ,
150152 inventory ,
153+ options . inventory ,
151154 ) ;
152155 options . onProgress ?.( "agent-done" , {
153156 features : agent . features . length ,
@@ -199,6 +202,7 @@ async function agentMap(
199202 provider : Provider ,
200203 providerOptions : ProviderOptions ,
201204 inventory : RepoInventory ,
205+ filters : PathFilters | undefined ,
202206) : Promise < MapResult > {
203207 const prompt = buildAgentMapPrompt ( project , {
204208 manifests : inventory . manifests ,
@@ -212,12 +216,10 @@ async function agentMap(
212216 const seeds = await Promise . all (
213217 output . features . map ( ( feature ) => toSeed ( root , feature , inventory . allFiles ) ) ,
214218 ) ;
215- return mapFeatureSeeds (
216- root ,
217- project ,
218- existing ,
219- uniqueSeeds ( seeds . filter ( ( seed ) : seed is FeatureSeed => seed !== null ) ) ,
220- ) ;
219+ const mappedSeeds = uniqueSeeds ( seeds . filter ( ( seed ) : seed is FeatureSeed => seed !== null ) ) ;
220+ return filters === undefined
221+ ? mapFeatureSeeds ( root , project , existing , mappedSeeds )
222+ : mapFeatureSeeds ( root , project , existing , mappedSeeds , { filters } ) ;
221223}
222224
223225async function toSeed (
@@ -385,10 +387,10 @@ async function repoInventory(
385387 root : string ,
386388 project : ProjectRecord ,
387389 features : FeatureRecord [ ] ,
388- filters : InventoryFilters | undefined ,
390+ filters : PathFilters | undefined ,
389391) : Promise < RepoInventory > {
390392 const skipPath = await inventorySkipPath ( root , project , features ) ;
391- const files = applyInventoryFilters (
393+ const files = applyPathFilters (
392394 ( ( await gitInventoryFiles ( root ) ) ?? ( await walk ( root , [ "" ] , skipPath ) ) ) . filter (
393395 ( path ) => ! skipPath ( path ) ,
394396 ) ,
@@ -443,73 +445,12 @@ async function gitInventoryFiles(root: string): Promise<string[] | null> {
443445 return existing . filter ( ( path ) : path is string => path !== null ) ;
444446}
445447
446- function applyInventoryFilters ( files : string [ ] , filters : InventoryFilters | undefined ) : string [ ] {
447- if ( filters === undefined ) {
448- return files ;
449- }
450- return files . filter (
451- ( file ) =>
452- filters . include . some ( ( pattern ) => inventoryPatternMatches ( pattern , file ) ) &&
453- ! filters . exclude . some ( ( pattern ) => inventoryPatternMatches ( pattern , file ) ) ,
454- ) ;
455- }
456-
457448function isInventoryPath ( path : string ) : boolean {
458449 return (
459450 path . length > 0 && ! isAbsolute ( path ) && ! path . includes ( "\0" ) && ! path . split ( "/" ) . includes ( ".." )
460451 ) ;
461452}
462453
463- function inventoryPatternMatches ( pattern : string , path : string ) : boolean {
464- const normalized = pattern . replace ( / \\ / gu, "/" ) . replace ( / ^ \. \/ / u, "" ) ;
465- if ( normalized === "**" || normalized === "**/*" ) {
466- return true ;
467- }
468- if ( normalized . length === 0 ) {
469- return false ;
470- }
471- if ( ! / [ ? * ] / u. test ( normalized ) ) {
472- return path === normalized || path . startsWith ( `${ normalized } /` ) ;
473- }
474- if ( normalized . endsWith ( "/**" ) ) {
475- const prefix = normalized . slice ( 0 , - 3 ) ;
476- if ( / [ ? * ] / u. test ( prefix ) ) {
477- return new RegExp ( `^${ globPatternRegExp ( prefix ) } (?:/.*)?$` , "u" ) . test ( path ) ;
478- }
479- return prefix . length === 0 || path === prefix || path . startsWith ( `${ prefix } /` ) ;
480- }
481- return new RegExp ( `^${ globPatternRegExp ( normalized ) } $` , "u" ) . test ( path ) ;
482- }
483-
484- function globPatternRegExp ( pattern : string ) : string {
485- let source = "" ;
486- for ( let index = 0 ; index < pattern . length ; index += 1 ) {
487- const char = pattern [ index ] ;
488- if ( char === "*" ) {
489- if ( pattern [ index + 1 ] === "*" ) {
490- if ( pattern [ index + 2 ] === "/" ) {
491- source += "(?:.*/)?" ;
492- index += 2 ;
493- } else {
494- source += ".*" ;
495- index += 1 ;
496- }
497- } else {
498- source += "[^/]*" ;
499- }
500- } else if ( char === "?" ) {
501- source += "[^/]" ;
502- } else {
503- source += regexpEscape ( char ?? "" ) ;
504- }
505- }
506- return source ;
507- }
508-
509- function regexpEscape ( value : string ) : string {
510- return value . replace ( / [ \\ ^ $ . * + ? ( ) [ \] { } | ] / gu, "\\$&" ) ;
511- }
512-
513454async function inventorySkipPath (
514455 root : string ,
515456 project : ProjectRecord ,
0 commit comments