@parseme/cli 0.0.3 → 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.
Files changed (47) hide show
  1. package/README.md +182 -187
  2. package/dist/cli/cli.js +144 -0
  3. package/dist/{analyzers → core/analyzers}/ast-analyzer.d.ts +1 -1
  4. package/dist/{analyzers → core/analyzers}/ast-analyzer.js +14 -27
  5. package/dist/core/analyzers/framework-detector.d.ts +7 -0
  6. package/dist/core/analyzers/framework-detector.js +165 -0
  7. package/dist/{analyzers → core/analyzers}/pattern-detector.d.ts +2 -5
  8. package/dist/{analyzers → core/analyzers}/pattern-detector.js +134 -49
  9. package/dist/{analyzers → core/analyzers}/project-analyzer.d.ts +2 -0
  10. package/dist/{analyzers → core/analyzers}/project-analyzer.js +12 -9
  11. package/dist/core/config.d.ts +19 -0
  12. package/dist/{config.js → core/config.js} +79 -91
  13. package/dist/{builders → core}/context-builder.d.ts +4 -11
  14. package/dist/core/context-builder.js +225 -0
  15. package/dist/{generator.d.ts → core/generator.d.ts} +0 -3
  16. package/dist/{generator.js → core/generator.js} +12 -17
  17. package/dist/core/types/analyzer-types.d.ts +38 -0
  18. package/dist/core/types/analyzer-types.js +2 -0
  19. package/dist/core/types/config-types.d.ts +36 -0
  20. package/dist/core/types/config-types.js +2 -0
  21. package/dist/core/types/generator-types.d.ts +6 -0
  22. package/dist/core/types/generator-types.js +2 -0
  23. package/dist/core/types/index.d.ts +4 -0
  24. package/dist/core/types/index.js +5 -0
  25. package/dist/core/types/project-types.d.ts +30 -0
  26. package/dist/core/types/project-types.js +2 -0
  27. package/dist/core/types.d.ts +1 -0
  28. package/dist/core/types.js +2 -0
  29. package/dist/index.d.ts +3 -4
  30. package/dist/index.js +2 -2
  31. package/dist/utils/file-collector.d.ts +23 -0
  32. package/dist/utils/file-collector.js +61 -0
  33. package/dist/utils/file-filter.d.ts +30 -0
  34. package/dist/utils/file-filter.js +99 -0
  35. package/dist/{analyzers/git-analyzer.d.ts → utils/git.d.ts} +1 -4
  36. package/dist/{analyzers/git-analyzer.js → utils/git.js} +0 -4
  37. package/dist/{prompt.d.ts → utils/prompt.d.ts} +0 -1
  38. package/dist/{prompt.js → utils/prompt.js} +0 -9
  39. package/package.json +12 -8
  40. package/dist/analyzers/framework-detector.d.ts +0 -12
  41. package/dist/analyzers/framework-detector.js +0 -180
  42. package/dist/builders/context-builder.js +0 -386
  43. package/dist/cli.js +0 -145
  44. package/dist/config.d.ts +0 -44
  45. package/dist/types.d.ts +0 -84
  46. package/dist/types.js +0 -2
  47. /package/dist/{cli.d.ts → cli/cli.d.ts} +0 -0
@@ -1,9 +1,12 @@
1
1
  import { readFile, access, readdir, stat } from 'fs/promises';
2
2
  import { join, basename } from 'path';
