@parseme/cli 0.0.2
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/LICENSE +21 -0
- package/README.md +453 -0
- package/dist/analyzers/ast-analyzer.d.ts +12 -0
- package/dist/analyzers/ast-analyzer.js +176 -0
- package/dist/analyzers/framework-detector.d.ts +12 -0
- package/dist/analyzers/framework-detector.js +180 -0
- package/dist/analyzers/git-analyzer.d.ts +13 -0
- package/dist/analyzers/git-analyzer.js +92 -0
- package/dist/analyzers/pattern-detector.d.ts +58 -0
- package/dist/analyzers/pattern-detector.js +174 -0
- package/dist/analyzers/project-analyzer.d.ts +14 -0
- package/dist/analyzers/project-analyzer.js +205 -0
- package/dist/builders/context-builder.d.ts +35 -0
- package/dist/builders/context-builder.js +332 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +145 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.js +203 -0
- package/dist/generator.d.ts +15 -0
- package/dist/generator.js +88 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/prompt.d.ts +8 -0
- package/dist/prompt.js +42 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +2 -0
- package/package.json +81 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { readFile, access, readdir, stat } from 'fs/promises';
|
|
2
|
+
import { join, basename } from 'path';
|
|
3
|
+
export class ProjectAnalyzer {
|
|
4
|
+
config;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
}
|
|
8
|
+
async analyze(rootDir) {
|
|
9
|
+
const packageJsonPath = join(rootDir, 'package.json');
|
|
10
|
+
try {
|
|
11
|
+
await access(packageJsonPath);
|
|
12
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
|
|
13
|
+
return {
|
|
14
|
+
name: packageJson.name || basename(rootDir),
|
|
15
|
+
version: packageJson.version,
|
|
16
|
+
description: packageJson.description,
|
|
17
|
+
type: await this.detectProjectType(rootDir),
|
|
18
|
+
category: this.detectProjectCategory(packageJson, rootDir),
|
|
19
|
+
packageManager: await this.detectPackageManager(rootDir),
|
|
20
|
+
dependencies: packageJson.dependencies || {},
|
|
21
|
+
devDependencies: packageJson.devDependencies || {},
|
|
22
|
+
scripts: packageJson.scripts || {},
|
|
23
|
+
entryPoints: this.detectEntryPoints(packageJson),
|
|
24
|
+
outputTargets: this.detectOutputTargets(packageJson),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// No package.json found, analyze directory structure
|
|
29
|
+
return {
|
|
30
|
+
name: basename(rootDir),
|
|
31
|
+
type: await this.detectProjectType(rootDir),
|
|
32
|
+
category: 'unknown',
|
|
33
|
+
packageManager: 'npm',
|
|
34
|
+
dependencies: {},
|
|
35
|
+
devDependencies: {},
|
|
36
|
+
scripts: {},
|
|
37
|
+
entryPoints: [],
|
|
38
|
+
outputTargets: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async detectProjectType(rootDir) {
|
|
43
|
+
try {
|
|
44
|
+
const files = await this.getFilesRecursive(rootDir, 2); // Only check 2 levels deep
|
|
45
|
+
const tsFiles = files.filter((f) => f.endsWith('.ts') && !f.endsWith('.d.ts'));
|
|
46
|
+
const jsFiles = files.filter((f) => f.endsWith('.js'));
|
|
47
|
+
if (tsFiles.length > 0 && jsFiles.length > 0) {
|
|
48
|
+
return 'mixed';
|
|
49
|
+
}
|
|
50
|
+
if (tsFiles.length > 0) {
|
|
51
|
+
return 'typescript';
|
|
52
|
+
}
|
|
53
|
+
return 'javascript';
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return 'javascript';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async detectPackageManager(rootDir) {
|
|
60
|
+
const lockFiles = {
|
|
61
|
+
'pnpm-lock.yaml': 'pnpm',
|
|
62
|
+
'yarn.lock': 'yarn',
|
|
63
|
+
'bun.lockb': 'bun',
|
|
64
|
+
'package-lock.json': 'npm',
|
|
65
|
+
};
|
|
66
|
+
for (const [lockFile, manager] of Object.entries(lockFiles)) {
|
|
67
|
+
try {
|
|
68
|
+
await access(join(rootDir, lockFile));
|
|
69
|
+
return manager;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Continue checking
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return 'npm';
|
|
76
|
+
}
|
|
77
|
+
async getFilesRecursive(dir, maxDepth) {
|
|
78
|
+
if (maxDepth <= 0) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const entries = await readdir(dir);
|
|
83
|
+
const files = [];
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
const fullPath = join(dir, entry);
|
|
86
|
+
const stats = await stat(fullPath);
|
|
87
|
+
if (stats.isFile()) {
|
|
88
|
+
files.push(fullPath);
|
|
89
|
+
}
|
|
90
|
+
else if (stats.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
|
|
91
|
+
files.push(...(await this.getFilesRecursive(fullPath, maxDepth - 1)));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return files;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
detectProjectCategory(packageJson, _rootDir) {
|
|
101
|
+
const deps = {
|
|
102
|
+
...packageJson.dependencies,
|
|
103
|
+
...packageJson.devDependencies,
|
|
104
|
+
};
|
|
105
|
+
// Check for monorepo indicators
|
|
106
|
+
if (packageJson.workspaces || packageJson.private) {
|
|
107
|
+
return 'monorepo';
|
|
108
|
+
}
|
|
109
|
+
// Check for CLI tools
|
|
110
|
+
if (packageJson.bin) {
|
|
111
|
+
return 'cli-tool';
|
|
112
|
+
}
|
|
113
|
+
// Check for desktop app frameworks
|
|
114
|
+
if (deps['electron'] || deps['tauri']) {
|
|
115
|
+
return 'desktop-app';
|
|
116
|
+
}
|
|
117
|
+
// Check for mobile frameworks
|
|
118
|
+
if (deps['react-native'] || deps['@ionic/react'] || deps['@ionic/angular']) {
|
|
119
|
+
return 'frontend-mobile';
|
|
120
|
+
}
|
|
121
|
+
// Check for fullstack frameworks
|
|
122
|
+
if (deps['next'] || deps['nuxt'] || deps['sveltekit'] || deps['remix']) {
|
|
123
|
+
return 'fullstack';
|
|
124
|
+
}
|
|
125
|
+
// Check for frontend frameworks
|
|
126
|
+
if (deps['react'] ||
|
|
127
|
+
deps['vue'] ||
|
|
128
|
+
deps['angular'] ||
|
|
129
|
+
deps['svelte'] ||
|
|
130
|
+
deps['@angular/core'] ||
|
|
131
|
+
deps['vue-router']) {
|
|
132
|
+
return 'frontend-web';
|
|
133
|
+
}
|
|
134
|
+
// Check for backend frameworks
|
|
135
|
+
if (deps['express'] ||
|
|
136
|
+
deps['fastify'] ||
|
|
137
|
+
deps['@nestjs/core'] ||
|
|
138
|
+
deps['koa'] ||
|
|
139
|
+
deps['@hapi/hapi']) {
|
|
140
|
+
return 'backend-api';
|
|
141
|
+
}
|
|
142
|
+
// Check if it's a library (has main/module/exports but no app dependencies)
|
|
143
|
+
if ((packageJson.main || packageJson.module || packageJson.exports) &&
|
|
144
|
+
!this.hasAppDependencies(deps)) {
|
|
145
|
+
return 'npm-package';
|
|
146
|
+
}
|
|
147
|
+
return 'unknown';
|
|
148
|
+
}
|
|
149
|
+
detectEntryPoints(packageJson) {
|
|
150
|
+
const entryPoints = [];
|
|
151
|
+
if (packageJson.main && typeof packageJson.main === 'string') {
|
|
152
|
+
entryPoints.push(packageJson.main);
|
|
153
|
+
}
|
|
154
|
+
if (packageJson.module && typeof packageJson.module === 'string') {
|
|
155
|
+
entryPoints.push(packageJson.module);
|
|
156
|
+
}
|
|
157
|
+
if (packageJson.browser && typeof packageJson.browser === 'string') {
|
|
158
|
+
entryPoints.push(packageJson.browser);
|
|
159
|
+
}
|
|
160
|
+
if (packageJson.exports) {
|
|
161
|
+
if (typeof packageJson.exports === 'string') {
|
|
162
|
+
entryPoints.push(packageJson.exports);
|
|
163
|
+
}
|
|
164
|
+
else if (typeof packageJson.exports === 'object') {
|
|
165
|
+
Object.values(packageJson.exports).forEach((exp) => {
|
|
166
|
+
if (typeof exp === 'string') {
|
|
167
|
+
entryPoints.push(exp);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...new Set(entryPoints)]; // Remove duplicates
|
|
173
|
+
}
|
|
174
|
+
detectOutputTargets(packageJson) {
|
|
175
|
+
const targets = [];
|
|
176
|
+
const main = packageJson.main;
|
|
177
|
+
// Common output directories
|
|
178
|
+
if (main?.includes('dist/')) {
|
|
179
|
+
targets.push('dist');
|
|
180
|
+
}
|
|
181
|
+
if (main?.includes('build/')) {
|
|
182
|
+
targets.push('build');
|
|
183
|
+
}
|
|
184
|
+
if (main?.includes('lib/')) {
|
|
185
|
+
targets.push('lib');
|
|
186
|
+
}
|
|
187
|
+
return [...new Set(targets)];
|
|
188
|
+
}
|
|
189
|
+
hasAppDependencies(deps) {
|
|
190
|
+
const appIndicators = [
|
|
191
|
+
'react',
|
|
192
|
+
'vue',
|
|
193
|
+
'angular',
|
|
194
|
+
'svelte',
|
|
195
|
+
'express',
|
|
196
|
+
'fastify',
|
|
197
|
+
'@nestjs/core',
|
|
198
|
+
'next',
|
|
199
|
+
'nuxt',
|
|
200
|
+
'electron',
|
|
201
|
+
'react-native',
|
|
202
|
+
];
|
|
203
|
+
return appIndicators.some((indicator) => deps[indicator]);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ParsemeConfig } from '../config.js';
|
|
2
|
+
import type { ContextOutput, ProjectInfo, FileAnalysis, GitInfo, GeneratorOptions } from '../types.js';
|
|
3
|
+
interface BuildContext {
|
|
4
|
+
projectInfo: ProjectInfo;
|
|
5
|
+
fileAnalyses: FileAnalysis[];
|
|
6
|
+
gitInfo?: GitInfo;
|
|
7
|
+
options: GeneratorOptions;
|
|
8
|
+
contextDir?: string;
|
|
9
|
+
outputPath?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class ContextBuilder {
|
|
12
|
+
private readonly config;
|
|
13
|
+
constructor(config: ParsemeConfig);
|
|
14
|
+
private truncateContent;
|
|
15
|
+
private splitContentByLines;
|
|
16
|
+
private splitContentByChars;
|
|
17
|
+
build(context: BuildContext): ContextOutput;
|
|
18
|
+
private buildMultiFile;
|
|
19
|
+
private buildHeader;
|
|
20
|
+
private buildProjectOverview;
|
|
21
|
+
private buildFrameworkSection;
|
|
22
|
+
private buildArchitectureSection;
|
|
23
|
+
private buildRoutesSection;
|
|
24
|
+
private buildFileStructureSection;
|
|
25
|
+
private buildDependenciesSection;
|
|
26
|
+
private buildGitSection;
|
|
27
|
+
private buildFooter;
|
|
28
|
+
private buildSummarySection;
|
|
29
|
+
private buildDetailedStructure;
|
|
30
|
+
private buildDetailedRoutes;
|
|
31
|
+
private buildDetailedDependencies;
|
|
32
|
+
private buildDetailedFramework;
|
|
33
|
+
private buildDetailedGit;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,332 @@
|
|
|
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 } = 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
|
+
const mainContent = this.buildHeader(projectInfo) +
|
|
86
|
+
'\n\n' +
|
|
87
|
+
this.buildProjectOverview(projectInfo) +
|
|
88
|
+
'\n\n' +
|
|
89
|
+
this.buildSummarySection(context);
|
|
90
|
+
const contextFiles = {
|
|
91
|
+
structure: '',
|
|
92
|
+
routes: '',
|
|
93
|
+
dependencies: '',
|
|
94
|
+
};
|
|
95
|
+
// Helper function to merge split files into contextFiles
|
|
96
|
+
const mergeSplitFiles = (result, baseName) => {
|
|
97
|
+
if (typeof result === 'string') {
|
|
98
|
+
contextFiles[baseName] = result;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Merge split files into contextFiles
|
|
102
|
+
Object.entries(result).forEach(([key, value]) => {
|
|
103
|
+
contextFiles[key] = value;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
// Detailed structure
|
|
108
|
+
const structureResult = this.buildDetailedStructure(limitedFileAnalyses);
|
|
109
|
+
mergeSplitFiles(structureResult, 'structure');
|
|
110
|
+
// Routes documentation
|
|
111
|
+
const routes = limitedFileAnalyses.flatMap((f) => f.routes || []);
|
|
112
|
+
if (routes.length > 0) {
|
|
113
|
+
const routesResult = this.buildDetailedRoutes(routes, limitedFileAnalyses);
|
|
114
|
+
mergeSplitFiles(routesResult, 'routes');
|
|
115
|
+
}
|
|
116
|
+
// Dependencies
|
|
117
|
+
const dependenciesResult = this.buildDetailedDependencies(projectInfo);
|
|
118
|
+
mergeSplitFiles(dependenciesResult, 'dependencies');
|
|
119
|
+
// Framework details
|
|
120
|
+
if (projectInfo.framework &&
|
|
121
|
+
projectInfo.framework.name &&
|
|
122
|
+
projectInfo.framework.name !== 'unknown') {
|
|
123
|
+
contextFiles.framework = this.buildDetailedFramework(projectInfo.framework);
|
|
124
|
+
}
|
|
125
|
+
// Git information
|
|
126
|
+
if (gitInfo) {
|
|
127
|
+
const gitResult = this.buildDetailedGit(gitInfo);
|
|
128
|
+
mergeSplitFiles(gitResult, 'git');
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
parseme: mainContent,
|
|
132
|
+
context: contextFiles,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
buildHeader(projectInfo) {
|
|
136
|
+
return `# ${projectInfo.name}
|
|
137
|
+
|
|
138
|
+
*AI-Generated Project Context*
|
|
139
|
+
|
|
140
|
+
${projectInfo.description || 'No description available.'}`;
|
|
141
|
+
}
|
|
142
|
+
buildProjectOverview(projectInfo) {
|
|
143
|
+
return `## Project Overview
|
|
144
|
+
|
|
145
|
+
- **Type**: ${projectInfo.type} project
|
|
146
|
+
- **Package Manager**: ${projectInfo.packageManager}
|
|
147
|
+
- **Framework**: ${projectInfo.framework?.name || 'None detected'}
|
|
148
|
+
- **Version**: ${projectInfo.version || 'Not specified'}`;
|
|
149
|
+
}
|
|
150
|
+
buildFrameworkSection(framework) {
|
|
151
|
+
if (!framework) {
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
const features = framework.features.length > 0 ? `\n- **Features**: ${framework.features.join(', ')}` : '';
|
|
155
|
+
return `## Framework: ${framework.name}
|
|
156
|
+
|
|
157
|
+
- **Version**: ${framework.version || 'Unknown'}${features}`;
|
|
158
|
+
}
|
|
159
|
+
buildArchitectureSection(fileAnalyses) {
|
|
160
|
+
const typeGroups = fileAnalyses.reduce((acc, file) => {
|
|
161
|
+
if (!acc[file.type]) {
|
|
162
|
+
acc[file.type] = [];
|
|
163
|
+
}
|
|
164
|
+
acc[file.type].push(file);
|
|
165
|
+
return acc;
|
|
166
|
+
}, {});
|
|
167
|
+
const archLines = Object.entries(typeGroups)
|
|
168
|
+
.map(([type, files]) => `- **${type}**: ${files.length} files`)
|
|
169
|
+
.join('\n');
|
|
170
|
+
return `## Architecture Overview
|
|
171
|
+
|
|
172
|
+
${archLines}`;
|
|
173
|
+
}
|
|
174
|
+
buildRoutesSection(routes, _fileAnalyses) {
|
|
175
|
+
const routesByMethod = routes.reduce((acc, route) => {
|
|
176
|
+
if (!acc[route.method]) {
|
|
177
|
+
acc[route.method] = [];
|
|
178
|
+
}
|
|
179
|
+
acc[route.method].push(route);
|
|
180
|
+
return acc;
|
|
181
|
+
}, {});
|
|
182
|
+
const routeLines = Object.entries(routesByMethod)
|
|
183
|
+
.map(([method, methodRoutes]) => {
|
|
184
|
+
const routeList = methodRoutes
|
|
185
|
+
.map((route) => ` - \`${route.path}\` → ${route.handler}`)
|
|
186
|
+
.join('\n');
|
|
187
|
+
return `- **${method}**:\n${routeList}`;
|
|
188
|
+
})
|
|
189
|
+
.join('\n');
|
|
190
|
+
return `## API Endpoints
|
|
191
|
+
|
|
192
|
+
${routeLines}`;
|
|
193
|
+
}
|
|
194
|
+
buildFileStructureSection(fileAnalyses) {
|
|
195
|
+
const structure = fileAnalyses.map((file) => `- \`${file.path}\` (${file.type})`).join('\n');
|
|
196
|
+
return `## File Structure
|
|
197
|
+
|
|
198
|
+
${structure}`;
|
|
199
|
+
}
|
|
200
|
+
buildDependenciesSection(projectInfo) {
|
|
201
|
+
const deps = Object.keys(projectInfo.dependencies);
|
|
202
|
+
const devDeps = Object.keys(projectInfo.devDependencies);
|
|
203
|
+
let content = '## Dependencies\n\n';
|
|
204
|
+
if (deps.length > 0) {
|
|
205
|
+
content += `**Production**: ${deps.join(', ')}\n\n`;
|
|
206
|
+
}
|
|
207
|
+
if (devDeps.length > 0) {
|
|
208
|
+
content += `**Development**: ${devDeps.join(', ')}`;
|
|
209
|
+
}
|
|
210
|
+
return content;
|
|
211
|
+
}
|
|
212
|
+
buildGitSection(gitInfo) {
|
|
213
|
+
return `## Git Information
|
|
214
|
+
|
|
215
|
+
- **Branch**: ${gitInfo.branch}
|
|
216
|
+
- **Status**: ${gitInfo.status}
|
|
217
|
+
- **Last Commit**: ${gitInfo.lastCommit}
|
|
218
|
+
${gitInfo.changedFiles.length > 0 ? `- **Changed Files**: ${gitInfo.changedFiles.join(', ')}` : ''}`;
|
|
219
|
+
}
|
|
220
|
+
buildFooter() {
|
|
221
|
+
return `---
|
|
222
|
+
|
|
223
|
+
*Generated by PARSEME v1.0.0 on ${new Date().toISOString().split('T')[0]}*`;
|
|
224
|
+
}
|
|
225
|
+
buildSummarySection(context) {
|
|
226
|
+
const { fileAnalyses, contextDir, outputPath } = context;
|
|
227
|
+
const totalFiles = fileAnalyses.length;
|
|
228
|
+
const routes = fileAnalyses.flatMap((f) => f.routes || []).length;
|
|
229
|
+
// Calculate the relative path for the link in markdown
|
|
230
|
+
let linkPath = 'parseme-context';
|
|
231
|
+
if (contextDir && outputPath) {
|
|
232
|
+
const outputDir = dirname(outputPath);
|
|
233
|
+
if (contextDir.startsWith('/')) {
|
|
234
|
+
// Absolute path: calculate relative path from output file to context dir
|
|
235
|
+
linkPath = relative(outputDir, contextDir);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
// Relative path: use as-is since it's relative to the output file's directory
|
|
239
|
+
linkPath = contextDir;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else if (contextDir) {
|
|
243
|
+
// Fallback: use contextDir as-is if no outputPath provided
|
|
244
|
+
linkPath = contextDir;
|
|
245
|
+
}
|
|
246
|
+
return `## Summary
|
|
247
|
+
|
|
248
|
+
This project contains ${totalFiles} analyzed files${routes > 0 ? ` with ${routes} API endpoints` : ''}.
|
|
249
|
+
|
|
250
|
+
For detailed information, see the files in the \`${linkPath}/\` directory.`;
|
|
251
|
+
}
|
|
252
|
+
buildDetailedStructure(fileAnalyses) {
|
|
253
|
+
const structureData = fileAnalyses.map((file) => ({
|
|
254
|
+
path: file.path,
|
|
255
|
+
type: file.type,
|
|
256
|
+
exports: file.exports,
|
|
257
|
+
imports: file.imports,
|
|
258
|
+
functions: file.functions,
|
|
259
|
+
classes: file.classes,
|
|
260
|
+
routes: file.routes || [],
|
|
261
|
+
}));
|
|
262
|
+
const jsonContent = JSON.stringify(structureData, null, 2);
|
|
263
|
+
const result = this.truncateContent(jsonContent);
|
|
264
|
+
if (Array.isArray(result)) {
|
|
265
|
+
// Return split files as a record with numbered keys
|
|
266
|
+
const splitFiles = {};
|
|
267
|
+
result.forEach((part, index) => {
|
|
268
|
+
const suffix = index === 0 ? '' : `_part${index + 1}`;
|
|
269
|
+
splitFiles[`structure${suffix}`] = part;
|
|
270
|
+
});
|
|
271
|
+
return splitFiles;
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
buildDetailedRoutes(routes, _fileAnalyses) {
|
|
276
|
+
const jsonContent = JSON.stringify(routes, null, 2);
|
|
277
|
+
const result = this.truncateContent(jsonContent);
|
|
278
|
+
if (Array.isArray(result)) {
|
|
279
|
+
const splitFiles = {};
|
|
280
|
+
result.forEach((part, index) => {
|
|
281
|
+
const suffix = index === 0 ? '' : `_part${index + 1}`;
|
|
282
|
+
splitFiles[`routes${suffix}`] = part;
|
|
283
|
+
});
|
|
284
|
+
return splitFiles;
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
buildDetailedDependencies(projectInfo) {
|
|
289
|
+
const jsonContent = JSON.stringify({
|
|
290
|
+
dependencies: projectInfo.dependencies,
|
|
291
|
+
packageManager: projectInfo.packageManager,
|
|
292
|
+
version: projectInfo.version,
|
|
293
|
+
}, null, 2);
|
|
294
|
+
const result = this.truncateContent(jsonContent);
|
|
295
|
+
if (Array.isArray(result)) {
|
|
296
|
+
const splitFiles = {};
|
|
297
|
+
result.forEach((part, index) => {
|
|
298
|
+
const suffix = index === 0 ? '' : `_part${index + 1}`;
|
|
299
|
+
splitFiles[`dependencies${suffix}`] = part;
|
|
300
|
+
});
|
|
301
|
+
return splitFiles;
|
|
302
|
+
}
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
buildDetailedFramework(framework) {
|
|
306
|
+
if (!framework) {
|
|
307
|
+
return '';
|
|
308
|
+
}
|
|
309
|
+
let content = `# Framework: ${framework.name}\n\n`;
|
|
310
|
+
content += `**Version**: ${framework.version || 'Unknown'}\n\n`;
|
|
311
|
+
if (framework.features.length > 0) {
|
|
312
|
+
content += '## Features Detected\n\n';
|
|
313
|
+
framework.features.forEach((feature) => {
|
|
314
|
+
content += `- ${feature}\n`;
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return content;
|
|
318
|
+
}
|
|
319
|
+
buildDetailedGit(gitInfo) {
|
|
320
|
+
const jsonContent = JSON.stringify(gitInfo, null, 2);
|
|
321
|
+
const result = this.truncateContent(jsonContent);
|
|
322
|
+
if (Array.isArray(result)) {
|
|
323
|
+
const splitFiles = {};
|
|
324
|
+
result.forEach((part, index) => {
|
|
325
|
+
const suffix = index === 0 ? '' : `_part${index + 1}`;
|
|
326
|
+
splitFiles[`git${suffix}`] = part;
|
|
327
|
+
});
|
|
328
|
+
return splitFiles;
|
|
329
|
+
}
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
}
|
package/dist/cli.d.ts
ADDED