@parseme/cli 0.0.4 → 0.0.5

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.
@@ -4,84 +4,12 @@ export class ContextBuilder {
4
4
  constructor(config) {
5
5
  this.config = config;
6
6
  }
7
- truncateContent(content, type = 'chars') {
8
- const limits = this.config.get().limits;
9
- if (!limits) {
10
- return content;
11
- }
12
- const strategy = limits.truncateStrategy || 'truncate';
13
- if (type === 'lines') {
14
- const lines = content.split('\n');
15
- const maxLines = limits.maxLinesPerFile || 1000;
16
- if (lines.length > maxLines) {
17
- if (strategy === 'split') {
18
- return this.splitContentByLines(lines, maxLines);
19
- }
20
- else {
21
- const truncated = lines.slice(0, maxLines).join('\n');
22
- return truncated + '\n\n[... truncated for AI compatibility ...]';
23
- }
24
- }
25
- }
26
- else {
27
- const maxChars = limits.maxCharsPerFile || 50000;
28
- if (content.length > maxChars) {
29
- if (strategy === 'split') {
30
- return this.splitContentByChars(content, maxChars);
31
- }
32
- else {
33
- const truncated = content.substring(0, maxChars - 100);
34
- return truncated + '\n\n[... truncated for AI compatibility ...]';
35
- }
36
- }
37
- }
38
- return content;
39
- }
40
- splitContentByLines(lines, maxLines) {
41
- const parts = [];
42
- const safeMaxLines = maxLines - 5; // Reserve space for split indicators
43
- for (let i = 0; i < lines.length; i += safeMaxLines) {
44
- const chunk = lines.slice(i, i + safeMaxLines);
45
- const partNumber = Math.floor(i / safeMaxLines) + 1;
46
- const totalParts = Math.ceil(lines.length / safeMaxLines);
47
- let partContent = chunk.join('\n');
48
- partContent += `\n\n[... part ${partNumber} of ${totalParts} ...]`;
49
- parts.push(partContent);
50
- }
51
- return parts;
52
- }
53
- splitContentByChars(content, maxChars) {
54
- const parts = [];
55
- const safeMaxChars = maxChars - 200; // Reserve space for split indicators
56
- for (let i = 0; i < content.length; i += safeMaxChars) {
57
- let chunk = content.substring(i, i + safeMaxChars);
58
- const partNumber = Math.floor(i / safeMaxChars) + 1;
59
- const totalParts = Math.ceil(content.length / safeMaxChars);
60
- // Try to break at a reasonable place (newline or word boundary)
61
- if (i + safeMaxChars < content.length) {
62
- const lastNewline = chunk.lastIndexOf('\n');
63
- const lastSpace = chunk.lastIndexOf(' ');
64
- const breakPoint = lastNewline > -1 ? lastNewline : lastSpace > -1 ? lastSpace : chunk.length;
65
- if (breakPoint > safeMaxChars * 0.8) {
66
- // Only break if we're not losing too much content
67
- chunk = chunk.substring(0, breakPoint);
68
- }
69
- }
70
- chunk += `\n\n[... part ${partNumber} of ${totalParts} ...]`;
71
- parts.push(chunk);
72
- }
73
- return parts;
74
- }
75
7
  build(context) {
76
8
  return this.buildMultiFile(context);
77
9
  }
78
10
  buildMultiFile(context) {
79
11
  const { projectInfo, fileAnalyses, gitInfo, contextDir, outputPath } = context;
80
- const limits = this.config.get().limits;
81
- // Limit number of files analyzed if specified
82
- const limitedFileAnalyses = limits?.maxFilesPerContext
83
- ? fileAnalyses.slice(0, limits.maxFilesPerContext)
84
- : fileAnalyses;
12
+ // Files are now pre-limited in their respective analyzers, so no need to limit here
85
13
  // Calculate the relative path for the link in markdown
86
14
  let linkPath = 'parseme-context';
87
15
  if (contextDir && outputPath) {
@@ -99,42 +27,36 @@ export class ContextBuilder {
99
27
  // Fallback: use contextDir as-is if no outputPath provided
100
28
  linkPath = contextDir;
101
29
  }
102
- const mainContent = this.buildHeader(linkPath) +
30
+ // Check if routes exist before building main content
31
+ // Extract all actual route objects (filter out reference objects)
32
+ const routes = fileAnalyses.flatMap((f) => {
33
+ const fileRoutes = f.routes || [];
34
+ // Only include if it's an array of actual routes, not a reference object
35
+ return Array.isArray(fileRoutes) && fileRoutes.length > 0 && !('$ref' in fileRoutes[0])
36
+ ? fileRoutes
37
+ : [];
38
+ });
39
+ const hasRoutes = routes.length > 0;
40
+ const mainContent = this.buildHeader(linkPath, hasRoutes) +
103
41
  '\n\n\n' +
104
42
  this.buildProjectOverview(projectInfo) +
105
43
  '\n\n\n' +
106
- this.buildSummarySection(context, linkPath) +
44
+ this.buildSummarySection(linkPath, hasRoutes) +
107
45
  '\n\n\n' +
108
46
  (gitInfo ? this.buildGitSection(gitInfo) : '');
109
47
  const contextFiles = {
110
48
  structure: '',
111
- routes: '',
112
- };
113
- // Helper function to merge split files into contextFiles
114
- const mergeSplitFiles = (result, baseName) => {
115
- if (typeof result === 'string') {
116
- contextFiles[baseName] = result;
117
- }
118
- else {
119
- // Merge split files into contextFiles
120
- Object.entries(result).forEach(([key, value]) => {
121
- contextFiles[key] = value;
122
- });
123
- }
124
49
  };
125
50
  // Files list (markdown) - all files in project, not just analyzed ones
126
51
  contextFiles.files = this.buildFilesList(context.allFiles);
127
52
  // Detailed structure (JSON with AST)
128
- const structureResult = this.buildDetailedStructure(limitedFileAnalyses);
129
- mergeSplitFiles(structureResult, 'structure');
130
- // Routes documentation
131
- const routes = limitedFileAnalyses.flatMap((f) => f.routes || []);
132
- if (routes.length > 0) {
133
- const routesResult = this.buildDetailedRoutes(routes, limitedFileAnalyses);
134
- mergeSplitFiles(routesResult, 'api-endpoints');
53
+ contextFiles.structure = this.buildDetailedStructure(fileAnalyses, hasRoutes);
54
+ // Routes documentation (only if routes exist)
55
+ if (hasRoutes) {
56
+ contextFiles.routes = this.buildDetailedRoutes(routes);
135
57
  }
136
58
  // Git information
137
- if (gitInfo) {
59
+ if (gitInfo?.diffStat?.length && gitInfo.diffStat.length > 0) {
138
60
  contextFiles.gitDiff = this.buildDetailedGit(gitInfo);
139
61
  }
140
62
  return {
@@ -142,17 +64,23 @@ export class ContextBuilder {
142
64
  context: contextFiles,
143
65
  };
144
66
  }
145
- buildHeader(linkPath) {
146
- return `## PARSEME - AI Agent Context
67
+ buildHeader(linkPath, hasRoutes) {
68
+ const routesInstructions = hasRoutes
69
+ ? ` - Files with routes will reference [${linkPath}/routes.json](${linkPath}/routes.json) using a $ref pattern for token efficiency
70
+ 5. For API route details, see [${linkPath}/routes.json](${linkPath}/routes.json) which contains all discovered endpoints
71
+ 6. For git tracked projects follow the instructions in the "Git Information" section of this file to validate the actuality of the provided information.
72
+ 7. Only dive deeper into specific files after reviewing this summary, that replaces the need for initial project exploration and significantly reduces token usage for project comprehension.`
73
+ : `5. For git tracked projects, follow the instructions in the "Git Information" section of this file to validate the actuality of the provided information.
74
+ 6. Only dive deeper into specific files after reviewing this summary, that replaces the need for initial project exploration and significantly reduces token usage for project comprehension.`;
75
+ return `## PARSEME - AI Agent Context
147
76
  Auto-generated project summary optimized for AI coding agents. This file provides complete project context without requiring full codebase traversal, designed for token efficiency.
148
77
 
149
78
  **Usage Instructions for AI Agents:**
150
79
  1. Read this PARSEME.md file completely first before accessing individual project files
151
80
  2. Basic project information, script availability and dependency information provides basic understanding of code base and tech stack without checking package.json
152
- 3. Use the provided file list (${linkPath}/files.md) to see all tracked files in the project
153
- 4. Utilize the structure and AST data (${linkPath}/structure.json) for code analysis without manual parsing
154
- 5. Follow the instruction in the "Git Information" section of this file to validate the actuality of the provided information.
155
- 6. Only dive deeper into specific files after reviewing this summary, that replaces the need for initial project exploration and significantly reduces token usage for project comprehension.`;
81
+ 3. Use the provided file list [${linkPath}/files.md](${linkPath}/files.md) to see all tracked files in the project
82
+ 4. Utilize the structure and AST data [${linkPath}/structure.json](${linkPath}/structure.json) for code analysis without manual parsing
83
+ ${routesInstructions}`;
156
84
  }
157
85
  buildProjectOverview(projectInfo) {
158
86
  let content = `## Basic Project Information
@@ -161,7 +89,7 @@ Auto-generated project summary optimized for AI coding agents. This file provide
161
89
  **Description:** ${projectInfo.description || 'No description available.'}
162
90
  **Type:** ${projectInfo.type} project
163
91
  **Package Manager:** ${projectInfo.packageManager}
164
- **Framework:** ${projectInfo.framework?.name || 'None detected'}`;
92
+ **Framework:** ${this.formatFrameworksList(projectInfo.frameworks)}`;
165
93
  // Add main entry point if available
166
94
  if (projectInfo.entryPoints && projectInfo.entryPoints.length > 0) {
167
95
  content += `\n**Main Entry Point:** ${projectInfo.entryPoints[0]}`;
@@ -184,81 +112,81 @@ Auto-generated project summary optimized for AI coding agents. This file provide
184
112
  }
185
113
  return content;
186
114
  }
115
+ formatFrameworksList(frameworks) {
116
+ if (!frameworks || frameworks.length === 0) {
117
+ return 'unknown';
118
+ }
119
+ if (frameworks.length === 1) {
120
+ return frameworks[0].name;
121
+ }
122
+ // Multiple frameworks - return comma-separated list
123
+ return frameworks.map((f) => f.name).join(', ');
124
+ }
187
125
  buildGitSection(gitInfo) {
188
- return `## Git Information
126
+ const base = `## Git Information
189
127
 
190
128
  **State when PARSEME.md and all linked files were automatically generated:**
191
129
 
192
130
  - **Branch:** ${gitInfo.branch}
193
131
  - **Commit:** ${gitInfo.lastCommit}${gitInfo.origin ? `\n- **Origin:** ${gitInfo.origin}` : ''}
194
132
 
195
- ### Git Diff Statistics
196
- Git diff statistics from the time of generation are available at \`parseme-context/gitDiff.md\`.
133
+ ### Git Diff Statistics`;
134
+ const info = gitInfo.diffStat && gitInfo.diffStat.length > 0
135
+ ? `Git diff statistics from the time of generation are available at [parseme-context/gitDiff.md](parseme-context/gitDiff.md) (relative to the commit mentioned above).
197
136
 
198
137
  **AI Agent Command:** To check for changes since generation, run:
199
138
  \`\`\`bash
200
139
  git diff --stat
201
140
  \`\`\`
202
- Compare the output with the baseline in \`parseme-context/gitDiff.md\` to detect any modifications.`;
141
+ Compare the output with the baseline in [parseme-context/gitDiff.md](parseme-context/gitDiff.md) to detect any modifications.`
142
+ : `Git diff statistics showed no changes at the time of generation relative to the commit mentioned above.`;
143
+ return base + '\n\n' + info;
203
144
  }
204
- buildSummarySection(context, linkPath) {
205
- const { fileAnalyses } = context;
206
- const routes = fileAnalyses.flatMap((f) => f.routes || []).length;
145
+ buildSummarySection(linkPath, hasRoutes) {
207
146
  let content = `## Project Files
208
- A complete list of all tracked files in the project is available at \`${linkPath}/files.md\`. This list excludes files ignored by git and provides a quick overview of the project structure.
147
+ A complete list of all git-tracked files in the project (excluding files matching additional exclude patterns) is available at [${linkPath}/files.md](${linkPath}/files.md). This provides a quick overview of the project structure.
209
148
 
210
149
 
211
150
  ## Project Structure & AST
212
- Detailed structure and Abstract Syntax Tree data for all tracked files is available at \`${linkPath}/structure.json\`. This includes file paths, types, imports, exports, functions, classes, interfaces, and routes for comprehensive code analysis without manual parsing.`;
213
- if (routes > 0) {
214
- content += `\n\n\n## API Endpoints
215
- A comprehensive list of all discovered API endpoints is available at \`${linkPath}/api-endpoints.json\`. This includes HTTP methods, paths, handler names, and source file locations for backend routes (Express, NestJS, and decorator-based routing).`;
151
+ Detailed structure and Abstract Syntax Tree data for all tracked files is available at [${linkPath}/structure.json](${linkPath}/structure.json). This includes file paths, types, imports, exports, functions, classes, interfaces, and routes for comprehensive code analysis without manual parsing.`;
152
+ if (hasRoutes) {
153
+ content += `\n\n\n## API Routes
154
+ A comprehensive list of all discovered API routes is available at [${linkPath}/routes.json](${linkPath}/routes.json). This includes HTTP methods, paths, handler names, and source file locations for backend routes (Express, NestJS, and decorator-based routing).`;
216
155
  }
217
156
  return content;
218
157
  }
219
158
  buildFilesList(allFiles) {
220
- let content = `# Project Files\n\n`;
221
- content += `This is a complete list of all files in the project (excluding files matching exclude patterns).\n\n`;
159
+ let content = `# Project Files\n`;
222
160
  allFiles.forEach((file) => {
223
161
  content += `- ${file}\n`;
224
162
  });
225
163
  return content;
226
164
  }
227
- buildDetailedStructure(fileAnalyses) {
228
- const structureData = fileAnalyses.map((file) => ({
229
- path: file.path,
230
- type: file.type,
231
- exports: file.exports,
232
- imports: file.imports,
233
- functions: file.functions,
234
- classes: file.classes,
235
- routes: file.routes || [],
236
- }));
237
- const jsonContent = JSON.stringify(structureData, null, 2);
238
- const result = this.truncateContent(jsonContent);
239
- if (Array.isArray(result)) {
240
- // Return split files as a record with numbered keys
241
- const splitFiles = {};
242
- result.forEach((part, index) => {
243
- const suffix = index === 0 ? '' : `_part${index + 1}`;
244
- splitFiles[`structure${suffix}`] = part;
245
- });
246
- return splitFiles;
247
- }
248
- return result;
165
+ buildDetailedStructure(fileAnalyses, hasRoutes) {
166
+ const structureData = fileAnalyses.map((file) => {
167
+ const routes = file.routes || [];
168
+ // If file has routes and routes exist in the project, replace with reference instead of full route objects
169
+ const routesData = routes.length > 0 && hasRoutes
170
+ ? {
171
+ $ref: './routes.json',
172
+ filter: { file: file.path },
173
+ count: routes.length,
174
+ }
175
+ : [];
176
+ return {
177
+ path: file.path,
178
+ type: file.type,
179
+ exports: file.exports,
180
+ imports: file.imports,
181
+ functions: file.functions,
182
+ classes: file.classes,
183
+ routes: routesData,
184
+ };
185
+ });
186
+ return JSON.stringify(structureData, null, 2);
249
187
  }
250
- buildDetailedRoutes(routes, _fileAnalyses) {
251
- const jsonContent = JSON.stringify(routes, null, 2);
252
- const result = this.truncateContent(jsonContent);
253
- if (Array.isArray(result)) {
254
- const splitFiles = {};
255
- result.forEach((part, index) => {
256
- const suffix = index === 0 ? '' : `_part${index + 1}`;
257
- splitFiles[`routes${suffix}`] = part;
258
- });
259
- return splitFiles;
260
- }
261
- return result;
188
+ buildDetailedRoutes(routes) {
189
+ return JSON.stringify(routes, null, 2);
262
190
  }
263
191
  buildDetailedDependencies(projectInfo) {
264
192
  const jsonContent = JSON.stringify({
@@ -266,44 +194,32 @@ A comprehensive list of all discovered API endpoints is available at \`${linkPat
266
194
  packageManager: projectInfo.packageManager,
267
195
  version: projectInfo.version,
268
196
  }, null, 2);
269
- const result = this.truncateContent(jsonContent);
270
- if (Array.isArray(result)) {
271
- const splitFiles = {};
272
- result.forEach((part, index) => {
273
- const suffix = index === 0 ? '' : `_part${index + 1}`;
274
- splitFiles[`dependencies${suffix}`] = part;
275
- });
276
- return splitFiles;
277
- }
278
- return result;
197
+ return jsonContent;
279
198
  }
280
- buildDetailedFramework(framework) {
281
- if (!framework) {
199
+ buildDetailedFramework(frameworks) {
200
+ if (!frameworks || frameworks.length === 0) {
282
201
  return '';
283
202
  }
284
- let content = `# Framework: ${framework.name}\n\n`;
285
- content += `**Version**: ${framework.version || 'Unknown'}\n\n`;
286
- if (framework.features.length > 0) {
287
- content += '## Features Detected\n\n';
288
- framework.features.forEach((feature) => {
289
- content += `- ${feature}\n`;
290
- });
291
- }
203
+ let content = '';
204
+ frameworks.forEach((framework, index) => {
205
+ if (index > 0) {
206
+ content += '\n\n';
207
+ }
208
+ content += `# Framework: ${framework.name}\n\n`;
209
+ content += `**Version**: ${framework.version || 'Unknown'}\n\n`;
210
+ if (framework.features.length > 0) {
211
+ content += '## Features Detected\n\n';
212
+ framework.features.forEach((feature) => {
213
+ content += `- ${feature}\n`;
214
+ });
215
+ }
216
+ });
292
217
  return content;
293
218
  }
294
219
  buildDetailedGit(gitInfo) {
295
220
  let content = `# Git Diff Statistics
296
- # Generated at time of PARSEME.md creation
297
- # To check for changes since then, run: git diff --stat
298
- # Compare the output with the content below
299
-
300
221
  `;
301
- if (gitInfo.diffStat && gitInfo.diffStat.length > 0) {
302
- content += gitInfo.diffStat;
303
- }
304
- else {
305
- content += 'No changes detected at time of generation.';
306
- }
222
+ content += gitInfo.diffStat;
307
223
  return content;
308
224
  }
309
225
  }
@@ -2,10 +2,10 @@ import { mkdir, writeFile } from 'fs/promises';
2
2
  import { join } from 'path';
3
3
  import { ASTAnalyzer } from './analyzers/ast-analyzer.js';
4
4
  import { FrameworkDetector } from './analyzers/framework-detector.js';
5
- import { GitAnalyzer } from './analyzers/git-analyzer.js';
6
5
  import { ProjectAnalyzer } from './analyzers/project-analyzer.js';
7
6
  import { ParsemeConfig } from './config.js';
8
7
  import { ContextBuilder } from './context-builder.js';
8
+ import { GitAnalyzer } from '../utils/git.js';
9
9
  export class ParsemeGenerator {
10
10
  config;
11
11
  projectAnalyzer;
@@ -25,10 +25,10 @@ export class ParsemeGenerator {
25
25
  const configData = this.config.get();
26
26
  // Step 1: Analyze the project structure and metadata
27
27
  const projectInfo = await this.projectAnalyzer.analyze(configData.rootDir);
28
- // Step 2: Detect framework and analyze specific patterns
29
- projectInfo.framework = await this.frameworkDetector.detect(projectInfo);
30
- // Step 3: Analyze all relevant files with AST
28
+ // Step 2: Analyze all relevant files with AST
31
29
  const fileAnalyses = await this.astAnalyzer.analyzeProject(configData.rootDir);
30
+ // Step 3: Detect frameworks from dependencies
31
+ projectInfo.frameworks = await this.frameworkDetector.detect(projectInfo);
32
32
  // Step 4: Get all project files (for file list output)
33
33
  const allFiles = await this.projectAnalyzer.getAllProjectFiles(configData.rootDir);
34
34
  // Step 5: Get git information if enabled
@@ -1,14 +1,13 @@
1
- import type { ServiceInfo, ModelInfo, ConfigInfo, MiddlewareInfo, UtilityInfo } from '../analyzers/pattern-detector.js';
2
- export type { ServiceInfo, ModelInfo, ConfigInfo, MiddlewareInfo, UtilityInfo };
1
+ import type { ServiceInfo, ModelInfo, ConfigInfo, MiddlewareInfo, UtilityInfo, EndpointInfo } from '../analyzers/pattern-detector.js';
2
+ export type { ServiceInfo, ModelInfo, ConfigInfo, MiddlewareInfo, UtilityInfo, EndpointInfo };
3
3
  export interface FileAnalysis {
4
4
  path: string;
5
5
  type: 'route' | 'middleware' | 'model' | 'service' | 'utility' | 'config' | 'test' | 'component';
6
- framework?: string;
7
6
  exports: string[];
8
7
  imports: string[];
9
8
  functions: string[];
10
9
  classes: string[];
11
- routes?: RouteInfo[];
10
+ routes?: EndpointInfo[];
12
11
  components?: ComponentInfo[];
13
12
  services?: ServiceInfo[];
14
13
  models?: ModelInfo[];
@@ -1,6 +1,7 @@
1
1
  export interface GeneratorOptions {
2
2
  rootDir?: string;
3
3
  includeGitInfo?: boolean;
4
+ useGitForFiles?: boolean;
4
5
  maxDepth?: number;
5
6
  excludePatterns?: string[];
6
7
  analyzeFileTypes?: string[];
@@ -13,6 +14,7 @@ export interface ParsemeConfigFile extends GeneratorOptions {
13
14
  excludePatterns?: string[];
14
15
  maxDepth?: number;
15
16
  includeGitInfo?: boolean;
17
+ useGitForFiles?: boolean;
16
18
  readmeSuggestion?: boolean;
17
19
  sections?: {
18
20
  overview?: boolean;
@@ -29,9 +31,6 @@ export interface ParsemeConfigFile extends GeneratorOptions {
29
31
  sortOrder?: 'alphabetical' | 'type' | 'size';
30
32
  };
31
33
  limits?: {
32
- maxLinesPerFile?: number;
33
- maxCharsPerFile?: number;
34
34
  maxFilesPerContext?: number;
35
- truncateStrategy?: 'truncate' | 'split' | 'summarize';
36
35
  };
37
36
  }
@@ -1,8 +1,6 @@
1
1
  export interface ContextOutput {
2
2
  parseme: string;
3
3
  context?: {
4
- structure: string;
5
- routes: string;
6
4
  [key: string]: string;
7
5
  };
8
6
  }
@@ -6,7 +6,7 @@ export interface ProjectInfo {
6
6
  type: 'typescript' | 'javascript' | 'mixed';
7
7
  category: ProjectCategory;
8
8
  packageManager: 'unknown' | 'npm' | 'yarn' | 'pnpm' | 'bun';
9
- framework?: FrameworkInfo;
9
+ frameworks?: FrameworkInfo[];
10
10
  buildTool?: BuildToolInfo;
11
11
  dependencies: Record<string, string>;
12
12
  devDependencies: Record<string, string>;
@@ -0,0 +1,23 @@
1
+ import type { ParsemeConfig } from '../core/config.js';
2
+ export interface FileCollectionOptions {
3
+ fileTypes?: string[];
4
+ }
5
+ export interface FileCollectionResult {
6
+ files: string[];
7
+ totalFound: number;
8
+ excluded: number;
9
+ }
10
+ export declare class FileCollector {
11
+ private readonly fileFilter;
12
+ private readonly config;
13
+ constructor(config: ParsemeConfig);
14
+ getFiles(rootDir: string, options?: FileCollectionOptions): Promise<FileCollectionResult>;
15
+ /**
16
+ * Get all project files (no file type filtering, for files.md)
17
+ */
18
+ getAllProjectFiles(rootDir: string): Promise<FileCollectionResult>;
19
+ /**
20
+ * Get code files for AST analysis (filtered by file types, for structure.json)
21
+ */
22
+ getCodeFiles(rootDir: string, fileTypes?: string[]): Promise<FileCollectionResult>;
23
+ }
@@ -0,0 +1,61 @@
1
+ import { FileFilterService } from './file-filter.js';
2
+ export class FileCollector {
3
+ fileFilter;
4
+ config;
5
+ constructor(config) {
6
+ this.config = config;
7
+ const configData = this.config.get();
8
+ this.fileFilter = new FileFilterService(configData.excludePatterns, configData.useGitForFiles ?? true);
9
+ }
10
+ async getFiles(rootDir, options = {}) {
11
+ const configData = this.config.get();
12
+ const { fileTypes } = options;
13
+ // Get all filtered files (respects git ignore + custom excludePatterns)
14
+ const allFiles = await this.fileFilter.getFilteredFiles(rootDir);
15
+ // Filter by file types if specified
16
+ let filteredFiles = allFiles;
17
+ if (fileTypes && fileTypes.length > 0) {
18
+ const extensionSet = new Set(fileTypes.map((ext) => (ext.startsWith('.') ? ext : `.${ext}`)));
19
+ filteredFiles = allFiles.filter((file) => {
20
+ const ext = file.substring(file.lastIndexOf('.'));
21
+ return extensionSet.has(ext);
22
+ });
23
+ }
24
+ // Apply limits
25
+ let finalFiles = filteredFiles;
26
+ let excluded = 0;
27
+ if (configData.limits?.maxFilesPerContext) {
28
+ const limit = configData.limits.maxFilesPerContext;
29
+ if (filteredFiles.length > limit) {
30
+ finalFiles = filteredFiles.slice(0, limit);
31
+ excluded = filteredFiles.length - limit;
32
+ // Show warning when files are excluded
33
+ const fileTypeDesc = fileTypes ? `${fileTypes.join(', ')} files` : 'files';
34
+ console.warn(`⚠️ File limit reached: ${excluded} ${fileTypeDesc} excluded.`);
35
+ console.warn(` Processed: ${limit}/${filteredFiles.length} files`);
36
+ console.warn(` To process more files, increase 'limits.maxFilesPerContext' in your config file.`);
37
+ }
38
+ }
39
+ return {
40
+ files: finalFiles,
41
+ totalFound: filteredFiles.length,
42
+ excluded,
43
+ };
44
+ }
45
+ /**
46
+ * Get all project files (no file type filtering, for files.md)
47
+ */
48
+ async getAllProjectFiles(rootDir) {
49
+ return this.getFiles(rootDir);
50
+ }
51
+ /**
52
+ * Get code files for AST analysis (filtered by file types, for structure.json)
53
+ */
54
+ async getCodeFiles(rootDir, fileTypes) {
55
+ const configData = this.config.get();
56
+ const defaultFileTypes = configData.analyzeFileTypes || ['ts', 'tsx', 'js', 'jsx'];
57
+ return this.getFiles(rootDir, {
58
+ fileTypes: fileTypes || defaultFileTypes,
59
+ });
60
+ }
61
+ }
@@ -0,0 +1,30 @@
1
+ export declare class FileFilterService {
2
+ private readonly excludePatterns;
3
+ private readonly useGitForFiles;
4
+ private readonly ig;
5
+ constructor(excludePatterns?: string[], useGitForFiles?: boolean);
6
+ /**
7
+ * Get all files that should be analyzed, respecting:
8
+ * 1. Git-tracked files (respects all .gitignore files automatically)
9
+ * 2. All files (if non-git repo) - respects only custom excludePatterns
10
+ * 3. Custom excludePatterns from config (always applied)
11
+ */
12
+ getFilteredFiles(rootDir: string, fileExtensions?: string[]): Promise<string[]>;
13
+ /**
14
+ * Check if the directory is a git repository
15
+ */
16
+ private isGitRepository;
17
+ /**
18
+ * Get all tracked files from git
19
+ * This respects all .gitignore files in the repository (parent, current, and child dirs)
20
+ */
21
+ private getGitTrackedFiles;
22
+ /**
23
+ * Get all files recursively (fallback for non-git repos)
24
+ */
25
+ private getAllFilesRecursive;
26
+ /**
27
+ * Check if a specific file should be filtered out
28
+ */
29
+ shouldIgnore(filePath: string): boolean;
30
+ }