3
+ import { FileCollector } from '../../utils/file-collector.js';
3
4
  export class ProjectAnalyzer {
4
5
  config;
6
+ fileCollector;
5
7
  constructor(config) {
6
8
  this.config = config;
9
+ this.fileCollector = new FileCollector(config);
7
10
  }
8
11
  async analyze(rootDir) {
9
12
  const packageJsonPath = join(rootDir, 'package.json');
@@ -30,7 +33,7 @@ export class ProjectAnalyzer {
30
33
  name: basename(rootDir),
31
34
  type: await this.detectProjectType(rootDir),
32
35
  category: 'unknown',
33
- packageManager: 'npm',
36
+ packageManager: 'unknown',
34
37
  dependencies: {},
35
38
  devDependencies: {},
36
39
  scripts: {},
@@ -41,7 +44,7 @@ export class ProjectAnalyzer {
41
44
  }
42
45
  async detectProjectType(rootDir) {
43
46
  try {
44
- const files = await this.getFilesRecursive(rootDir, 2); // Only check 2 levels deep
47
+ const files = await this.getFilesRecursive(rootDir, rootDir, 2); // Only check 2 levels deep
45
48
  const tsFiles = files.filter((f) => f.endsWith('.ts') && !f.endsWith('.d.ts'));
46
49
  const jsFiles = files.filter((f) => f.endsWith('.js'));
47
50
  if (tsFiles.length > 0 && jsFiles.length > 0) {
@@ -74,7 +77,7 @@ export class ProjectAnalyzer {
74
77
  }
75
78
  return 'npm';
76
79
  }
77
- async getFilesRecursive(dir, maxDepth) {
80
+ async getFilesRecursive(dir, rootDir, maxDepth) {
78
81
  if (maxDepth <= 0) {
79
82
  return [];
80
83
  }
@@ -88,7 +91,7 @@ export class ProjectAnalyzer {
88
91
  files.push(fullPath);
89
92
  }
90
93
  else if (stats.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
91
- files.push(...(await this.getFilesRecursive(fullPath, maxDepth - 1)));
94
+ files.push(...(await this.getFilesRecursive(fullPath, rootDir, maxDepth - 1)));
92
95
  }
93
96
  }
94
97
  return files;
@@ -132,11 +135,7 @@ export class ProjectAnalyzer {
132
135
  return 'frontend-web';
133
136
  }
134
137
  // Check for backend frameworks
135
- if (deps['express'] ||
136
- deps['fastify'] ||
137
- deps['@nestjs/core'] ||
138
- deps['koa'] ||
139
- deps['@hapi/hapi']) {
138
+ if (deps['express'] || deps['fastify'] || deps['@nestjs/core']) {
140
139
  return 'backend-api';
141
140
  }
142
141
  // Check if it's a library (has main/module/exports but no app dependencies)
@@ -202,4 +201,8 @@ export class ProjectAnalyzer {
202
201
  ];
203
202
  return appIndicators.some((indicator) => deps[indicator]);
204
203
  }
204
+ async getAllProjectFiles(rootDir) {
205
+ const result = await this.fileCollector.getAllProjectFiles(rootDir);
206
+ return result.files;
207
+ }
205
208
  }
@@ -0,0 +1,19 @@
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
+ }
@@ -1,30 +1,37 @@
1
- import { readFileSync, existsSync } from 'fs';
1
+ import { existsSync } from 'fs';
2
2
  import { readFile, writeFile } from 'fs/promises';
3
3
  import { join, extname } from 'path';
4
4
  export class ParsemeConfig {
5
5
  config;
6
+ userConfig;
6
7
  constructor(config = {}) {
8
+ this.userConfig = { ...config }; // Store original user config
7
9
  this.config = this.mergeWithDefaults(config);
8
10
  }
9
- static async fromFileWithOptions(configPath, cliOptions = {}) {
10
- const configFromFile = await ParsemeConfig.fromFile(configPath);
11
+ static async fromFileWithOptions(configPath, cliOptions = {}, options = { showWarnings: true }) {
12
+ const configFromFile = await ParsemeConfig.fromFile(configPath, options);
11
13
  const mergedConfig = {
12
14
  ...configFromFile.get(),
13
15
  ...cliOptions, // CLI options take priority
14
16
  };
15
17
  return new ParsemeConfig(mergedConfig);
16
18
  }
17
- static async fromFile(configPath) {
19
+ static async fromFile(configPath, options = {
20
+ showWarnings: true,
21
+ throwOnNotFound: false,
22
+ }) {
18
23
  const defaultPaths = [
19
- 'parseme.config.ts',
20
- 'parseme.config.js',
21
24
  'parseme.config.json',
22
- '.parsemerc.ts',
23
- '.parsemerc.js',
24
25
  '.parsemerc.json',
25
26
  '.parsemerc',
27
+ 'parseme.config.ts',
28
+ '.parsemerc.ts',
29
+ 'parseme.config.js',
30
+ '.parsemerc.js',
26
31
  ];
27
32
  const paths = configPath ? [configPath] : defaultPaths;
33
+ let tsWarning = null;
34
+ let configLoadError = null;
28
35
  for (const path of paths) {
29
36
  try {
30
37
  const ext = extname(path);
@@ -32,17 +39,18 @@ export class ParsemeConfig {
32
39
  // Dynamic import for JS/TS config files
33
40
  const fullPath = path.startsWith('/') ? path : join(process.cwd(), path);
34
41
  if (ext === '.ts') {
35
- // For TypeScript files, try to import directly first
36
- // This works if the user has transpiled their TS config to JS or is using tsx/ts-node
37
- try {
38
- const module = await import(fullPath);
39
- const config = module.default || module;
40
- return new ParsemeConfig(config);
41
- }
42
- catch {
43
- // If direct import fails, suggest using .js instead
44
- console.warn(`Could not load TypeScript config file: ${path}`);
45
- console.warn(`Consider using a .js config file or ensure tsx/ts-node is available`);
42
+ // For TypeScript files, check if file exists first
43
+ if (existsSync(fullPath)) {
44
+ try {
45
+ const module = await import(fullPath);
46
+ const config = module.default || module;
47
+ return new ParsemeConfig(config);
48
+ }
49
+ catch (error) {
50
+ // File exists but can't be loaded - save warning
51
+ tsWarning = path;
52
+ configLoadError = { path, error: error };
53
+ }
46
54
  }
47
55
  }
48
56
  else {
@@ -54,20 +62,56 @@ export class ParsemeConfig {
54
62
  }
55
63
  else {
56
64
  // JSON config files
57
- const content = await readFile(path, 'utf-8');
65
+ const fullPath = path.startsWith('/') ? path : join(process.cwd(), path);
66
+ const content = await readFile(fullPath, 'utf-8');
58
67
  const config = JSON.parse(content);
59
68
  return new ParsemeConfig(config);
60
69
  }
61
70
  }
62
- catch {
71
+ catch (error) {
72
+ // If file exists, save the error
73
+ const fullPath = path.startsWith('/') ? path : join(process.cwd(), path);
74
+ if (existsSync(fullPath)) {
75
+ configLoadError = { path, error: error };
76
+ }
63
77
  // Continue to next path
64
78
  }
65
79
  }
80
+ // Handle case when config file was found but couldn't be loaded
81
+ if (configLoadError) {
82
+ const { path, error } = configLoadError;
83
+ if (options.throwOnNotFound) {
84
+ throw new Error(`Configuration file "${path}" found but failed to load: ${error.message}`);
85
+ }
86
+ if (options.showWarnings) {
87
+ console.warn(`Configuration file "${path}" found but failed to load: ${error.message}`);
88
+ }
89
+ }
90
+ else {
91
+ // Handle case when no config found at all
92
+ if (options.throwOnNotFound) {
93
+ throw new Error('No configuration file found. Run "parseme init" to create one.');
94
+ }
95
+ if (options.showWarnings) {
96
+ console.warn('No configuration file found. Run "parseme init" to create one.');
97
+ }
98
+ }
99
+ if (tsWarning && !configLoadError && options.showWarnings) {
100
+ console.warn(`Could not load TypeScript config file: ${tsWarning}`);
101
+ console.warn(`Consider using a .js config file or ensure tsx/ts-node is available`);
102
+ }
66
103
  // Return default config if no file found
67
104
  return new ParsemeConfig();
68
105
  }
69
106
  mergeWithDefaults(config) {
70
107
  const rootDir = config.rootDir || process.cwd();
108
+ const supportedFileTypes = ['ts', 'tsx', 'js', 'jsx'];
109
+ // Validate analyzeFileTypes
110
+ const fileTypes = config.analyzeFileTypes || ['ts', 'tsx', 'js', 'jsx'];
111
+ const invalidTypes = fileTypes.filter((type) => !supportedFileTypes.includes(type));
112
+ if (invalidTypes.length > 0) {
113
+ throw new Error(`Invalid file types: ${invalidTypes.join(', ')}. Supported types are: ${supportedFileTypes.join(', ')}`);
114
+ }
71
115
  return {
72
116
  // Output
73
117
  outputPath: config.outputPath || 'PARSEME.md',
@@ -76,19 +120,10 @@ export class ParsemeConfig {
76
120
  rootDir,
77
121
  maxDepth: config.maxDepth || 10,
78
122
  excludePatterns: this.mergeExcludePatterns(config.excludePatterns, rootDir),
79
- includePatterns: config.includePatterns || [
80
- 'src/**/*.ts',
81
- 'src/**/*.js',
82
- 'src/**/*.tsx',
83
- 'src/**/*.jsx',
84
- 'lib/**/*.ts',
85
- 'lib/**/*.js',
86
- 'package.json',
87
- 'tsconfig.json',
88
- 'README.md',
89
- ],
123
+ analyzeFileTypes: fileTypes,
90
124
  // Git
91
125
  includeGitInfo: config.includeGitInfo ?? true,
126
+ useGitForFiles: config.useGitForFiles ?? true,
92
127
  // Sections
93
128
  sections: {
94
129
  overview: true,
@@ -109,10 +144,7 @@ export class ParsemeConfig {
109
144
  },
110
145
  // Size limits
111
146
  limits: {
112
- maxLinesPerFile: config.limits?.maxLinesPerFile ?? 1000,
113
- maxCharsPerFile: config.limits?.maxCharsPerFile ?? 50000, // ~15k tokens
114
- maxFilesPerContext: config.limits?.maxFilesPerContext ?? 20,
115
- truncateStrategy: config.limits?.truncateStrategy ?? 'truncate',
147
+ maxFilesPerContext: config.limits?.maxFilesPerContext ?? 5000,
116
148
  },
117
149
  };
118
150
  }
@@ -132,73 +164,29 @@ export class ParsemeConfig {
132
164
  await writeFile(path, configContent);
133
165
  }
134
166
  else {
135
- // Generate JSON config file
136
- await writeFile(path, JSON.stringify(this.config, null, 2));
167
+ // Generate JSON config file - only save user config, not defaults
168
+ await writeFile(path, JSON.stringify(this.userConfig, null, 2));
137
169
  }
138
170
  }
139
171
  generateJSConfig() {
172
+ // Only export user-specified config, not defaults
140
173
  return `/** @type {import('parseme').ParsemeConfigFile} */
141
- export default ${JSON.stringify(this.config, null, 2)};
174
+ const config = ${JSON.stringify(this.userConfig, null, 2)};
175
+
176
+ export default config;
142
177
  `;
143
178
  }
144
179
  generateTSConfig() {
180
+ // Only export user-specified config, not defaults
145
181
  return `import type { ParsemeConfigFile } from 'parseme';
146
182
 
147
- const config: ParsemeConfigFile = ${JSON.stringify(this.config, null, 2)};
183
+ const config: ParsemeConfigFile = ${JSON.stringify(this.userConfig, null, 2)};
148
184
 
149
185
  export default config;
150
186
  `;
151
187
  }
152
- mergeExcludePatterns(configPatterns, rootDir) {
153
- const defaultPatterns = [
154
- 'node_modules/**',
155
- 'dist/**',
156
- 'build/**',
157
- 'coverage/**',
158
- '.git/**',
159
- '**/*.log',
160
- '**/*.tmp',
161
- '**/.DS_Store',
162
- '**/.*',
163
- ];
164
- // Priority: Config patterns > .gitignore patterns > Default patterns
165
- if (configPatterns) {
166
- return configPatterns;
167
- }
168
- // Try to read .gitignore patterns
169
- const gitignorePatterns = this.readGitignorePatterns(rootDir);
170
- if (gitignorePatterns.length > 0) {
171
- // Merge gitignore patterns with critical defaults
172
- const criticalDefaults = ['node_modules/**', '.git/**'];
173
- return [...new Set([...criticalDefaults, ...gitignorePatterns])];
174
- }
175
- return defaultPatterns;
176
- }
177
- readGitignorePatterns(rootDir) {
178
- try {
179
- const gitignorePath = join(rootDir, '.gitignore');
180
- if (!existsSync(gitignorePath)) {
181
- return [];
182
- }
183
- const gitignoreContent = readFileSync(gitignorePath, 'utf-8');
184
- return gitignoreContent
185
- .split('\n')
186
- .map((line) => line.trim())
187
- .filter((line) => line && !line.startsWith('#'))
188
- .map((pattern) => {
189
- // Convert gitignore patterns to glob patterns
190
- if (pattern.endsWith('/')) {
191
- return pattern + '**';
192
- }
193
- if (!pattern.includes('/') && !pattern.includes('*')) {
194
- // Convert simple names to match directory patterns
195
- return pattern + '/**';
196
- }
197
- return pattern;
198
- });
199
- }
200
- catch {
201
- return [];
202
- }
188
+ mergeExcludePatterns(configPatterns, _rootDir) {
189
+ // Only use config patterns - git ignore is now handled by FileFilterService
190
+ return configPatterns || [];
203
191
  }
204
192
  }
@@ -1,8 +1,9 @@
1
- import type { ParsemeConfig } from '../config.js';
2
- import type { ContextOutput, ProjectInfo, FileAnalysis, GitInfo, GeneratorOptions } from '../types.js';
1
+ import type { ParsemeConfig } from './config.js';
2
+ import type { ContextOutput, ProjectInfo, FileAnalysis, GitInfo, GeneratorOptions } from './types.js';
3
3
  interface BuildContext {
4
4
  projectInfo: ProjectInfo;
5
5
  fileAnalyses: FileAnalysis[];
6
+ allFiles: string[];
6
7
  gitInfo?: GitInfo | null;
7
8
  options: GeneratorOptions;
8
9
  contextDir?: string;
@@ -11,20 +12,12 @@ interface BuildContext {
11
12
  export declare class ContextBuilder {
12
13
  private readonly config;
13
14
  constructor(config: ParsemeConfig);
14
- private truncateContent;
15
- private splitContentByLines;
16
- private splitContentByChars;
17
15
  build(context: BuildContext): ContextOutput;
18
16
  private buildMultiFile;
19
17
  private buildHeader;
20
18
  private buildProjectOverview;
21
- private buildFrameworkSection;
22
- private buildArchitectureSection;
23
- private buildRoutesSection;
24
- private buildFileStructureSection;
25
- private buildDependenciesSection;
19
+ private formatFrameworksList;
26
20
  private buildGitSection;
27
- private buildFooter;
28
21
  private buildSummarySection;
29
22
  private buildFilesList;
30
23
  private buildDetailedStructure;
@@ -0,0 +1,225 @@
1
+ import { relative, dirname } from 'path';
2
+ export class ContextBuilder {
3
+ config;
4
+ constructor(config) {
5
+ this.config = config;
6
+ }
7
+ build(context) {
8
+ return this.buildMultiFile(context);
9
+ }
10
+ buildMultiFile(context) {
11
+ const { projectInfo, fileAnalyses, gitInfo, contextDir, outputPath } = context;
12
+ // Files are now pre-limited in their respective analyzers, so no need to limit here
13
+ // Calculate the relative path for the link in markdown
14
+ let linkPath = 'parseme-context';
15
+ if (contextDir && outputPath) {
16
+ const outputDir = dirname(outputPath);
17
+ if (contextDir.startsWith('/')) {
18
+ // Absolute path: calculate relative path from output file to context dir
19
+ linkPath = relative(outputDir, contextDir);
20
+ }
21
+ else {
22
+ // Relative path: use as-is since it's relative to the output file's directory
23
+ linkPath = contextDir;
24
+ }
25
+ }
26
+ else if (contextDir) {
27
+ // Fallback: use contextDir as-is if no outputPath provided
28
+ linkPath = contextDir;
29
+ }
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) +
41
+ '\n\n\n' +
42
+ this.buildProjectOverview(projectInfo) +
43
+ '\n\n\n' +
44
+ this.buildSummarySection(linkPath, hasRoutes) +
45
+ '\n\n\n' +
46
+ (gitInfo ? this.buildGitSection(gitInfo) : '');
47
+ const contextFiles = {
48
+ structure: '',
49
+ };
50
+ // Files list (markdown) - all files in project, not just analyzed ones
51
+ contextFiles.files = this.buildFilesList(context.allFiles);
52
+ // Detailed structure (JSON with AST)
53
+ contextFiles.structure = this.buildDetailedStructure(fileAnalyses, hasRoutes);
54
+ // Routes documentation (only if routes exist)
55
+ if (hasRoutes) {
56
+ contextFiles.routes = this.buildDetailedRoutes(routes);
57
+ }
58
+ // Git information
59
+ if (gitInfo?.diffStat?.length && gitInfo.diffStat.length > 0) {
60
+ contextFiles.gitDiff = this.buildDetailedGit(gitInfo);
61
+ }
62
+ return {
63
+ parseme: mainContent,
64
+ context: contextFiles,
65
+ };
66
+ }
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
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.
77
+
78
+ **Usage Instructions for AI Agents:**
79
+ 1. Read this PARSEME.md file completely first before accessing individual project files
80
+ 2. Basic project information, script availability and dependency information provides basic understanding of code base and tech stack without checking package.json
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}`;
84
+ }
85
+ buildProjectOverview(projectInfo) {
86
+ let content = `## Basic Project Information
87
+
88
+ **Project:** ${projectInfo.name}${projectInfo.version ? ` v${projectInfo.version}` : ''}
89
+ **Description:** ${projectInfo.description || 'No description available.'}
90
+ **Type:** ${projectInfo.type} project
91
+ **Package Manager:** ${projectInfo.packageManager}
92
+ **Framework:** ${this.formatFrameworksList(projectInfo.frameworks)}`;
93
+ // Add main entry point if available
94
+ if (projectInfo.entryPoints && projectInfo.entryPoints.length > 0) {
95
+ content += `\n**Main Entry Point:** ${projectInfo.entryPoints[0]}`;
96
+ }
97
+ content += '\n';
98
+ // Add dependencies
99
+ const deps = Object.keys(projectInfo.dependencies);
100
+ if (deps.length > 0) {
101
+ content += '\n\n### Dependencies\n';
102
+ deps.forEach((dep) => {
103
+ content += `- ${dep}\n`;
104
+ });
105
+ }
106
+ // Add available scripts
107
+ if (projectInfo.scripts && Object.keys(projectInfo.scripts).length > 0) {
108
+ content += '\n### Available Scripts\n';
109
+ Object.entries(projectInfo.scripts).forEach(([name, script]) => {
110
+ content += `- **${name}**: \`${script}\`\n`;
111
+ });
112
+ }
113
+ return content;
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
+ }
125
+ buildGitSection(gitInfo) {
126
+ const base = `## Git Information
127
+
128
+ **State when PARSEME.md and all linked files were automatically generated:**
129
+
130
+ - **Branch:** ${gitInfo.branch}
131
+ - **Commit:** ${gitInfo.lastCommit}${gitInfo.origin ? `\n- **Origin:** ${gitInfo.origin}` : ''}
132
+
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).
136
+
137
+ **AI Agent Command:** To check for changes since generation, run:
138
+ \`\`\`bash
139
+ git diff --stat
140
+ \`\`\`
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;
144
+ }
145
+ buildSummarySection(linkPath, hasRoutes) {
146
+ let content = `## Project Files
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.
148
+
149
+
150
+ ## Project Structure & AST
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).`;
155
+ }
156
+ return content;
157
+ }
158
+ buildFilesList(allFiles) {
159
+ let content = `# Project Files\n`;
160
+ allFiles.forEach((file) => {
161
+ content += `- ${file}\n`;
162
+ });
163
+ return content;
164
+ }
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);
187
+ }
188
+ buildDetailedRoutes(routes) {
189
+ return JSON.stringify(routes, null, 2);
190
+ }
191
+ buildDetailedDependencies(projectInfo) {
192
+ const jsonContent = JSON.stringify({
193
+ dependencies: projectInfo.dependencies,
194
+ packageManager: projectInfo.packageManager,
195
+ version: projectInfo.version,
196
+ }, null, 2);
197
+ return jsonContent;
198
+ }
199
+ buildDetailedFramework(frameworks) {
200
+ if (!frameworks || frameworks.length === 0) {
201
+ return '';
202
+ }
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
+ });
217
+ return content;
218
+ }
219
+ buildDetailedGit(gitInfo) {
220
+ let content = `# Git Diff Statistics
221
+ `;
222
+ content += gitInfo.diffStat;
223
+ return content;
224
+ }
225
+ }
@@ -1,4 +1,3 @@
1
- import { type ParsemeConfigFile } from './config.js';
2
1
  import type { ContextOutput, GeneratorOptions } from './types.js';
3
2
  export declare class ParsemeGenerator {
4
3
  private readonly config;
@@ -8,8 +7,6 @@ export declare class ParsemeGenerator {
8
7
  private readonly gitAnalyzer;
9
8
  private readonly contextBuilder;
10
9
  constructor(options?: GeneratorOptions);
11
- static fromConfig(configPath?: string): Promise<ParsemeGenerator>;
12
- static fromConfigWithOptions(configPath?: string, cliOptions?: Partial<ParsemeConfigFile>): Promise<ParsemeGenerator>;
13
10
  generate(outputPath?: string): Promise<ContextOutput>;
14
11
  generateToFile(outputPath?: string, contextDir?: string): Promise<void>;
15
12
  }