@principal-ai/codebase-composition 0.1.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 (129) hide show
  1. package/README.md +67 -0
  2. package/dist/index.d.ts +9 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +23 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/modules/DependencyLayerModule.d.ts +44 -0
  7. package/dist/modules/DependencyLayerModule.d.ts.map +1 -0
  8. package/dist/modules/DependencyLayerModule.js +286 -0
  9. package/dist/modules/DependencyLayerModule.js.map +1 -0
  10. package/dist/modules/FileSystemModule.d.ts +30 -0
  11. package/dist/modules/FileSystemModule.d.ts.map +1 -0
  12. package/dist/modules/FileSystemModule.js +46 -0
  13. package/dist/modules/FileSystemModule.js.map +1 -0
  14. package/dist/modules/FileTypeLayerModule.d.ts +34 -0
  15. package/dist/modules/FileTypeLayerModule.d.ts.map +1 -0
  16. package/dist/modules/FileTypeLayerModule.js +169 -0
  17. package/dist/modules/FileTypeLayerModule.js.map +1 -0
  18. package/dist/modules/FrameworkLayerModule.d.ts +22 -0
  19. package/dist/modules/FrameworkLayerModule.d.ts.map +1 -0
  20. package/dist/modules/FrameworkLayerModule.js +388 -0
  21. package/dist/modules/FrameworkLayerModule.js.map +1 -0
  22. package/dist/modules/PackageLayerModule.d.ts +23 -0
  23. package/dist/modules/PackageLayerModule.d.ts.map +1 -0
  24. package/dist/modules/PackageLayerModule.js +810 -0
  25. package/dist/modules/PackageLayerModule.js.map +1 -0
  26. package/dist/modules/TypeExtractionModule.d.ts +37 -0
  27. package/dist/modules/TypeExtractionModule.d.ts.map +1 -0
  28. package/dist/modules/TypeExtractionModule.js +180 -0
  29. package/dist/modules/TypeExtractionModule.js.map +1 -0
  30. package/dist/modules/VersionControlLayerModule.d.ts +10 -0
  31. package/dist/modules/VersionControlLayerModule.d.ts.map +1 -0
  32. package/dist/modules/VersionControlLayerModule.js +32 -0
  33. package/dist/modules/VersionControlLayerModule.js.map +1 -0
  34. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts +4 -0
  35. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts.map +1 -0
  36. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js +7 -0
  37. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js.map +1 -0
  38. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts +15 -0
  39. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts.map +1 -0
  40. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js +3 -0
  41. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js.map +1 -0
  42. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts +34 -0
  43. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts.map +1 -0
  44. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js +23 -0
  45. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js.map +1 -0
  46. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts +39 -0
  47. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts.map +1 -0
  48. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js +39 -0
  49. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js.map +1 -0
  50. package/dist/modules/extractors/TypeScriptExtractor.d.ts +18 -0
  51. package/dist/modules/extractors/TypeScriptExtractor.d.ts.map +1 -0
  52. package/dist/modules/extractors/TypeScriptExtractor.js +361 -0
  53. package/dist/modules/extractors/TypeScriptExtractor.js.map +1 -0
  54. package/dist/modules/index.d.ts +13 -0
  55. package/dist/modules/index.d.ts.map +1 -0
  56. package/dist/modules/index.js +21 -0
  57. package/dist/modules/index.js.map +1 -0
  58. package/dist/providers/GitVersionControlProvider.d.ts +108 -0
  59. package/dist/providers/GitVersionControlProvider.d.ts.map +1 -0
  60. package/dist/providers/GitVersionControlProvider.js +380 -0
  61. package/dist/providers/GitVersionControlProvider.js.map +1 -0
  62. package/dist/providers/PackageManagerApiProvider.d.ts +78 -0
  63. package/dist/providers/PackageManagerApiProvider.d.ts.map +1 -0
  64. package/dist/providers/PackageManagerApiProvider.js +14 -0
  65. package/dist/providers/PackageManagerApiProvider.js.map +1 -0
  66. package/dist/providers/index.d.ts +4 -0
  67. package/dist/providers/index.d.ts.map +1 -0
  68. package/dist/providers/index.js +10 -0
  69. package/dist/providers/index.js.map +1 -0
  70. package/dist/services/FilesystemService.d.ts +59 -0
  71. package/dist/services/FilesystemService.d.ts.map +1 -0
  72. package/dist/services/FilesystemService.js +391 -0
  73. package/dist/services/FilesystemService.js.map +1 -0
  74. package/dist/services/index.d.ts +2 -0
  75. package/dist/services/index.d.ts.map +1 -0
  76. package/dist/services/index.js +7 -0
  77. package/dist/services/index.js.map +1 -0
  78. package/dist/types/file-system.d.ts +7 -0
  79. package/dist/types/file-system.d.ts.map +1 -0
  80. package/dist/types/file-system.js +7 -0
  81. package/dist/types/file-system.js.map +1 -0
  82. package/dist/types/index.d.ts +4 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +3 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/dist/types/layer-types.d.ts +187 -0
  87. package/dist/types/layer-types.d.ts.map +1 -0
  88. package/dist/types/layer-types.js +7 -0
  89. package/dist/types/layer-types.js.map +1 -0
  90. package/dist/types/version-control-layer.d.ts +53 -0
  91. package/dist/types/version-control-layer.d.ts.map +1 -0
  92. package/dist/types/version-control-layer.js +3 -0
  93. package/dist/types/version-control-layer.js.map +1 -0
  94. package/dist/types/workspace-boundaries.d.ts +17 -0
  95. package/dist/types/workspace-boundaries.d.ts.map +1 -0
  96. package/dist/types/workspace-boundaries.js +7 -0
  97. package/dist/types/workspace-boundaries.js.map +1 -0
  98. package/package.json +42 -0
  99. package/src/index.ts +62 -0
  100. package/src/modules/DependencyLayerModule.ts +329 -0
  101. package/src/modules/FileSystemModule.ts +65 -0
  102. package/src/modules/FileTypeLayerModule.ts +199 -0
  103. package/src/modules/FrameworkLayerModule.ts +437 -0
  104. package/src/modules/PackageLayerModule.ts +979 -0
  105. package/src/modules/TypeExtractionModule.test.ts +340 -0
  106. package/src/modules/TypeExtractionModule.ts +180 -0
  107. package/src/modules/VersionControlLayerModule.ts +31 -0
  108. package/src/modules/__fixtures__/typescript-packages/complex-types/package.json +6 -0
  109. package/src/modules/__fixtures__/typescript-packages/complex-types/src/index.ts +6 -0
  110. package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/category.ts +15 -0
  111. package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/product.ts +48 -0
  112. package/src/modules/__fixtures__/typescript-packages/javascript-only/index.js +18 -0
  113. package/src/modules/__fixtures__/typescript-packages/javascript-only/package.json +5 -0
  114. package/src/modules/__fixtures__/typescript-packages/simple-types/index.ts +53 -0
  115. package/src/modules/__fixtures__/typescript-packages/simple-types/package.json +6 -0
  116. package/src/modules/extractors/README.md +55 -0
  117. package/src/modules/extractors/TypeScriptExtractor.ts +409 -0
  118. package/src/modules/index.ts +13 -0
  119. package/src/providers/GitVersionControlProvider.ts +500 -0
  120. package/src/providers/PackageManagerApiProvider.ts +108 -0
  121. package/src/providers/README.md +88 -0
  122. package/src/providers/index.ts +17 -0
  123. package/src/services/FilesystemService.ts +530 -0
  124. package/src/services/index.ts +2 -0
  125. package/src/types/file-system.ts +11 -0
  126. package/src/types/index.ts +24 -0
  127. package/src/types/layer-types.ts +264 -0
  128. package/src/types/version-control-layer.ts +87 -0
  129. package/src/types/workspace-boundaries.ts +17 -0
