@parseme/cli 0.0.3 → 0.0.4

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 (38) hide show
  1. package/README.md +163 -156
  2. package/dist/{cli.js → cli/cli.js} +59 -49
  3. package/dist/{prompt.d.ts → cli/prompt.d.ts} +0 -1
  4. package/dist/{prompt.js → cli/prompt.js} +0 -9
  5. package/dist/{analyzers → core/analyzers}/ast-analyzer.js +7 -6
  6. package/dist/{analyzers → core/analyzers}/framework-detector.d.ts +0 -3
  7. package/dist/{analyzers → core/analyzers}/framework-detector.js +0 -4
  8. package/dist/{analyzers → core/analyzers}/git-analyzer.d.ts +0 -3
  9. package/dist/{analyzers → core/analyzers}/git-analyzer.js +0 -4
  10. package/dist/{analyzers → core/analyzers}/pattern-detector.d.ts +0 -3
  11. package/dist/{analyzers → core/analyzers}/pattern-detector.js +52 -47
  12. package/dist/{analyzers → core/analyzers}/project-analyzer.d.ts +2 -0
  13. package/dist/{analyzers → core/analyzers}/project-analyzer.js +19 -5
  14. package/dist/core/config.d.ts +20 -0
  15. package/dist/{config.js → core/config.js} +80 -68
  16. package/dist/{builders → core}/context-builder.d.ts +3 -8
  17. package/dist/{builders → core}/context-builder.js +7 -84
  18. package/dist/{generator.d.ts → core/generator.d.ts} +0 -3
  19. package/dist/{generator.js → core/generator.js} +8 -13
  20. package/dist/core/types/analyzer-types.d.ts +39 -0
  21. package/dist/core/types/analyzer-types.js +2 -0
  22. package/dist/{config.d.ts → core/types/config-types.d.ts} +8 -15
  23. package/dist/core/types/config-types.js +2 -0
  24. package/dist/core/types/generator-types.d.ts +8 -0
  25. package/dist/core/types/generator-types.js +2 -0
  26. package/dist/core/types/index.d.ts +4 -0
  27. package/dist/core/types/index.js +5 -0
  28. package/dist/core/types/project-types.d.ts +30 -0
  29. package/dist/core/types/project-types.js +2 -0
  30. package/dist/core/types.d.ts +1 -0
  31. package/dist/core/types.js +2 -0
  32. package/dist/index.d.ts +3 -4
  33. package/dist/index.js +2 -2
  34. package/package.json +3 -4
  35. package/dist/types.d.ts +0 -84
  36. package/dist/types.js +0 -2
  37. /package/dist/{cli.d.ts → cli/cli.d.ts} +0 -0
  38. /package/dist/{analyzers → core/analyzers}/ast-analyzer.d.ts +0 -0
@@ -1,22 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { join } from 'path';
3
3
  import { Command } from 'commander';
4
- import { ParsemeConfig } from './config.js';
5
- import { ParsemeGenerator } from './generator.js';
6
- import { confirmPrompt } from './prompt.js';
4
+ import { prompt } from './prompt.js';
5
+ import { ParsemeConfig } from '../core/config.js';
6
+ import { ParsemeGenerator } from '../core/generator.js';
7
7
  const program = new Command();
