@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.
- package/README.md +182 -187
- package/dist/cli/cli.js +144 -0
- package/dist/{analyzers → core/analyzers}/ast-analyzer.d.ts +1 -1
- package/dist/{analyzers → core/analyzers}/ast-analyzer.js +14 -27
- package/dist/core/analyzers/framework-detector.d.ts +7 -0
- package/dist/core/analyzers/framework-detector.js +165 -0
- package/dist/{analyzers → core/analyzers}/pattern-detector.d.ts +2 -5
- package/dist/{analyzers → core/analyzers}/pattern-detector.js +134 -49
- package/dist/{analyzers → core/analyzers}/project-analyzer.d.ts +2 -0
- package/dist/{analyzers → core/analyzers}/project-analyzer.js +12 -9
- package/dist/core/config.d.ts +19 -0
- package/dist/{config.js → core/config.js} +79 -91
- package/dist/{builders → core}/context-builder.d.ts +4 -11
- package/dist/core/context-builder.js +225 -0
- package/dist/{generator.d.ts → core/generator.d.ts} +0 -3
- package/dist/{generator.js → core/generator.js} +12 -17
- package/dist/core/types/analyzer-types.d.ts +38 -0
- package/dist/core/types/analyzer-types.js +2 -0
- package/dist/core/types/config-types.d.ts +36 -0
- package/dist/core/types/config-types.js +2 -0
- package/dist/core/types/generator-types.d.ts +6 -0
- package/dist/core/types/generator-types.js +2 -0
- package/dist/core/types/index.d.ts +4 -0
- package/dist/core/types/index.js +5 -0
- package/dist/core/types/project-types.d.ts +30 -0
- package/dist/core/types/project-types.js +2 -0
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.js +2 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +2 -2
- package/dist/utils/file-collector.d.ts +23 -0
- package/dist/utils/file-collector.js +61 -0
- package/dist/utils/file-filter.d.ts +30 -0
- package/dist/utils/file-filter.js +99 -0
- package/dist/{analyzers/git-analyzer.d.ts → utils/git.d.ts} +1 -4
- package/dist/{analyzers/git-analyzer.js → utils/git.js} +0 -4
- package/dist/{prompt.d.ts → utils/prompt.d.ts} +0 -1
- package/dist/{prompt.js → utils/prompt.js} +0 -9
- package/package.json +12 -8
- package/dist/analyzers/framework-detector.d.ts +0 -12
- package/dist/analyzers/framework-detector.js +0 -180
- package/dist/builders/context-builder.js +0 -386
- package/dist/cli.js +0 -145
- package/dist/config.d.ts +0 -44
- package/dist/types.d.ts +0 -84
- package/dist/types.js +0 -2
- /package/dist/{cli.d.ts → cli/cli.d.ts} +0 -0
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
export class FrameworkDetector {
|
|
2
|
-
config;
|
|
3
|
-
constructor(config) {
|
|
4
|
-
this.config = config;
|
|
5
|
-
}
|
|
6
|
-
async detect(projectInfo) {
|
|
7
|
-
const deps = { ...projectInfo.dependencies, ...projectInfo.devDependencies };
|
|
8
|
-
// Check for frameworks in dependencies
|
|
9
|
-
if (deps['@nestjs/core'] || deps['@nestjs/common']) {
|
|
10
|
-
return this.detectNestJS(deps);
|
|
11
|
-
}
|
|
12
|
-
if (deps['fastify']) {
|
|
13
|
-
return this.detectFastify(deps);
|
|
14
|
-
}
|
|
15
|
-
if (deps['express']) {
|
|
16
|
-
return this.detectExpress(deps);
|
|
17
|
-
}
|
|
18
|
-
if (deps['koa']) {
|
|
19
|
-
return this.detectKoa(deps);
|
|
20
|
-
}
|
|
21
|
-
if (deps['@hapi/hapi']) {
|
|
22
|
-
return this.detectHapi(deps);
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
name: 'unknown',
|
|
26
|
-
features: [],
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
detectExpress(deps) {
|
|
30
|
-
const features = [];
|
|
31
|
-
// Detect common Express features
|
|
32
|
-
if (deps['express-session']) {
|
|
33
|
-
features.push('sessions');
|
|
34
|
-
}
|
|
35
|
-
if (deps['passport']) {
|
|
36
|
-
features.push('authentication');
|
|
37
|
-
}
|
|
38
|
-
if (deps['express-rate-limit']) {
|
|
39
|
-
features.push('rate-limiting');
|
|
40
|
-
}
|
|
41
|
-
if (deps['helmet']) {
|
|
42
|
-
features.push('security');
|
|
43
|
-
}
|
|
44
|
-
if (deps['cors']) {
|
|
45
|
-
features.push('cors');
|
|
46
|
-
}
|
|
47
|
-
if (deps['body-parser']) {
|
|
48
|
-
features.push('body-parsing');
|
|
49
|
-
}
|
|
50
|
-
if (deps['express-validator']) {
|
|
51
|
-
features.push('validation');
|
|
52
|
-
}
|
|
53
|
-
if (deps['multer']) {
|
|
54
|
-
features.push('file-upload');
|
|
55
|
-
}
|
|
56
|
-
if (deps['express-static']) {
|
|
57
|
-
features.push('static-files');
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
name: 'express',
|
|
61
|
-
version: deps['express'],
|
|
62
|
-
features,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
detectFastify(deps) {
|
|
66
|
-
const features = [];
|
|
67
|
-
if (deps['@fastify/cors']) {
|
|
68
|
-
features.push('cors');
|
|
69
|
-
}
|
|
70
|
-
if (deps['@fastify/helmet']) {
|
|
71
|
-
features.push('security');
|
|
72
|
-
}
|
|
73
|
-
if (deps['@fastify/rate-limit']) {
|
|
74
|
-
features.push('rate-limiting');
|
|
75
|
-
}
|
|
76
|
-
if (deps['@fastify/multipart']) {
|
|
77
|
-
features.push('file-upload');
|
|
78
|
-
}
|
|
79
|
-
if (deps['@fastify/static']) {
|
|
80
|
-
features.push('static-files');
|
|
81
|
-
}
|
|
82
|
-
if (deps['@fastify/jwt']) {
|
|
83
|
-
features.push('jwt');
|
|
84
|
-
}
|
|
85
|
-
if (deps['@fastify/session']) {
|
|
86
|
-
features.push('sessions');
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
name: 'fastify',
|
|
90
|
-
version: deps['fastify'],
|
|
91
|
-
features,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
detectNestJS(deps) {
|
|
95
|
-
const features = [];
|
|
96
|
-
if (deps['@nestjs/typeorm'] || deps['@nestjs/mongoose']) {
|
|
97
|
-
features.push('orm');
|
|
98
|
-
}
|
|
99
|
-
if (deps['@nestjs/passport']) {
|
|
100
|
-
features.push('authentication');
|
|
101
|
-
}
|
|
102
|
-
if (deps['@nestjs/jwt']) {
|
|
103
|
-
features.push('jwt');
|
|
104
|
-
}
|
|
105
|
-
if (deps['@nestjs/swagger']) {
|
|
106
|
-
features.push('swagger');
|
|
107
|
-
}
|
|
108
|
-
if (deps['@nestjs/graphql']) {
|
|
109
|
-
features.push('graphql');
|
|
110
|
-
}
|
|
111
|
-
if (deps['@nestjs/websockets']) {
|
|
112
|
-
features.push('websockets');
|
|
113
|
-
}
|
|
114
|
-
if (deps['@nestjs/microservices']) {
|
|
115
|
-
features.push('microservices');
|
|
116
|
-
}
|
|
117
|
-
if (deps['@nestjs/testing']) {
|
|
118
|
-
features.push('testing');
|
|
119
|
-
}
|
|
120
|
-
// NestJS uses decorators by default
|
|
121
|
-
features.push('decorators', 'dependency-injection', 'modules');
|
|
122
|
-
return {
|
|
123
|
-
name: 'nestjs',
|
|
124
|
-
version: deps['@nestjs/core'],
|
|
125
|
-
features,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
detectKoa(deps) {
|
|
129
|
-
const features = [];
|
|
130
|
-
if (deps['@koa/cors']) {
|
|
131
|
-
features.push('cors');
|
|
132
|
-
}
|
|
133
|
-
if (deps['koa-helmet']) {
|
|
134
|
-
features.push('security');
|
|
135
|
-
}
|
|
136
|
-
if (deps['koa-ratelimit']) {
|
|
137
|
-
features.push('rate-limiting');
|
|
138
|
-
}
|
|
139
|
-
if (deps['koa-multer']) {
|
|
140
|
-
features.push('file-upload');
|
|
141
|
-
}
|
|
142
|
-
if (deps['koa-static']) {
|
|
143
|
-
features.push('static-files');
|
|
144
|
-
}
|
|
145
|
-
if (deps['koa-session']) {
|
|
146
|
-
features.push('sessions');
|
|
147
|
-
}
|
|
148
|
-
if (deps['koa-bodyparser']) {
|
|
149
|
-
features.push('body-parsing');
|
|
150
|
-
}
|
|
151
|
-
return {
|
|
152
|
-
name: 'koa',
|
|
153
|
-
version: deps['koa'],
|
|
154
|
-
features,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
detectHapi(deps) {
|
|
158
|
-
const features = [];
|
|
159
|
-
if (deps['@hapi/cookie']) {
|
|
160
|
-
features.push('sessions');
|
|
161
|
-
}
|
|
162
|
-
if (deps['@hapi/jwt']) {
|
|
163
|
-
features.push('jwt');
|
|
164
|
-
}
|
|
165
|
-
if (deps['@hapi/inert']) {
|
|
166
|
-
features.push('static-files');
|
|
167
|
-
}
|
|
168
|
-
if (deps['@hapi/vision']) {
|
|
169
|
-
features.push('templates');
|
|
170
|
-
}
|
|
171
|
-
if (deps['@hapi/boom']) {
|
|
172
|
-
features.push('error-handling');
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
name: 'hapi',
|
|
176
|
-
version: deps['@hapi/hapi'],
|
|
177
|
-
features,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
}
|
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
import { relative, dirname } from 'path';
|
|
2
|
-
export class ContextBuilder {
|
|
3
|
-
config;
|
|
4
|
-
constructor(config) {
|
|
5
|
-
this.config = config;
|
|
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
|
-
build(context) {
|
|
76
|
-
return this.buildMultiFile(context);
|
|
77
|
-
}
|
|
78
|
-
buildMultiFile(context) {
|
|
79
|
-
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;
|
|
85
|
-
// Calculate the relative path for the link in markdown
|
|
86
|
-
let linkPath = 'parseme-context';
|
|
87
|
-
if (contextDir && outputPath) {
|
|
88
|
-
const outputDir = dirname(outputPath);
|
|
89
|
-
if (contextDir.startsWith('/')) {
|
|
90
|
-
// Absolute path: calculate relative path from output file to context dir
|
|
91
|
-
linkPath = relative(outputDir, contextDir);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
// Relative path: use as-is since it's relative to the output file's directory
|
|
95
|
-
linkPath = contextDir;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else if (contextDir) {
|
|
99
|
-
// Fallback: use contextDir as-is if no outputPath provided
|
|
100
|
-
linkPath = contextDir;
|
|
101
|
-
}
|
|
102
|
-
const mainContent = this.buildHeader(linkPath) +
|
|
103
|
-
'\n\n\n' +
|
|
104
|
-
this.buildProjectOverview(projectInfo) +
|
|
105
|
-
'\n\n\n' +
|
|
106
|
-
this.buildSummarySection(context, linkPath) +
|
|
107
|
-
'\n\n\n' +
|
|
108
|
-
(gitInfo ? this.buildGitSection(gitInfo) : '');
|
|
109
|
-
const contextFiles = {
|
|
110
|
-
structure: '',
|
|
111
|
-
routes: '',
|
|
112
|
-
dependencies: '',
|
|
113
|
-
};
|
|
114
|
-
// Helper function to merge split files into contextFiles
|
|
115
|
-
const mergeSplitFiles = (result, baseName) => {
|
|
116
|
-
if (typeof result === 'string') {
|
|
117
|
-
contextFiles[baseName] = result;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
// Merge split files into contextFiles
|
|
121
|
-
Object.entries(result).forEach(([key, value]) => {
|
|
122
|
-
contextFiles[key] = value;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
// Files list (markdown)
|
|
127
|
-
contextFiles.files = this.buildFilesList(limitedFileAnalyses);
|
|
128
|
-
// Detailed structure (JSON with AST)
|
|
129
|
-
const structureResult = this.buildDetailedStructure(limitedFileAnalyses);
|
|
130
|
-
mergeSplitFiles(structureResult, 'structure');
|
|
131
|
-
// Routes documentation
|
|
132
|
-
const routes = limitedFileAnalyses.flatMap((f) => f.routes || []);
|
|
133
|
-
if (routes.length > 0) {
|
|
134
|
-
const routesResult = this.buildDetailedRoutes(routes, limitedFileAnalyses);
|
|
135
|
-
mergeSplitFiles(routesResult, 'api-endpoints');
|
|
136
|
-
}
|
|
137
|
-
// Dependencies
|
|
138
|
-
const dependenciesResult = this.buildDetailedDependencies(projectInfo);
|
|
139
|
-
mergeSplitFiles(dependenciesResult, 'dependencies');
|
|
140
|
-
// Framework details
|
|
141
|
-
if (projectInfo.framework &&
|
|
142
|
-
projectInfo.framework.name &&
|
|
143
|
-
projectInfo.framework.name !== 'unknown') {
|
|
144
|
-
contextFiles.framework = this.buildDetailedFramework(projectInfo.framework);
|
|
145
|
-
}
|
|
146
|
-
// Git information
|
|
147
|
-
if (gitInfo) {
|
|
148
|
-
contextFiles.gitDiff = this.buildDetailedGit(gitInfo);
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
parseme: mainContent,
|
|
152
|
-
context: contextFiles,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
buildHeader(linkPath) {
|
|
156
|
-
return `## PARSEME - AI Agent Context
|
|
157
|
-
Auto-generated project summary optimized for AI coding agents. This file provides complete project context without requiring full codebase traversal, designed for token efficiency.
|
|
158
|
-
|
|
159
|
-
**Usage Instructions for AI Agents:**
|
|
160
|
-
1. Read this PARSEME.md file completely first before accessing individual project files
|
|
161
|
-
2. Basic project information, script availability and dependency information provides basic understanding of code base and tech stack without checking package.json
|
|
162
|
-
3. Use the provided file list (${linkPath}/files.md) to see all tracked files in the project
|
|
163
|
-
4. Utilize the structure and AST data (${linkPath}/structure.json) for code analysis without manual parsing
|
|
164
|
-
5. Follow the instruction in the "Git Information" section of this file to validate the actuality of the provided information.
|
|
165
|
-
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.`;
|
|
166
|
-
}
|
|
167
|
-
buildProjectOverview(projectInfo) {
|
|
168
|
-
let content = `## Basic Project Information
|
|
169
|
-
|
|
170
|
-
**Project:** ${projectInfo.name}${projectInfo.version ? ` v${projectInfo.version}` : ''}
|
|
171
|
-
**Description:** ${projectInfo.description || 'No description available.'}
|
|
172
|
-
**Type:** ${projectInfo.type} project
|
|
173
|
-
**Package Manager:** ${projectInfo.packageManager}
|
|
174
|
-
**Framework:** ${projectInfo.framework?.name || 'None detected'}`;
|
|
175
|
-
// Add main entry point if available
|
|
176
|
-
if (projectInfo.entryPoints && projectInfo.entryPoints.length > 0) {
|
|
177
|
-
content += `\n**Main Entry Point:** ${projectInfo.entryPoints[0]}`;
|
|
178
|
-
}
|
|
179
|
-
content += '\n';
|
|
180
|
-
// Add dependencies
|
|
181
|
-
const deps = Object.keys(projectInfo.dependencies);
|
|
182
|
-
if (deps.length > 0) {
|
|
183
|
-
content += '\n\n### Dependencies\n';
|
|
184
|
-
deps.forEach((dep) => {
|
|
185
|
-
content += `- ${dep}\n`;
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
// Add available scripts
|
|
189
|
-
if (projectInfo.scripts && Object.keys(projectInfo.scripts).length > 0) {
|
|
190
|
-
content += '\n### Available Scripts\n';
|
|
191
|
-
Object.entries(projectInfo.scripts).forEach(([name, script]) => {
|
|
192
|
-
content += `- **${name}**: \`${script}\`\n`;
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
return content;
|
|
196
|
-
}
|
|
197
|
-
buildFrameworkSection(framework) {
|
|
198
|
-
if (!framework) {
|
|
199
|
-
return '';
|
|
200
|
-
}
|
|
201
|
-
const features = framework.features.length > 0 ? `\n- **Features**: ${framework.features.join(', ')}` : '';
|
|
202
|
-
return `## Framework: ${framework.name}
|
|
203
|
-
|
|
204
|
-
- **Version**: ${framework.version || 'Unknown'}${features}`;
|
|
205
|
-
}
|
|
206
|
-
buildArchitectureSection(fileAnalyses) {
|
|
207
|
-
const typeGroups = fileAnalyses.reduce((acc, file) => {
|
|
208
|
-
if (!acc[file.type]) {
|
|
209
|
-
acc[file.type] = [];
|
|
210
|
-
}
|
|
211
|
-
acc[file.type].push(file);
|
|
212
|
-
return acc;
|
|
213
|
-
}, {});
|
|
214
|
-
const archLines = Object.entries(typeGroups)
|
|
215
|
-
.map(([type, files]) => `- **${type}**: ${files.length} files`)
|
|
216
|
-
.join('\n');
|
|
217
|
-
return `## Architecture Overview
|
|
218
|
-
|
|
219
|
-
${archLines}`;
|
|
220
|
-
}
|
|
221
|
-
buildRoutesSection(routes, _fileAnalyses) {
|
|
222
|
-
const routesByMethod = routes.reduce((acc, route) => {
|
|
223
|
-
if (!acc[route.method]) {
|
|
224
|
-
acc[route.method] = [];
|
|
225
|
-
}
|
|
226
|
-
acc[route.method].push(route);
|
|
227
|
-
return acc;
|
|
228
|
-
}, {});
|
|
229
|
-
const routeLines = Object.entries(routesByMethod)
|
|
230
|
-
.map(([method, methodRoutes]) => {
|
|
231
|
-
const routeList = methodRoutes
|
|
232
|
-
.map((route) => ` - \`${route.path}\` → ${route.handler}`)
|
|
233
|
-
.join('\n');
|
|
234
|
-
return `- **${method}**:\n${routeList}`;
|
|
235
|
-
})
|
|
236
|
-
.join('\n');
|
|
237
|
-
return `## API Endpoints
|
|
238
|
-
|
|
239
|
-
${routeLines}`;
|
|
240
|
-
}
|
|
241
|
-
buildFileStructureSection(fileAnalyses) {
|
|
242
|
-
const structure = fileAnalyses.map((file) => `- \`${file.path}\` (${file.type})`).join('\n');
|
|
243
|
-
return `## File Structure
|
|
244
|
-
|
|
245
|
-
${structure}`;
|
|
246
|
-
}
|
|
247
|
-
buildDependenciesSection(projectInfo) {
|
|
248
|
-
const deps = Object.keys(projectInfo.dependencies);
|
|
249
|
-
const devDeps = Object.keys(projectInfo.devDependencies);
|
|
250
|
-
let content = '## Dependencies\n\n';
|
|
251
|
-
if (deps.length > 0) {
|
|
252
|
-
content += `**Production**: ${deps.join(', ')}\n\n`;
|
|
253
|
-
}
|
|
254
|
-
if (devDeps.length > 0) {
|
|
255
|
-
content += `**Development**: ${devDeps.join(', ')}`;
|
|
256
|
-
}
|
|
257
|
-
return content;
|
|
258
|
-
}
|
|
259
|
-
buildGitSection(gitInfo) {
|
|
260
|
-
return `## Git Information
|
|
261
|
-
|
|
262
|
-
**State when PARSEME.md and all linked files were automatically generated:**
|
|
263
|
-
|
|
264
|
-
- **Branch:** ${gitInfo.branch}
|
|
265
|
-
- **Commit:** ${gitInfo.lastCommit}${gitInfo.origin ? `\n- **Origin:** ${gitInfo.origin}` : ''}
|
|
266
|
-
|
|
267
|
-
### Git Diff Statistics
|
|
268
|
-
Git diff statistics from the time of generation are available at \`parseme-context/gitDiff.md\`.
|
|
269
|
-
|
|
270
|
-
**AI Agent Command:** To check for changes since generation, run:
|
|
271
|
-
\`\`\`bash
|
|
272
|
-
git diff --stat
|
|
273
|
-
\`\`\`
|
|
274
|
-
Compare the output with the baseline in \`parseme-context/gitDiff.json\` to detect any modifications.`;
|
|
275
|
-
}
|
|
276
|
-
buildFooter() {
|
|
277
|
-
return `---
|
|
278
|
-
|
|
279
|
-
*Generated by PARSEME v1.0.0 on ${new Date().toISOString().split('T')[0]}*`;
|
|
280
|
-
}
|
|
281
|
-
buildSummarySection(context, linkPath) {
|
|
282
|
-
const { fileAnalyses } = context;
|
|
283
|
-
const routes = fileAnalyses.flatMap((f) => f.routes || []).length;
|
|
284
|
-
let content = `## Project Files
|
|
285
|
-
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.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
## Project Structure & AST
|
|
289
|
-
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.`;
|
|
290
|
-
if (routes > 0) {
|
|
291
|
-
content += `\n\n\n## API Endpoints
|
|
292
|
-
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).`;
|
|
293
|
-
}
|
|
294
|
-
return content;
|
|
295
|
-
}
|
|
296
|
-
buildFilesList(fileAnalyses) {
|
|
297
|
-
let content = `# Project Files\n\n`;
|
|
298
|
-
content += `This is a complete list of all analyzed files in the project.\n\n`;
|
|
299
|
-
fileAnalyses.forEach((file) => {
|
|
300
|
-
content += `- ${file.path}\n`;
|
|
301
|
-
});
|
|
302
|
-
return content;
|
|
303
|
-
}
|
|
304
|
-
buildDetailedStructure(fileAnalyses) {
|
|
305
|
-
const structureData = fileAnalyses.map((file) => ({
|
|
306
|
-
path: file.path,
|
|
307
|
-
type: file.type,
|
|
308
|
-
exports: file.exports,
|
|
309
|
-
imports: file.imports,
|
|
310
|
-
functions: file.functions,
|
|
311
|
-
classes: file.classes,
|
|
312
|
-
routes: file.routes || [],
|
|
313
|
-
}));
|
|
314
|
-
const jsonContent = JSON.stringify(structureData, null, 2);
|
|
315
|
-
const result = this.truncateContent(jsonContent);
|
|
316
|
-
if (Array.isArray(result)) {
|
|
317
|
-
// Return split files as a record with numbered keys
|
|
318
|
-
const splitFiles = {};
|
|
319
|
-
result.forEach((part, index) => {
|
|
320
|
-
const suffix = index === 0 ? '' : `_part${index + 1}`;
|
|
321
|
-
splitFiles[`structure${suffix}`] = part;
|
|
322
|
-
});
|
|
323
|
-
return splitFiles;
|
|
324
|
-
}
|
|
325
|
-
return result;
|
|
326
|
-
}
|
|
327
|
-
buildDetailedRoutes(routes, _fileAnalyses) {
|
|
328
|
-
const jsonContent = JSON.stringify(routes, null, 2);
|
|
329
|
-
const result = this.truncateContent(jsonContent);
|
|
330
|
-
if (Array.isArray(result)) {
|
|
331
|
-
const splitFiles = {};
|
|
332
|
-
result.forEach((part, index) => {
|
|
333
|
-
const suffix = index === 0 ? '' : `_part${index + 1}`;
|
|
334
|
-
splitFiles[`routes${suffix}`] = part;
|
|
335
|
-
});
|
|
336
|
-
return splitFiles;
|
|
337
|
-
}
|
|
338
|
-
return result;
|
|
339
|
-
}
|
|
340
|
-
buildDetailedDependencies(projectInfo) {
|
|
341
|
-
const jsonContent = JSON.stringify({
|
|
342
|
-
dependencies: projectInfo.dependencies,
|
|
343
|
-
packageManager: projectInfo.packageManager,
|
|
344
|
-
version: projectInfo.version,
|
|
345
|
-
}, null, 2);
|
|
346
|
-
const result = this.truncateContent(jsonContent);
|
|
347
|
-
if (Array.isArray(result)) {
|
|
348
|
-
const splitFiles = {};
|
|
349
|
-
result.forEach((part, index) => {
|
|
350
|
-
const suffix = index === 0 ? '' : `_part${index + 1}`;
|
|
351
|
-
splitFiles[`dependencies${suffix}`] = part;
|
|
352
|
-
});
|
|
353
|
-
return splitFiles;
|
|
354
|
-
}
|
|
355
|
-
return result;
|
|
356
|
-
}
|
|
357
|
-
buildDetailedFramework(framework) {
|
|
358
|
-
if (!framework) {
|
|
359
|
-
return '';
|
|
360
|
-
}
|
|
361
|
-
let content = `# Framework: ${framework.name}\n\n`;
|
|
362
|
-
content += `**Version**: ${framework.version || 'Unknown'}\n\n`;
|
|
363
|
-
if (framework.features.length > 0) {
|
|
364
|
-
content += '## Features Detected\n\n';
|
|
365
|
-
framework.features.forEach((feature) => {
|
|
366
|
-
content += `- ${feature}\n`;
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
return content;
|
|
370
|
-
}
|
|
371
|
-
buildDetailedGit(gitInfo) {
|
|
372
|
-
let content = `# Git Diff Statistics
|
|
373
|
-
# Generated at time of PARSEME.md creation
|
|
374
|
-
# To check for changes since then, run: git diff --stat
|
|
375
|
-
# Compare the output with the content below
|
|
376
|
-
|
|
377
|
-
`;
|
|
378
|
-
if (gitInfo.diffStat && gitInfo.diffStat.length > 0) {
|
|
379
|
-
content += gitInfo.diffStat;
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
content += 'No changes detected at time of generation.';
|
|
383
|
-
}
|
|
384
|
-
return content;
|
|
385
|
-
}
|
|
386
|
-
}
|