@@ -0,0 +1,17 @@
1
+ // Version control providers
2
+ export { GitVersionControlProvider } from './GitVersionControlProvider';
3
+ export type { FileSystemAdapter, GitAdapter, ShellAdapter } from './GitVersionControlProvider';
4
+
5
+ // Package management providers
6
+ export {
7
+ PackageManagerApiProvider,
8
+ type PackageManager,
9
+ type PackageVersionInfo,
10
+ type VersionCheckResult,
11
+ type VulnerabilityInfo,
12
+ type VulnerabilityCheckResult,
13
+ type LicenseInfo,
14
+ type LicenseCheckResult,
15
+ type DependencyCheckProgress,
16
+ type BatchCheckOptions,
17
+ } from './PackageManagerApiProvider';
@@ -0,0 +1,530 @@
1
+ import ignore from 'ignore';
2
+
3
+ import { FileTree, FileInfo, DirectoryInfo } from '@principal-ai/repository-abstraction';
4
+ import { FileSystemAdapter } from '../providers';
5
+ import { FileSystemFilterLayer } from '../types';
6
+
7
+ // Use path operations that work in browser
8
+ const path = {
9
+ basename: (p: string) => p.split('/').pop() || '',
10
+ join: (...parts: string[]) => {
11
+ const joined = parts.filter(p => p).join('/');
12
+ return joined.replace(/\/+/g, '/');
13
+ },
14
+ relative: (from: string, to: string) => {
15
+ if (!from || from === '.') {
16
+ return to;
17
+ }
18
+ if (to.startsWith(from + '/')) {
19
+ return to.slice(from.length + 1);
20
+ }
21
+ if (to === from) {
22
+ return '.';
23
+ }
24
+ return to;
25
+ },
26
+ dirname: (p: string) => {
27
+ const lastSlash = p.lastIndexOf('/');
28
+ return lastSlash > 0 ? p.slice(0, lastSlash) : '';
29
+ },
30
+ extname: (p: string) => {
31
+ const lastDot = p.lastIndexOf('.');
32
+ return lastDot > 0 ? p.slice(lastDot) : '';
33
+ },
34
+ };
35
+
36
+ interface FilterStats {
37
+ totalFiltered: number;
38
+ filteredByLayer: Record<string, number>;
39
+ examples: Array<{ path: string; filteredBy: string }>;
40
+ filteredByDirectory: Record<string, { count: number; reasons: Set<string> }>;
41
+ }
42
+
43
+ export class FilesystemService {
44
+ private filterStats: FilterStats = {
45
+ totalFiltered: 0,
46
+ filteredByLayer: {},
47
+ examples: [],
48
+ filteredByDirectory: {},
49
+ };
50
+
51
+ constructor(private adapter?: FileSystemAdapter) {}
52
+
53
+ /**
54
+ * Build file tree using the provided adapter with efficient filtering
55
+ */
56
+ async buildFileSystemTreeFromPath(
57
+ directoryPath: string,
58
+ filters?: FileSystemFilterLayer[],
59
+ options?: { enableInverseFiltering?: boolean } | object,
60
+ ): Promise<
61
+ FileTree & {
62
+ filterStats?: FilterStats;
63
+ appliedFilters?: FileSystemFilterLayer[];
64
+ inverseMatches?: Array<{ path: string; matchedFilters: string[] }>;
65
+ }
66
+ > {
67
+ console.time('[FilesystemService] Total build time');
68
+
69
+ // Reset filter stats
70
+ this.filterStats = {
71
+ totalFiltered: 0,
72
+ filteredByLayer: {},
73
+ examples: [],
74
+ filteredByDirectory: {},
75
+ };
76
+
77
+ const allFilters = filters || [];
78
+ // Handle both old and new options format for backward compatibility
79
+ const enableInverseFiltering =
80
+ (options as { enableInverseFiltering?: boolean })?.enableInverseFiltering || false;
81
+
82
+ // Handle case when no adapter is provided
83
+ if (!this.adapter) {
84
+ // Return empty tree structure for backward compatibility
85
+ const rootDir = {
86
+ path: directoryPath,
87
+ name: directoryPath.split('/').pop() || directoryPath,
88
+ children: [],
89
+ fileCount: 0,
90
+ totalSize: 0,
91
+ depth: 0,
92
+ relativePath: '.',
93
+ };
94
+
95
+ return {
96
+ sha: '',
97
+ root: rootDir,
98
+ allFiles: [],
99
+ allDirectories: [rootDir],
100
+ stats: {
101
+ totalFiles: 0,
102
+ totalDirectories: 1,
103
+ totalSize: 0,
104
+ maxDepth: 0,
105
+ buildingTypeDistribution: {},
106
+ directoryTypeDistribution: {},
107
+ combinedTypeDistribution: {},
108
+ },
109
+ filterStats: this.filterStats,
110
+ appliedFilters: allFilters,
111
+ inverseMatches: enableInverseFiltering ? [] : undefined,
112
+ };
113
+ }
114
+
115
+ console.time('[FilesystemService] Build file tree');
116
+
117
+ // Use the adapter's optimized implementation
118
+ const patterns: string[] = [];
119
+ let sourceDir: string | undefined;
120
+
121
+ // Convert filters to patterns for the adapter
122
+ for (const filter of allFilters) {
123
+ if (filter.enabled && filter.filterData?.excludedPatterns) {
124
+ patterns.push(...filter.filterData.excludedPatterns.map(p => p.pattern));
125
+ if (filter.filterData.sourceDirectory) {
126
+ sourceDir = filter.filterData.sourceDirectory;
127
+ }
128
+ }
129
+ }
130
+
131
+ const adapterResult = await this.adapter.buildFilteredFileTree(
132
+ directoryPath,
133
+ patterns,
134
+ sourceDir,
135
+ );
136
+ const crawledPaths = adapterResult.paths;
137
+ const fileStats = adapterResult.stats;
138
+
139
+ // Update filter stats
140
+ const ignoreInstances = this.createIgnoreInstances(allFilters);
141
+ for (const path of crawledPaths) {
142
+ if (!this.shouldIncludeFile(path, ignoreInstances, allFilters)) {
143
+ this.filterStats.totalFiltered++;
144
+ }
145
+ }
146
+
147
+ console.timeEnd('[FilesystemService] Build file tree');
148
+ console.log(`[FilesystemService] Found ${crawledPaths.length} paths after filtering`);
149
+
150
+ // Convert paths to FileInfo and DirectoryInfo objects
151
+ console.time('[FilesystemService] Convert to tree structure');
152
+ const { allFiles, allDirectories, rootDir } = await this.convertPathsToTree(
153
+ crawledPaths,
154
+ directoryPath,
155
+ fileStats,
156
+ );
157
+ console.timeEnd('[FilesystemService] Convert to tree structure');
158
+
159
+ // Handle inverse filtering for power users
160
+ let inverseMatches: Array<{ path: string; matchedFilters: string[] }> = [];
161
+ if (enableInverseFiltering) {
162
+ inverseMatches = await this.findInverseMatches(directoryPath, ignoreInstances, allFilters);
163
+ }
164
+
165
+ // Calculate stats
166
+ const totalSize = allFiles.reduce((sum, file) => sum + (file.size || 0), 0);
167
+ const maxDepth = Math.max(...allDirectories.map(d => d.depth || 0));
168
+
169
+ // Update filter layer stats
170
+ allFilters.forEach(layer => {
171
+ if (layer.enabled && this.filterStats.filteredByLayer[layer.id]) {
172
+ if (!layer.filterData.stats) {
173
+ layer.filterData.stats = {
174
+ totalFiltered: 0,
175
+ bytesExcluded: 0,
176
+ };
177
+ }
178
+ layer.filterData.stats.totalFiltered = this.filterStats.filteredByLayer[layer.id];
179
+ }
180
+ });
181
+
182
+ const treeResult: FileTree & {
183
+ filterStats?: FilterStats;
184
+ appliedFilters?: FileSystemFilterLayer[];
185
+ inverseMatches?: Array<{ path: string; matchedFilters: string[] }>;
186
+ } = {
187
+ sha: '',
188
+ root: rootDir || {
189
+ path: '.',
190
+ name: path.basename(directoryPath) || 'root',
191
+ children: [],
192
+ fileCount: 0,
193
+ totalSize: 0,
194
+ depth: 0,
195
+ relativePath: '.',
196
+ },
197
+ allFiles,
198
+ allDirectories,
199
+ stats: {
200
+ totalFiles: allFiles.length,
201
+ totalDirectories: allDirectories.length,
202
+ totalSize,
203
+ maxDepth,
204
+ buildingTypeDistribution: {},
205
+ directoryTypeDistribution: {},
206
+ combinedTypeDistribution: {},
207
+ },
208
+ filterStats: this.filterStats,
209
+ appliedFilters: allFilters,
210
+ inverseMatches: inverseMatches,
211
+ };
212
+
213
+ console.timeEnd('[FilesystemService] Total build time');
214
+ return treeResult;
215
+ }
216
+
217
+ /**
218
+ * Create optimized ignore instances from filter layers
219
+ */
220
+ private createIgnoreInstances(filters: FileSystemFilterLayer[]): Map<
221
+ string,
222
+ {
223
+ ignore: ReturnType<typeof ignore>;
224
+ sourceDirectory: string;
225
+ filterId: string;
226
+ filterName: string;
227
+ }
228
+ > {
229
+ const ignoreInstances = new Map<
230
+ string,
231
+ {
232
+ ignore: ReturnType<typeof ignore>;
233
+ sourceDirectory: string;
234
+ filterId: string;
235
+ filterName: string;
236
+ }
237
+ >();
238
+
239
+ for (const filter of filters) {
240
+ if (!filter.enabled || !filter.filterData?.excludedPatterns) continue;
241
+
242
+ const sourceDirectory = filter.filterData.sourceDirectory || '';
243
+ const key = `${filter.id}:${sourceDirectory}`;
244
+
245
+ const ig = ignore();
246
+ const patterns = filter.filterData.excludedPatterns.map(p => p.pattern);
247
+ ig.add(patterns);
248
+
249
+ ignoreInstances.set(key, {
250
+ ignore: ig,
251
+ sourceDirectory,
252
+ filterId: filter.id,
253
+ filterName: filter.name,
254
+ });
255
+ }
256
+
257
+ return ignoreInstances;
258
+ }
259
+
260
+ /**
261
+ * Fast filtering using node-ignore during fdir crawl
262
+ */
263
+ private shouldIncludeFile(
264
+ filePath: string,
265
+ ignoreInstances: Map<
266
+ string,
267
+ {
268
+ ignore: ReturnType<typeof ignore>;
269
+ sourceDirectory: string;
270
+ filterId: string;
271
+ filterName: string;
272
+ }
273
+ >,
274
+ _filters: FileSystemFilterLayer[],
275
+ ): boolean {
276
+ for (const [, { ignore: ig, sourceDirectory, filterId, filterName }] of ignoreInstances) {
277
+ // Check if this path is within the filter's scope
278
+ if (sourceDirectory !== '') {
279
+ if (!filePath.startsWith(sourceDirectory + '/') && filePath !== sourceDirectory) {
280
+ continue; // Skip this filter - path is outside its scope
281
+ }
282
+ }
283
+
284
+ // Get the path relative to the filter's source directory for testing
285
+ let pathToTest = filePath;
286
+ if (sourceDirectory !== '' && filePath.startsWith(sourceDirectory + '/')) {
287
+ pathToTest = filePath.slice(sourceDirectory.length + 1);
288
+ }
289
+
290
+ // Skip empty paths and "." which node-ignore doesn't handle
291
+ if (!pathToTest || pathToTest === '.') {
292
+ continue;
293
+ }
294
+
295
+ // Use node-ignore for fast pattern matching
296
+ if (ig.ignores(pathToTest)) {
297
+ // Track statistics
298
+ this.filterStats.totalFiltered++;
299
+ this.filterStats.filteredByLayer[filterId] =
300
+ (this.filterStats.filteredByLayer[filterId] || 0) + 1;
301
+
302
+ // Track by directory
303
+ const dirPath = path.dirname(filePath) || '.';
304
+ if (!this.filterStats.filteredByDirectory[dirPath]) {
305
+ this.filterStats.filteredByDirectory[dirPath] = { count: 0, reasons: new Set() };
306
+ }
307
+ this.filterStats.filteredByDirectory[dirPath].count++;
308
+ this.filterStats.filteredByDirectory[dirPath].reasons.add(filterName);
309
+
310
+ return false; // Exclude this file
311
+ }
312
+ }
313
+
314
+ return true; // Include this file
315
+ }
316
+
317
+ /**
318
+ * Convert array of paths to FileTree structure
319
+ */
320
+ private async convertPathsToTree(
321
+ paths: string[],
322
+ rootPath: string,
323
+ providedStats?: Map<string, { size: number; isDirectory: boolean; lastModified: Date }>,
324
+ ): Promise<{
325
+ allFiles: FileInfo[];
326
+ allDirectories: DirectoryInfo[];
327
+ rootDir: DirectoryInfo | null;
328
+ }> {
329
+ const allFiles: FileInfo[] = [];
330
+ const allDirectories: DirectoryInfo[] = [];
331
+ const directoryMap = new Map<string, DirectoryInfo>();
332
+
333
+ // Use provided stats or fetch them if adapter supports it
334
+ const fileStats =
335
+ providedStats ||
336
+ new Map<string, { size: number; lastModified: Date; isDirectory: boolean }>();
337
+
338
+ if (!providedStats && this.adapter && 'getFileStats' in this.adapter) {
339
+ // Batch file stats operations for better performance
340
+ const statPromises = paths.map(async filePath => {
341
+ try {
342
+ const fullPath = path.join(rootPath, filePath);
343
+ const stats = await (
344
+ this.adapter as FileSystemAdapter & {
345
+ getFileStats: (
346
+ path: string,
347
+ ) => Promise<{ size?: number; lastModified?: Date; isDirectory?: boolean }>;
348
+ }
349
+ ).getFileStats(fullPath);
350
+ if (stats) {
351
+ fileStats.set(filePath, {
352
+ size: stats.size || 0,
353
+ lastModified: stats.lastModified || new Date(),
354
+ isDirectory: stats.isDirectory || false,
355
+ });
356
+ }
357
+ } catch {
358
+ // Ignore stat errors, will use defaults
359
+ }
360
+ });
361
+
362
+ await Promise.all(statPromises);
363
+ }
364
+
365
+ // Create file and directory objects
366
+ for (const filePath of paths) {
367
+ const stats = fileStats.get(filePath);
368
+ const isDirectory = stats?.isDirectory || filePath.endsWith('/');
369
+
370
+ if (isDirectory) {
371
+ const dirInfo: DirectoryInfo = {
372
+ path: filePath || '.',
373
+ name: path.basename(filePath) || 'root',
374
+ children: [],
375
+ fileCount: 0,
376
+ totalSize: 0,
377
+ depth: filePath.split('/').length - 1,
378
+ relativePath: filePath || '.',
379
+ };
380
+
381
+ allDirectories.push(dirInfo);
382
+ directoryMap.set(filePath, dirInfo);
383
+ } else {
384
+ const fileInfo: FileInfo = {
385
+ path: filePath,
386
+ name: path.basename(filePath),
387
+ extension: path.extname(filePath).toLowerCase(),
388
+ size: stats?.size || 0,
389
+ lastModified: stats?.lastModified || new Date(),
390
+ isDirectory: false,
391
+ relativePath: filePath,
392
+ };
393
+
394
+ allFiles.push(fileInfo);
395
+ }
396
+ }
397
+
398
+ // Build hierarchical structure (simplified - just return root)
399
+ const rootDir = directoryMap.get('.') ||
400
+ directoryMap.get('') || {
401
+ path: '.',
402
+ name: path.basename(rootPath) || 'root',
403
+ children: [],
404
+ fileCount: allFiles.length,
405
+ totalSize: allFiles.reduce((sum, f) => sum + (f.size || 0), 0),
406
+ depth: 0,
407
+ relativePath: '.',
408
+ };
409
+
410
+ return { allFiles, allDirectories, rootDir };
411
+ }
412
+
413
+ /**
414
+ * Find files that would match specific filters - for power user investigation
415
+ */
416
+ private async findInverseMatches(
417
+ projectPath: string,
418
+ ignoreInstances: Map<
419
+ string,
420
+ {
421
+ ignore: ReturnType<typeof ignore>;
422
+ sourceDirectory: string;
423
+ filterId: string;
424
+ filterName: string;
425
+ }
426
+ >,
427
+ _filters: FileSystemFilterLayer[],
428
+ ): Promise<Array<{ path: string; matchedFilters: string[] }>> {
429
+ console.log('[FilesystemService] Running inverse filter analysis...');
430
+
431
+ const inverseMatches: Array<{ path: string; matchedFilters: string[] }> = [];
432
+
433
+ // Get ALL files without any filtering to get the complete picture
434
+ if (!this.adapter) {
435
+ return [];
436
+ }
437
+ const unfilteredResult = await this.adapter.buildFilteredFileTree(projectPath);
438
+ const allPaths = unfilteredResult.paths;
439
+
440
+ console.log(`[FilesystemService] Found ${allPaths.length} total paths for inverse analysis`);
441
+
442
+ // Test each path against each filter to see what matches
443
+ for (const filePath of allPaths) {
444
+ const matchedFilters: string[] = [];
445
+
446
+ for (const [, { ignore: ig, sourceDirectory, filterName }] of ignoreInstances) {
447
+ // Check if this path is within the filter's scope
448
+ if (sourceDirectory !== '') {
449
+ if (!filePath.startsWith(sourceDirectory + '/') && filePath !== sourceDirectory) {
450
+ continue; // Skip this filter - path is outside its scope
451
+ }
452
+ }
453
+
454
+ // Get the path relative to the filter's source directory for testing
455
+ let pathToTest = filePath;
456
+ if (sourceDirectory !== '' && filePath.startsWith(sourceDirectory + '/')) {
457
+ pathToTest = filePath.slice(sourceDirectory.length + 1);
458
+ }
459
+
460
+ // Skip empty paths and "." which node-ignore doesn't handle
461
+ if (!pathToTest || pathToTest === '.') {
462
+ continue;
463
+ }
464
+
465
+ // Check if this filter would match this path
466
+ if (ig.ignores(pathToTest)) {
467
+ matchedFilters.push(filterName);
468
+ }
469
+ }
470
+
471
+ // If any filters matched, record it
472
+ if (matchedFilters.length > 0) {
473
+ inverseMatches.push({
474
+ path: filePath,
475
+ matchedFilters,
476
+ });
477
+ }
478
+ }
479
+
480
+ console.log(
481
+ `[FilesystemService] Inverse analysis complete: ${inverseMatches.length} files matched filters`,
482
+ );
483
+ return inverseMatches;
484
+ }
485
+
486
+ /**
487
+ * Get the filter statistics from the last build
488
+ */
489
+ getFilterStats(): FilterStats {
490
+ return { ...this.filterStats };
491
+ }
492
+
493
+ /**
494
+ * Create an empty tree structure - matches original FilesystemService API
495
+ */
496
+ private getEmptyTree(directoryPath: string): FileTree & {
497
+ filterStats?: FilterStats;
498
+ appliedFilters?: FileSystemFilterLayer[];
499
+ inverseMatches?: Array<{ path: string; matchedFilters: string[] }>;
500
+ } {
501
+ const rootDir: DirectoryInfo = {
502
+ path: '.',
503
+ name: path.basename(directoryPath) || 'root',
504
+ children: [],
505
+ fileCount: 0,
506
+ totalSize: 0,
507
+ depth: 0,
508
+ relativePath: '.',
509
+ };
510
+
511
+ return {
512
+ sha: '',
513
+ root: rootDir,
514
+ allFiles: [],
515
+ allDirectories: [rootDir],
516
+ stats: {
517
+ totalFiles: 0,
518
+ totalDirectories: 1,
519
+ totalSize: 0,
520
+ maxDepth: 0,
521
+ buildingTypeDistribution: {},
522
+ directoryTypeDistribution: {},
523
+ combinedTypeDistribution: {},
524
+ },
525
+ filterStats: this.filterStats,
526
+ appliedFilters: [],
527
+ inverseMatches: [],
528
+ };
529
+ }
530
+ }
@@ -0,0 +1,2 @@
1
+ // File system services
2
+ export { FilesystemService } from './FilesystemService';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * File system types for layer modules
3
+ * Re-exported from core/file-tree to maintain consistency
4
+ */
5
+
6
+ // Re-export from the central file-tree module
7
+ export type { FileInfo, DirectoryInfo, FileTree } from '@principal-ai/repository-abstraction';
8
+
9
+ // Alias FileTree as FileSystemTree for backward compatibility
10
+ // This can be removed in a future version
11
+ export type { FileTree as FileSystemTree } from '@principal-ai/repository-abstraction';
@@ -0,0 +1,24 @@
1
+ // Export layer types
2
+ export type {
3
+ FilePattern,
4
+ FileSet,
5
+ BaseLayer,
6
+ PackageCommand,
7
+ ConfigFile,
8
+ PackageLayer,
9
+ FileSystemFilterLayer,
10
+ ExtractedType,
11
+ PackageTypes,
12
+ TypeDefinitionLayer,
13
+ TypeExtractor,
14
+ } from './layer-types';
15
+
16
+ // Export version control types
17
+ export type {
18
+ VersionControlLayer,
19
+ VersionControlIgnorePattern,
20
+ VersionControlProvider,
21
+ } from './version-control-layer';
22
+
23
+ // Export file system types
24
+ export type { FileInfo, DirectoryInfo, FileTree, FileSystemTree } from './file-system';