8
- async function promptForMissingConfig(config, cliOptions) {
9
- const finalConfig = { ...config };
10
- // Only prompt if running interactively (stdin is a TTY) and not in CI
11
- if (!process.stdin.isTTY || process.env.CI) {
12
- return finalConfig;
13
- }
14
- // Prompt for README suggestion if not set via CLI or config
15
- if (cliOptions.readmeSuggestion === undefined && config.readmeSuggestion === undefined) {
16
- const wantReadmeSuggestion = await confirmPrompt('Show README.md section suggestion for AI agents?', true);
17
- finalConfig.readmeSuggestion = wantReadmeSuggestion;
18
- }
19
- return finalConfig;
8
+ async function promptForMissingConfig(config) {
9
+ return { ...config };
20
10
  }
21
11
  program.name('parseme').description('AI Project Context Generator').version('0.1.0');
22
12
  // Main command - just run parseme
@@ -26,11 +16,10 @@ program
26
16
  .option('-o, --output <path>', 'Output file path')
27
17
  .option('-r, --root <path>', 'Root directory to analyze')
28
18
  .option('--context-dir <path>', 'Context directory path (default: parseme-context)')
29
- .option('--include <patterns...>', 'Include patterns (glob)')
19
+ .option('--file-types <types...>', 'File types to analyze (e.g., ts tsx js jsx)')
30
20
  .option('--exclude <patterns...>', 'Exclude patterns (glob)')
31
21
  .option('--no-git', 'Disable git information')
32
22
  .option('--max-depth <number>', 'Maximum directory depth', parseInt)
33
- .option('--no-readme-suggestion', 'Disable README.md section suggestion')
34
23
  .action(async (options) => {
35
24
  try {
36
25
  // Convert CLI options to config format
@@ -38,14 +27,16 @@ program
38
27
  ...(options.output && { outputPath: options.output }),
39
28
  ...(options.root && { rootDir: options.root }),
40
29
  ...(options.contextDir && { contextDir: options.contextDir }),
41
- ...(options.include && { includePatterns: options.include }),
30
+ ...(options.fileTypes && { analyzeFileTypes: options.fileTypes }),
42
31
  ...(options.exclude && { excludePatterns: options.exclude }),
43
32
  ...(options.git === false && { includeGitInfo: false }),
44
33
  ...(options.maxDepth && { maxDepth: options.maxDepth }),
45
- ...(options.readmeSuggestion === false && { readmeSuggestion: false }),
46
34
  };
47
- const configFromFile = await ParsemeConfig.fromFile(options.config);
48
- const interactiveConfig = await promptForMissingConfig(configFromFile.get(), cliOptions);
35
+ const configFromFile = await ParsemeConfig.fromFile(options.config, {
36
+ showWarnings: true,
37
+ throwOnNotFound: true,
38
+ });
39
+ const interactiveConfig = await promptForMissingConfig(configFromFile.get());
49
40
  // Merge: CLI options > interactive prompts > config file > defaults
50
41
  const finalConfig = {
51
42
  ...interactiveConfig,
@@ -55,19 +46,12 @@ program
55
46
  const generator = new ParsemeGenerator(config.get());
56
47
  await generator.generateToFile();
57
48
  console.log('Context generated successfully');
58
- console.log('Tip: Add parseme as a git hook to keep context auto-updated! See README for setup instructions.');
59
- const shouldShowReadmeSuggestion = finalConfig.readmeSuggestion !== false;
60
- if (shouldShowReadmeSuggestion) {
61
- console.log('Tip: Add this section to your README.md to help AI agents find the context:');
62
- console.log('');
63
- console.log('## For AI Assistants');
64
- console.log('This project includes AI-optimized documentation:');
65
- console.log('- `PARSEME.md` - Main project context and overview');
66
- console.log('- `parseme-context/` - Detailed JSON files with code structure, dependencies, and git info');
67
- console.log('');
68
- }
69
49
  }
70
50
  catch (error) {
51
+ if (error instanceof Error && error.message.includes('No configuration file found')) {
52
+ console.error(error.message);
53
+ process.exit(1);
54
+ }
71
55
  console.error('Failed to generate context:', error);
72
56
  process.exit(1);
73
57
  }
@@ -76,7 +60,7 @@ program
76
60
  .command('init')
77
61
  .description('Initialize parseme configuration')
78
62
  .option('-f, --force', 'Overwrite existing config')
79
- .option('--format <format>', 'Config format: js, ts, or json', 'js')
63
+ .option('--format <format>', 'Config format: json, ts, or js', 'json')
80
64
  .action(async (options) => {
81
65
  try {
82
66
  // Validate format
@@ -85,7 +69,6 @@ program
85
69
  process.exit(1);
86
70
  }
87
71
  const configPath = join(process.cwd(), `parseme.config.${options.format}`);
88
- const config = new ParsemeConfig();
89
72
  // Check if config already exists
90
73
  if (!options.force) {
91
74
  try {
@@ -98,12 +81,44 @@ program
98
81
  // File doesn't exist, continue
99
82
  }
100
83
  }
84
+ // Build config with only user-specified values
85
+ const userConfig = {};
86
+ // Only prompt if interactive (TTY) and not in CI
87
+ if (process.stdin.isTTY && !process.env.CI) {
88
+ // Ask about context directory path
89
+ // Set context directory to what user entered (or default if they pressed enter)
90
+ userConfig.contextDir = await prompt({
91
+ message: 'Context directory path',
92
+ defaultValue: 'parseme-context',
93
+ });
94
+ // Ask about exclude patterns
95
+ const defaultExcludePatterns = ['node_modules/**', 'dist/**', '.git/**'];
96
+ const excludePatternsAnswer = await prompt({
97
+ message: 'Exclude patterns (comma-separated glob patterns, patterns from .gitignore will be ignored by default as well)',
98
+ defaultValue: defaultExcludePatterns.join(', '),
99
+ });
100
+ // Always set exclude patterns to what user entered (or defaults if they pressed enter)
101
+ userConfig.excludePatterns = excludePatternsAnswer
102
+ .split(',')
103
+ .map((p) => p.trim())
104
+ .filter((p) => p.length > 0);
105
+ }
106
+ const config = new ParsemeConfig(userConfig);
101
107
  await config.save(configPath);
102
108
  console.log(`Configuration file created at ${configPath}`);
103
109
  if (options.format === 'ts') {
104
110
  console.log('For TypeScript configs, ensure tsx or ts-node is available to load .ts files');
105
111
  }
106
- console.log('Add "parseme": "parseme" to your package.json scripts');
112
+ console.log('Tip: Add "parseme": "parseme" to your package.json scripts for easier manual execution or hook integration');
113
+ console.log('Tip: Add parseme as a git hook to keep context auto-updated! See README for setup instructions.');
114
+ console.log('');
115
+ console.log('Tip: Add this section to your README.md to help AI agents find the context:');
116
+ console.log('');
117
+ console.log('## Instructions For AI Agents');
118
+ console.log('This project includes AI-optimized documentation for efficient context providing:');
119
+ console.log('- `PARSEME.md` - Project overview with links to detailed context files');
120
+ console.log('- `parseme-context/` - Structured data files (AST analysis, dependencies, routes, git info)');
121
+ console.log('');
107
122
  }
108
123
  catch (error) {
109
124
  console.error('Failed to create configuration:', error);
@@ -115,27 +130,22 @@ if (process.argv.length <= 2) {
115
130
  // Run the default action
116
131
  (async () => {
117
132
  try {
118
- const configFromFile = await ParsemeConfig.fromFile();
119
- const interactiveConfig = await promptForMissingConfig(configFromFile.get(), {});
133
+ const configFromFile = await ParsemeConfig.fromFile(undefined, {
134
+ showWarnings: true,
135
+ throwOnNotFound: true,
136
+ });
137
+ const interactiveConfig = await promptForMissingConfig(configFromFile.get());
120
138
  const config = new ParsemeConfig(interactiveConfig);
121
139
  const generator = new ParsemeGenerator(config.get());
122
140
  await generator.generateToFile();
123
141
  console.log('Context generated successfully');
124
- console.log('Tip: Add parseme as a git hook to keep context auto-updated! See README for setup instructions.');
125
- const shouldShowReadmeSuggestion = interactiveConfig.readmeSuggestion !== false;
126
- if (shouldShowReadmeSuggestion) {
127
- console.log('Tip: Add this section to your README.md to help AI agents find the context:');
128
- console.log('');
129
- console.log('## For AI Assistants');
130
- console.log('This project includes AI-optimized documentation:');
131
- console.log('- `PARSEME.md` - Main project context and overview');
132
- console.log('- `parseme-context/` - Detailed JSON files with code structure, dependencies, and git info');
133
- console.log('');
134
- }
135
142
  }
136
143
  catch (error) {
144
+ if (error instanceof Error && error.message.includes('No configuration file found')) {
145
+ console.error(error.message);
146
+ process.exit(1);
147
+ }
137
148
  console.error('Failed to generate context:', error);
138
- console.log('Run "parseme init" to create a configuration file');
139
149
  process.exit(1);
140
150
  }
141
151
  })();
@@ -4,5 +4,4 @@ interface PromptOptions {
4
4
  choices?: string[];
5
5
  }
6
6
  export declare function prompt(options: PromptOptions): Promise<string>;
7
- export declare function confirmPrompt(message: string, defaultValue?: boolean): Promise<boolean>;
8
7
  export {};
@@ -31,12 +31,3 @@ export async function prompt(options) {
31
31
  });
32
32
  });
33
33
  }
34
- export async function confirmPrompt(message, defaultValue = true) {
35
- const answer = await prompt({
36
- message,
37
- defaultValue: defaultValue ? 'y' : 'n',
38
- choices: ['y', 'n', 'yes', 'no'],
39
- });
40
- const normalized = answer.toLowerCase();
41
- return normalized === 'y' || normalized === 'yes';
42
- }
@@ -15,11 +15,12 @@ export class ASTAnalyzer {
15
15
  this.ig = ignore();
16
16
  const configData = this.config.get();
17
17
  this.ig.add(configData.excludePatterns || []);
18
- this.patternDetector = new PatternDetector(config);
18
+ this.patternDetector = new PatternDetector();
19
19
  }
20
20
  async analyzeProject(rootDir) {
21
21
  const configData = this.config.get();
22
- const patterns = configData.includePatterns || ['**/*.ts', '**/*.js'];
22
+ const fileTypes = configData.analyzeFileTypes || ['ts', 'tsx', 'js', 'jsx'];
23
+ const patterns = fileTypes.map((type) => `**/*.${type}`);
23
24
  const files = await glob(patterns, {
24
25
  cwd: rootDir,
25
26
  absolute: true,
@@ -58,7 +59,7 @@ export class ASTAnalyzer {
58
59
  const patterns = this.patternDetector.analyzePatterns(ast, relativePath, content);
59
60
  const analysis = {
60
61
  path: relativePath,
61
- type: this.determineFileType(relativePath, content, patterns),
62
+ type: this.determineFileType(relativePath, patterns),
62
63
  exports: [],
63
64
  imports: [],
64
65
  functions: [],
@@ -116,8 +117,8 @@ export class ASTAnalyzer {
116
117
  });
117
118
  return analysis;
118
119
  }
119
- catch (error) {
120
- console.warn(`Failed to parse ${relativePath}:`, error);
120
+ catch {
121
+ console.warn(`Failed to parse ${relativePath}`);
121
122
  return null;
122
123
  }
123
124
  }
@@ -144,7 +145,7 @@ export class ASTAnalyzer {
144
145
  ],
145
146
  });
146
147
  }
147
- determineFileType(relativePath, content, patterns) {
148
+ determineFileType(relativePath, patterns) {
148
149
  // Use pattern analysis to determine file type dynamically
149
150
  if (patterns.endpoints.length > 0) {
150
151
  return 'route';
@@ -1,8 +1,5 @@
1
- import type { ParsemeConfig } from '../config.js';
2
1
  import type { ProjectInfo, FrameworkInfo } from '../types.js';
3
2
  export declare class FrameworkDetector {
4
- private readonly config;
5
- constructor(config: ParsemeConfig);
6
3
  detect(projectInfo: ProjectInfo): Promise<FrameworkInfo>;
7
4
  private detectExpress;
8
5
  private detectFastify;
@@ -1,8 +1,4 @@
1
1
  export class FrameworkDetector {
2
- config;
3
- constructor(config) {
4
- this.config = config;
5
- }
6
2
  async detect(projectInfo) {
7
3
  const deps = { ...projectInfo.dependencies, ...projectInfo.devDependencies };
8
4
  // Check for frameworks in dependencies
@@ -1,8 +1,5 @@
1
- import type { ParsemeConfig } from '../config.js';
2
1
  import type { GitInfo } from '../types.js';
3
2
  export declare class GitAnalyzer {
4
- private readonly config;
5
- constructor(config: ParsemeConfig);
6
3
  analyze(rootDir: string): Promise<GitInfo | null>;
7
4
  private getCurrentBranch;
8
5
  private getLastCommit;
@@ -2,10 +2,6 @@ import { exec } from 'child_process';
2
2
  import { promisify } from 'util';
3
3
  const execAsync = promisify(exec);
4
4
  export class GitAnalyzer {
5
- config;
6
- constructor(config) {
7
- this.config = config;
8
- }
9
5
  async analyze(rootDir) {
10
6
  try {
11
7
  // Check if this is a git repository
@@ -1,5 +1,4 @@
1
1
  import * as t from '@babel/types';
2
- import type { ParsemeConfig } from '../config.js';
3
2
  import type { ComponentInfo, RouteInfo } from '../types.js';
4
3
  export interface PatternAnalysis {
5
4
  endpoints: EndpointInfo[];
@@ -51,8 +50,6 @@ export interface UtilityInfo {
51
50
  type: 'helper' | 'lib' | 'hook' | 'composable';
52
51
  }
53
52
  export declare class PatternDetector {
54
- private readonly config;
55
- constructor(config: ParsemeConfig);
56
53
  analyzePatterns(ast: t.File, filePath: string, _content: string): PatternAnalysis;
57
54
  private hasJSXReturn;
58
55
  }
@@ -1,10 +1,6 @@
1
1
  import traverse from '@babel/traverse';
2
2
  import * as t from '@babel/types';
3
3
  export class PatternDetector {
4
- config;
5
- constructor(config) {
6
- this.config = config;
7
- }
8
4
  analyzePatterns(ast, filePath, _content) {
9
5
  const analysis = {
10
6
  endpoints: [],
@@ -17,25 +13,6 @@ export class PatternDetector {
17
13
  };
18
14
  // Use a single traverse call to detect all patterns
19
15
  traverse.default(ast, {
20
- // Detect Express-style routes: app.get(), router.post(), etc.
21
- CallExpression: (path) => {
22
- if (t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.property)) {
23
- const methodName = path.node.callee.property.name;
24
- const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'all'];
25
- if (httpMethods.includes(methodName)) {
26
- const routeArg = path.node.arguments[0];
27
- const routePath = t.isStringLiteral(routeArg) ? routeArg.value : '/';
28
- analysis.endpoints.push({
29
- method: methodName.toUpperCase(),
30
- path: routePath,
31
- handler: 'callback',
32
- file: filePath,
33
- line: path.node.loc?.start.line || 0,
34
- type: 'rest',
35
- });
36
- }
37
- }
38
- },
39
16
  // Detect decorator-based routes: @Get(), @Post(), etc.
40
17
  ClassMethod: (path) => {
41
18
  const decorators = path.node.decorators;
@@ -63,6 +40,58 @@ export class PatternDetector {
63
40
  });
64
41
  }
65
42
  },
43
+ // Detect Express/Router-style routes: app.get(), router.post(), etc.
44
+ CallExpression: (path) => {
45
+ const { callee, arguments: args } = path.node;
46
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
47
+ const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head'];
48
+ const methodName = callee.property.name;
49
+ if (httpMethods.includes(methodName) && args.length >= 2) {
50
+ const routePath = args[0];
51
+ if (t.isStringLiteral(routePath)) {
52
+ analysis.endpoints.push({
53
+ method: methodName.toUpperCase(),
54
+ path: routePath.value,
55
+ handler: 'anonymous',
56
+ file: filePath,
57
+ line: path.node.loc?.start.line || 0,
58
+ type: 'rest',
59
+ framework: 'express',
60
+ });
61
+ }
62
+ }
63
+ }
64
+ },
65
+ // Detect TypeScript interfaces and type aliases
66
+ TSInterfaceDeclaration: (path) => {
67
+ const interfaceName = path.node.id.name;
68
+ const fields = path.node.body.body
69
+ .filter((member) => t.isTSPropertySignature(member))
70
+ .map((member) => {
71
+ if (t.isIdentifier(member.key)) {
72
+ return member.key.name;
73
+ }
74
+ return '';
75
+ })
76
+ .filter((name) => name !== '');
77
+ analysis.models.push({
78
+ name: interfaceName,
79
+ file: filePath,
80
+ line: path.node.loc?.start.line || 0,
81
+ fields,
82
+ type: 'interface',
83
+ });
84
+ },
85
+ TSTypeAliasDeclaration: (path) => {
86
+ const typeName = path.node.id.name;
87
+ analysis.models.push({
88
+ name: typeName,
89
+ file: filePath,
90
+ line: path.node.loc?.start.line || 0,
91
+ fields: [],
92
+ type: 'type',
93
+ });
94
+ },
66
95
  // Detect React components
67
96
  FunctionDeclaration: (path) => {
68
97
  const functionName = path.node.id?.name;
@@ -141,30 +170,6 @@ export class PatternDetector {
141
170
  });
142
171
  }
143
172
  },
144
- // Detect TypeScript interfaces
145
- TSInterfaceDeclaration: (path) => {
146
- const interfaceName = path.node.id.name;
147
- const fields = path.node.body.body
148
- .filter((member) => t.isTSPropertySignature(member) && t.isIdentifier(member.key))
149
- .map((member) => member.key.name);
150
- analysis.models.push({
151
- name: interfaceName,
152
- file: filePath,
153
- line: path.node.loc?.start.line || 0,
154
- fields,
155
- type: 'interface',
156
- });
157
- },
158
- // Detect type aliases
159
- TSTypeAliasDeclaration: (path) => {
160
- analysis.models.push({
161
- name: path.node.id.name,
162
- file: filePath,
163
- line: path.node.loc?.start.line || 0,
164
- fields: [],
165
- type: 'type',
166
- });
167
- },
168
173
  });
169
174
  return analysis;
170
175
  }
@@ -2,6 +2,7 @@ import type { ParsemeConfig } from '../config.js';
2
2
  import type { ProjectInfo } from '../types.js';
3
3
  export declare class ProjectAnalyzer {
4
4
  private readonly config;
5
+ private readonly ig;
5
6
  constructor(config: ParsemeConfig);
6
7
  analyze(rootDir: string): Promise<ProjectInfo>;
7
8
  private detectProjectType;
@@ -11,4 +12,5 @@ export declare class ProjectAnalyzer {
11
12
  private detectEntryPoints;
12
13
  private detectOutputTargets;
13
14
  private hasAppDependencies;
15
+ getAllProjectFiles(rootDir: string): Promise<string[]>;
14
16
  }
@@ -1,9 +1,14 @@
1
1
  import { readFile, access, readdir, stat } from 'fs/promises';
2
- import { join, basename } from 'path';
2
+ import { join, basename, relative } from 'path';
3
+ import ignore from 'ignore';
3
4
  export class ProjectAnalyzer {
4
5
  config;
6
+ ig;
5
7
  constructor(config) {
6
8
  this.config = config;
9
+ this.ig = ignore();
10
+ const configData = this.config.get();
11
+ this.ig.add(configData.excludePatterns || []);
7
12
  }
8
13
  async analyze(rootDir) {
9
14
  const packageJsonPath = join(rootDir, 'package.json');
@@ -30,7 +35,7 @@ export class ProjectAnalyzer {
30
35
  name: basename(rootDir),
31
36
  type: await this.detectProjectType(rootDir),
32
37
  category: 'unknown',
33
- packageManager: 'npm',
38
+ packageManager: 'unknown',
34
39
  dependencies: {},
35
40
  devDependencies: {},
36
41
  scripts: {},
@@ -41,7 +46,7 @@ export class ProjectAnalyzer {
41
46
  }
42
47
  async detectProjectType(rootDir) {
43
48
  try {
44
- const files = await this.getFilesRecursive(rootDir, 2); // Only check 2 levels deep
49
+ const files = await this.getFilesRecursive(rootDir, rootDir, 2); // Only check 2 levels deep
45
50
  const tsFiles = files.filter((f) => f.endsWith('.ts') && !f.endsWith('.d.ts'));
46
51
  const jsFiles = files.filter((f) => f.endsWith('.js'));
47
52
  if (tsFiles.length > 0 && jsFiles.length > 0) {
@@ -74,7 +79,7 @@ export class ProjectAnalyzer {
74
79
  }
75
80
  return 'npm';
76
81
  }
77
- async getFilesRecursive(dir, maxDepth) {
82
+ async getFilesRecursive(dir, rootDir, maxDepth) {
78
83
  if (maxDepth <= 0) {
79
84
  return [];
80
85
  }
@@ -83,12 +88,17 @@ export class ProjectAnalyzer {
83
88
  const files = [];
84
89
  for (const entry of entries) {
85
90
  const fullPath = join(dir, entry);
91
+ const relativePath = relative(rootDir, fullPath);
92
+ // Skip if ignored by exclude patterns
93
+ if (this.ig.ignores(relativePath)) {
94
+ continue;
95
+ }
86
96
  const stats = await stat(fullPath);
87
97
  if (stats.isFile()) {
88
98
  files.push(fullPath);
89
99
  }
90
100
  else if (stats.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
91
- files.push(...(await this.getFilesRecursive(fullPath, maxDepth - 1)));
101
+ files.push(...(await this.getFilesRecursive(fullPath, rootDir, maxDepth - 1)));
92
102
  }
93
103
  }
94
104
  return files;
@@ -202,4 +212,8 @@ export class ProjectAnalyzer {
202
212
  ];
203
213
  return appIndicators.some((indicator) => deps[indicator]);
204
214
  }
215
+ async getAllProjectFiles(rootDir) {
216
+ const allFiles = await this.getFilesRecursive(rootDir, rootDir, Infinity);
217
+ return allFiles.map((file) => relative(rootDir, file));
218
+ }
205
219
  }
@@ -0,0 +1,20 @@
1
+ import type { ParsemeConfigFile } from './types.js';
2
+ export declare class ParsemeConfig {
3
+ private readonly config;
4
+ private readonly userConfig;
5
+ constructor(config?: Partial<ParsemeConfigFile>);
6
+ static fromFileWithOptions(configPath?: string, cliOptions?: Partial<ParsemeConfigFile>, options?: {
7
+ showWarnings?: boolean;
8
+ }): Promise<ParsemeConfig>;
9
+ static fromFile(configPath?: string, options?: {
10
+ showWarnings?: boolean;
11
+ throwOnNotFound?: boolean;
12
+ }): Promise<ParsemeConfig>;
13
+ private mergeWithDefaults;
14
+ get(): ParsemeConfigFile;
15
+ save(path?: string): Promise<void>;
16
+ private generateJSConfig;
17
+ private generateTSConfig;
18
+ private mergeExcludePatterns;
19
+ private readGitignorePatterns;
20
+ }