@parseme/cli 0.0.2 → 0.0.3
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/dist/analyzers/git-analyzer.d.ts +3 -3
- package/dist/analyzers/git-analyzer.js +12 -12
- package/dist/builders/context-builder.d.ts +2 -1
- package/dist/builders/context-builder.js +110 -56
- package/dist/cli.js +0 -0
- package/dist/config.js +2 -1
- package/dist/generator.js +4 -2
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -3,11 +3,11 @@ import type { GitInfo } from '../types.js';
|
|
|
3
3
|
export declare class GitAnalyzer {
|
|
4
4
|
private readonly config;
|
|
5
5
|
constructor(config: ParsemeConfig);
|
|
6
|
-
analyze(rootDir: string): Promise<GitInfo |
|
|
6
|
+
analyze(rootDir: string): Promise<GitInfo | null>;
|
|
7
7
|
private getCurrentBranch;
|
|
8
8
|
private getLastCommit;
|
|
9
9
|
private getStatus;
|
|
10
10
|
private getChangedFiles;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
private getOrigin;
|
|
12
|
+
private getDiffStat;
|
|
13
13
|
}
|
|
@@ -10,22 +10,26 @@ export class GitAnalyzer {
|
|
|
10
10
|
try {
|
|
11
11
|
// Check if this is a git repository
|
|
12
12
|
await execAsync('git rev-parse --git-dir', { cwd: rootDir });
|
|
13
|
-
const [branch, lastCommit, status, changedFiles] = await Promise.all([
|
|
13
|
+
const [branch, lastCommit, status, changedFiles, origin, diffStat] = await Promise.all([
|
|
14
14
|
this.getCurrentBranch(rootDir),
|
|
15
15
|
this.getLastCommit(rootDir),
|
|
16
16
|
this.getStatus(rootDir),
|
|
17
17
|
this.getChangedFiles(rootDir),
|
|
18
|
+
this.getOrigin(rootDir),
|
|
19
|
+
this.getDiffStat(rootDir),
|
|
18
20
|
]);
|
|
19
21
|
return {
|
|
20
22
|
branch,
|
|
21
23
|
lastCommit,
|
|
22
24
|
status,
|
|
23
25
|
changedFiles,
|
|
26
|
+
origin,
|
|
27
|
+
diffStat,
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
catch {
|
|
27
31
|
// Not a git repository or git not available
|
|
28
|
-
return
|
|
32
|
+
return null;
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
async getCurrentBranch(rootDir) {
|
|
@@ -67,26 +71,22 @@ export class GitAnalyzer {
|
|
|
67
71
|
return [];
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
|
-
async
|
|
74
|
+
async getOrigin(rootDir) {
|
|
71
75
|
try {
|
|
72
|
-
const { stdout } = await execAsync(
|
|
73
|
-
cwd: rootDir,
|
|
74
|
-
});
|
|
76
|
+
const { stdout } = await execAsync('git remote get-url origin', { cwd: rootDir });
|
|
75
77
|
return stdout.trim();
|
|
76
78
|
}
|
|
77
79
|
catch {
|
|
78
80
|
return undefined;
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
|
-
async
|
|
83
|
+
async getDiffStat(rootDir) {
|
|
82
84
|
try {
|
|
83
|
-
const { stdout } = await execAsync(
|
|
84
|
-
|
|
85
|
-
});
|
|
86
|
-
return stdout.split('\n').filter((line) => line.trim());
|
|
85
|
+
const { stdout } = await execAsync('git diff --stat', { cwd: rootDir });
|
|
86
|
+
return stdout.trim();
|
|
87
87
|
}
|
|
88
88
|
catch {
|
|
89
|
-
return
|
|
89
|
+
return undefined;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -3,7 +3,7 @@ import type { ContextOutput, ProjectInfo, FileAnalysis, GitInfo, GeneratorOption
|
|
|
3
3
|
interface BuildContext {
|
|
4
4
|
projectInfo: ProjectInfo;
|
|
5
5
|
fileAnalyses: FileAnalysis[];
|
|
6
|
-
gitInfo?: GitInfo;
|
|
6
|
+
gitInfo?: GitInfo | null;
|
|
7
7
|
options: GeneratorOptions;
|
|
8
8
|
contextDir?: string;
|
|
9
9
|
outputPath?: string;
|
|
@@ -26,6 +26,7 @@ export declare class ContextBuilder {
|
|
|
26
26
|
private buildGitSection;
|
|
27
27
|
private buildFooter;
|
|
28
28
|
private buildSummarySection;
|
|
29
|
+
private buildFilesList;
|
|
29
30
|
private buildDetailedStructure;
|
|
30
31
|
private buildDetailedRoutes;
|
|
31
32
|
private buildDetailedDependencies;
|
|
@@ -76,17 +76,36 @@ export class ContextBuilder {
|
|
|
76
76
|
return this.buildMultiFile(context);
|
|
77
77
|
}
|
|
78
78
|
buildMultiFile(context) {
|
|
79
|
-
const { projectInfo, fileAnalyses, gitInfo } = context;
|
|
79
|
+
const { projectInfo, fileAnalyses, gitInfo, contextDir, outputPath } = context;
|
|
80
80
|
const limits = this.config.get().limits;
|
|
81
81
|
// Limit number of files analyzed if specified
|
|
82
82
|
const limitedFileAnalyses = limits?.maxFilesPerContext
|
|
83
83
|
? fileAnalyses.slice(0, limits.maxFilesPerContext)
|
|
84
84
|
: fileAnalyses;
|
|
85
|
-
|
|
86
|
-
|
|
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' +
|
|
87
104
|
this.buildProjectOverview(projectInfo) +
|
|
88
|
-
'\n\n' +
|
|
89
|
-
this.buildSummarySection(context)
|
|
105
|
+
'\n\n\n' +
|
|
106
|
+
this.buildSummarySection(context, linkPath) +
|
|
107
|
+
'\n\n\n' +
|
|
108
|
+
(gitInfo ? this.buildGitSection(gitInfo) : '');
|
|
90
109
|
const contextFiles = {
|
|
91
110
|
structure: '',
|
|
92
111
|
routes: '',
|
|
@@ -104,14 +123,16 @@ export class ContextBuilder {
|
|
|
104
123
|
});
|
|
105
124
|
}
|
|
106
125
|
};
|
|
107
|
-
//
|
|
126
|
+
// Files list (markdown)
|
|
127
|
+
contextFiles.files = this.buildFilesList(limitedFileAnalyses);
|
|
128
|
+
// Detailed structure (JSON with AST)
|
|
108
129
|
const structureResult = this.buildDetailedStructure(limitedFileAnalyses);
|
|
109
130
|
mergeSplitFiles(structureResult, 'structure');
|
|
110
131
|
// Routes documentation
|
|
111
132
|
const routes = limitedFileAnalyses.flatMap((f) => f.routes || []);
|
|
112
133
|
if (routes.length > 0) {
|
|
113
134
|
const routesResult = this.buildDetailedRoutes(routes, limitedFileAnalyses);
|
|
114
|
-
mergeSplitFiles(routesResult, '
|
|
135
|
+
mergeSplitFiles(routesResult, 'api-endpoints');
|
|
115
136
|
}
|
|
116
137
|
// Dependencies
|
|
117
138
|
const dependenciesResult = this.buildDetailedDependencies(projectInfo);
|
|
@@ -124,28 +145,54 @@ export class ContextBuilder {
|
|
|
124
145
|
}
|
|
125
146
|
// Git information
|
|
126
147
|
if (gitInfo) {
|
|
127
|
-
|
|
128
|
-
mergeSplitFiles(gitResult, 'git');
|
|
148
|
+
contextFiles.gitDiff = this.buildDetailedGit(gitInfo);
|
|
129
149
|
}
|
|
130
150
|
return {
|
|
131
151
|
parseme: mainContent,
|
|
132
152
|
context: contextFiles,
|
|
133
153
|
};
|
|
134
154
|
}
|
|
135
|
-
buildHeader(
|
|
136
|
-
return
|
|
137
|
-
|
|
138
|
-
*AI-Generated Project Context*
|
|
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.
|
|
139
158
|
|
|
140
|
-
|
|
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.`;
|
|
141
166
|
}
|
|
142
167
|
buildProjectOverview(projectInfo) {
|
|
143
|
-
|
|
168
|
+
let content = `## Basic Project Information
|
|
144
169
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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;
|
|
149
196
|
}
|
|
150
197
|
buildFrameworkSection(framework) {
|
|
151
198
|
if (!framework) {
|
|
@@ -212,42 +259,47 @@ ${structure}`;
|
|
|
212
259
|
buildGitSection(gitInfo) {
|
|
213
260
|
return `## Git Information
|
|
214
261
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
- **
|
|
218
|
-
${gitInfo.
|
|
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.`;
|
|
219
275
|
}
|
|
220
276
|
buildFooter() {
|
|
221
277
|
return `---
|
|
222
278
|
|
|
223
279
|
*Generated by PARSEME v1.0.0 on ${new Date().toISOString().split('T')[0]}*`;
|
|
224
280
|
}
|
|
225
|
-
buildSummarySection(context) {
|
|
226
|
-
const { fileAnalyses
|
|
227
|
-
const totalFiles = fileAnalyses.length;
|
|
281
|
+
buildSummarySection(context, linkPath) {
|
|
282
|
+
const { fileAnalyses } = context;
|
|
228
283
|
const routes = fileAnalyses.flatMap((f) => f.routes || []).length;
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
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.
|
|
247
286
|
|
|
248
|
-
This project contains ${totalFiles} analyzed files${routes > 0 ? ` with ${routes} API endpoints` : ''}.
|
|
249
287
|
|
|
250
|
-
|
|
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;
|
|
251
303
|
}
|
|
252
304
|
buildDetailedStructure(fileAnalyses) {
|
|
253
305
|
const structureData = fileAnalyses.map((file) => ({
|
|
@@ -317,16 +369,18 @@ For detailed information, see the files in the \`${linkPath}/\` directory.`;
|
|
|
317
369
|
return content;
|
|
318
370
|
}
|
|
319
371
|
buildDetailedGit(gitInfo) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return splitFiles;
|
|
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;
|
|
329
380
|
}
|
|
330
|
-
|
|
381
|
+
else {
|
|
382
|
+
content += 'No changes detected at time of generation.';
|
|
383
|
+
}
|
|
384
|
+
return content;
|
|
331
385
|
}
|
|
332
386
|
}
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/config.js
CHANGED
|
@@ -191,7 +191,8 @@ export default config;
|
|
|
191
191
|
return pattern + '**';
|
|
192
192
|
}
|
|
193
193
|
if (!pattern.includes('/') && !pattern.includes('*')) {
|
|
194
|
-
|
|
194
|
+
// Convert simple names to match directory patterns
|
|
195
|
+
return pattern + '/**';
|
|
195
196
|
}
|
|
196
197
|
return pattern;
|
|
197
198
|
});
|
package/dist/generator.js
CHANGED
|
@@ -40,7 +40,7 @@ export class ParsemeGenerator {
|
|
|
40
40
|
// Step 4: Get git information if enabled
|
|
41
41
|
const gitInfo = configData.includeGitInfo
|
|
42
42
|
? await this.gitAnalyzer.analyze(configData.rootDir)
|
|
43
|
-
:
|
|
43
|
+
: null;
|
|
44
44
|
// Calculate final output path for link generation
|
|
45
45
|
const finalOutputPath = outputPath || configData.outputPath || join(configData.rootDir, 'PARSEME.md');
|
|
46
46
|
// Step 5: Build the context output
|
|
@@ -81,7 +81,9 @@ export class ParsemeGenerator {
|
|
|
81
81
|
await writeFile(finalOutputPath, context.parseme);
|
|
82
82
|
if (context.context) {
|
|
83
83
|
for (const [filename, content] of Object.entries(context.context)) {
|
|
84
|
-
|
|
84
|
+
// Use .md extension for markdown files, .json for others
|
|
85
|
+
const extension = filename === 'gitDiff' || filename === 'files' ? '.md' : '.json';
|
|
86
|
+
await writeFile(join(parsemeDir, `${filename}${extension}`), content);
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
}
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED