@contextmirror/claude-memory 0.1.0 → 0.2.0
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/briefing/briefingGenerator.d.ts +72 -0
- package/dist/briefing/briefingGenerator.js +593 -0
- package/dist/cli.js +92 -23
- package/dist/setup/setupWizard.d.ts +23 -0
- package/dist/setup/setupWizard.js +196 -0
- package/landing/index.html +455 -0
- package/package.json +4 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Briefing Generator - Deep project analysis for CLAUDE.md generation
|
|
3
|
+
*
|
|
4
|
+
* This is the core value-add of claude-memory Pro.
|
|
5
|
+
* Free: Basic template-based CLAUDE.md
|
|
6
|
+
* Pro: AI-enhanced deep analysis (future)
|
|
7
|
+
*/
|
|
8
|
+
export interface BriefingOptions {
|
|
9
|
+
projectPath: string;
|
|
10
|
+
includeFileContents?: boolean;
|
|
11
|
+
maxDepth?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ProjectBriefing {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
language: string;
|
|
17
|
+
techStack: string[];
|
|
18
|
+
structure: DirectoryNode;
|
|
19
|
+
entryPoints: EntryPoint[];
|
|
20
|
+
patterns: DetectedPattern[];
|
|
21
|
+
dependencies: DependencyInfo[];
|
|
22
|
+
gitInfo: GitInfo;
|
|
23
|
+
keyFiles: KeyFile[];
|
|
24
|
+
recentActivity: RecentChange[];
|
|
25
|
+
}
|
|
26
|
+
interface DirectoryNode {
|
|
27
|
+
name: string;
|
|
28
|
+
type: 'file' | 'directory';
|
|
29
|
+
children?: DirectoryNode[];
|
|
30
|
+
description?: string;
|
|
31
|
+
}
|
|
32
|
+
interface EntryPoint {
|
|
33
|
+
file: string;
|
|
34
|
+
type: 'main' | 'cli' | 'server' | 'test' | 'config';
|
|
35
|
+
description: string;
|
|
36
|
+
}
|
|
37
|
+
interface DetectedPattern {
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
evidence: string[];
|
|
41
|
+
}
|
|
42
|
+
interface DependencyInfo {
|
|
43
|
+
name: string;
|
|
44
|
+
version: string;
|
|
45
|
+
purpose: string;
|
|
46
|
+
isDev: boolean;
|
|
47
|
+
}
|
|
48
|
+
interface GitInfo {
|
|
49
|
+
branch: string;
|
|
50
|
+
isDirty: boolean;
|
|
51
|
+
recentCommits: string[];
|
|
52
|
+
contributors: string[];
|
|
53
|
+
}
|
|
54
|
+
interface KeyFile {
|
|
55
|
+
path: string;
|
|
56
|
+
purpose: string;
|
|
57
|
+
summary?: string;
|
|
58
|
+
}
|
|
59
|
+
interface RecentChange {
|
|
60
|
+
file: string;
|
|
61
|
+
type: 'added' | 'modified' | 'deleted';
|
|
62
|
+
when: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Generate a deep briefing for a project
|
|
66
|
+
*/
|
|
67
|
+
export declare function generateBriefing(options: BriefingOptions): Promise<ProjectBriefing>;
|
|
68
|
+
/**
|
|
69
|
+
* Convert a briefing to CLAUDE.md format
|
|
70
|
+
*/
|
|
71
|
+
export declare function briefingToClaudeMd(briefing: ProjectBriefing): string;
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Briefing Generator - Deep project analysis for CLAUDE.md generation
|
|
3
|
+
*
|
|
4
|
+
* This is the core value-add of claude-memory Pro.
|
|
5
|
+
* Free: Basic template-based CLAUDE.md
|
|
6
|
+
* Pro: AI-enhanced deep analysis (future)
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
9
|
+
import { join, basename } from 'path';
|
|
10
|
+
import { simpleGit } from 'simple-git';
|
|
11
|
+
// Well-known file purposes
|
|
12
|
+
const FILE_PURPOSES = {
|
|
13
|
+
'package.json': 'Node.js project config and dependencies',
|
|
14
|
+
'tsconfig.json': 'TypeScript compiler configuration',
|
|
15
|
+
'Cargo.toml': 'Rust project config and dependencies',
|
|
16
|
+
'pyproject.toml': 'Python project config (PEP 518)',
|
|
17
|
+
'requirements.txt': 'Python dependencies',
|
|
18
|
+
'go.mod': 'Go module dependencies',
|
|
19
|
+
'.env': 'Environment variables (DO NOT COMMIT)',
|
|
20
|
+
'.env.example': 'Environment variable template',
|
|
21
|
+
'.gitignore': 'Git ignore patterns',
|
|
22
|
+
'Dockerfile': 'Docker container definition',
|
|
23
|
+
'docker-compose.yml': 'Multi-container Docker setup',
|
|
24
|
+
'README.md': 'Project documentation',
|
|
25
|
+
'CLAUDE.md': 'AI assistant context',
|
|
26
|
+
'LICENSE': 'License terms',
|
|
27
|
+
'.eslintrc.js': 'ESLint configuration',
|
|
28
|
+
'.prettierrc': 'Prettier configuration',
|
|
29
|
+
'jest.config.js': 'Jest test configuration',
|
|
30
|
+
'vitest.config.ts': 'Vitest test configuration',
|
|
31
|
+
'vite.config.ts': 'Vite bundler configuration',
|
|
32
|
+
'webpack.config.js': 'Webpack bundler configuration',
|
|
33
|
+
'tailwind.config.js': 'Tailwind CSS configuration',
|
|
34
|
+
};
|
|
35
|
+
// Well-known directory purposes
|
|
36
|
+
const DIR_PURPOSES = {
|
|
37
|
+
'src': 'Source code',
|
|
38
|
+
'lib': 'Library code',
|
|
39
|
+
'dist': 'Built/compiled output',
|
|
40
|
+
'build': 'Build output',
|
|
41
|
+
'test': 'Test files',
|
|
42
|
+
'tests': 'Test files',
|
|
43
|
+
'__tests__': 'Jest test files',
|
|
44
|
+
'spec': 'Test specifications',
|
|
45
|
+
'docs': 'Documentation',
|
|
46
|
+
'scripts': 'Build/utility scripts',
|
|
47
|
+
'config': 'Configuration files',
|
|
48
|
+
'public': 'Static public assets',
|
|
49
|
+
'assets': 'Static assets',
|
|
50
|
+
'static': 'Static files',
|
|
51
|
+
'components': 'UI components',
|
|
52
|
+
'pages': 'Page components/routes',
|
|
53
|
+
'api': 'API endpoints',
|
|
54
|
+
'routes': 'Route handlers',
|
|
55
|
+
'controllers': 'Controller logic',
|
|
56
|
+
'models': 'Data models',
|
|
57
|
+
'services': 'Business logic services',
|
|
58
|
+
'utils': 'Utility functions',
|
|
59
|
+
'helpers': 'Helper functions',
|
|
60
|
+
'hooks': 'React/framework hooks',
|
|
61
|
+
'types': 'TypeScript type definitions',
|
|
62
|
+
'interfaces': 'Interface definitions',
|
|
63
|
+
'middleware': 'Middleware functions',
|
|
64
|
+
'migrations': 'Database migrations',
|
|
65
|
+
'seeds': 'Database seed data',
|
|
66
|
+
'fixtures': 'Test fixtures',
|
|
67
|
+
'mocks': 'Mock data/functions',
|
|
68
|
+
};
|
|
69
|
+
// Dependency purposes (common packages)
|
|
70
|
+
const DEP_PURPOSES = {
|
|
71
|
+
'react': 'UI component library',
|
|
72
|
+
'react-dom': 'React DOM rendering',
|
|
73
|
+
'vue': 'Vue.js framework',
|
|
74
|
+
'next': 'Next.js React framework',
|
|
75
|
+
'nuxt': 'Nuxt.js Vue framework',
|
|
76
|
+
'express': 'Node.js web server',
|
|
77
|
+
'fastify': 'Fast Node.js web server',
|
|
78
|
+
'koa': 'Koa web framework',
|
|
79
|
+
'typescript': 'TypeScript compiler',
|
|
80
|
+
'vite': 'Fast build tool',
|
|
81
|
+
'webpack': 'Module bundler',
|
|
82
|
+
'esbuild': 'Fast JavaScript bundler',
|
|
83
|
+
'jest': 'Testing framework',
|
|
84
|
+
'vitest': 'Vite-native testing',
|
|
85
|
+
'mocha': 'Testing framework',
|
|
86
|
+
'chai': 'Assertion library',
|
|
87
|
+
'prisma': 'Database ORM',
|
|
88
|
+
'drizzle-orm': 'TypeScript ORM',
|
|
89
|
+
'mongoose': 'MongoDB ODM',
|
|
90
|
+
'sequelize': 'SQL ORM',
|
|
91
|
+
'axios': 'HTTP client',
|
|
92
|
+
'zod': 'Schema validation',
|
|
93
|
+
'yup': 'Schema validation',
|
|
94
|
+
'tailwindcss': 'Utility-first CSS',
|
|
95
|
+
'styled-components': 'CSS-in-JS',
|
|
96
|
+
'emotion': 'CSS-in-JS',
|
|
97
|
+
'@anthropic-ai/sdk': 'Claude AI SDK',
|
|
98
|
+
'openai': 'OpenAI SDK',
|
|
99
|
+
'@modelcontextprotocol/sdk': 'MCP protocol SDK',
|
|
100
|
+
'commander': 'CLI framework',
|
|
101
|
+
'yargs': 'CLI argument parser',
|
|
102
|
+
'inquirer': 'Interactive CLI prompts',
|
|
103
|
+
'chalk': 'Terminal colors',
|
|
104
|
+
'lodash': 'Utility functions',
|
|
105
|
+
'date-fns': 'Date utilities',
|
|
106
|
+
'moment': 'Date library (legacy)',
|
|
107
|
+
'dayjs': 'Lightweight date library',
|
|
108
|
+
'uuid': 'UUID generation',
|
|
109
|
+
'dotenv': 'Environment variable loading',
|
|
110
|
+
'cors': 'CORS middleware',
|
|
111
|
+
'helmet': 'Security headers',
|
|
112
|
+
'jsonwebtoken': 'JWT authentication',
|
|
113
|
+
'bcrypt': 'Password hashing',
|
|
114
|
+
'socket.io': 'Real-time websockets',
|
|
115
|
+
'ws': 'WebSocket client/server',
|
|
116
|
+
'redis': 'Redis client',
|
|
117
|
+
'ioredis': 'Redis client',
|
|
118
|
+
'pg': 'PostgreSQL client',
|
|
119
|
+
'mysql2': 'MySQL client',
|
|
120
|
+
'sqlite3': 'SQLite client',
|
|
121
|
+
'glob': 'File pattern matching',
|
|
122
|
+
'simple-git': 'Git operations',
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Generate a deep briefing for a project
|
|
126
|
+
*/
|
|
127
|
+
export async function generateBriefing(options) {
|
|
128
|
+
const { projectPath, maxDepth = 3 } = options;
|
|
129
|
+
const name = detectName(projectPath);
|
|
130
|
+
const description = detectDescription(projectPath);
|
|
131
|
+
const { language, techStack } = detectStack(projectPath);
|
|
132
|
+
const structure = buildStructure(projectPath, maxDepth);
|
|
133
|
+
const entryPoints = detectEntryPoints(projectPath);
|
|
134
|
+
const patterns = detectPatterns(projectPath);
|
|
135
|
+
const dependencies = extractDependencies(projectPath);
|
|
136
|
+
const gitInfo = await extractGitInfo(projectPath);
|
|
137
|
+
const keyFiles = identifyKeyFiles(projectPath);
|
|
138
|
+
const recentActivity = await getRecentActivity(projectPath);
|
|
139
|
+
return {
|
|
140
|
+
name,
|
|
141
|
+
description,
|
|
142
|
+
language,
|
|
143
|
+
techStack,
|
|
144
|
+
structure,
|
|
145
|
+
entryPoints,
|
|
146
|
+
patterns,
|
|
147
|
+
dependencies,
|
|
148
|
+
gitInfo,
|
|
149
|
+
keyFiles,
|
|
150
|
+
recentActivity,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Convert a briefing to CLAUDE.md format
|
|
155
|
+
*/
|
|
156
|
+
export function briefingToClaudeMd(briefing) {
|
|
157
|
+
const lines = [];
|
|
158
|
+
// Header
|
|
159
|
+
lines.push(`# ${briefing.name}`);
|
|
160
|
+
lines.push('');
|
|
161
|
+
lines.push(briefing.description);
|
|
162
|
+
lines.push('');
|
|
163
|
+
// Tech Stack
|
|
164
|
+
lines.push('## Tech Stack');
|
|
165
|
+
lines.push('');
|
|
166
|
+
lines.push(`- **Language:** ${briefing.language}`);
|
|
167
|
+
lines.push(`- **Stack:** ${briefing.techStack.join(', ')}`);
|
|
168
|
+
lines.push('');
|
|
169
|
+
// Project Structure
|
|
170
|
+
lines.push('## Project Structure');
|
|
171
|
+
lines.push('');
|
|
172
|
+
lines.push('```');
|
|
173
|
+
lines.push(renderStructure(briefing.structure, ''));
|
|
174
|
+
lines.push('```');
|
|
175
|
+
lines.push('');
|
|
176
|
+
// Entry Points
|
|
177
|
+
if (briefing.entryPoints.length > 0) {
|
|
178
|
+
lines.push('## Entry Points');
|
|
179
|
+
lines.push('');
|
|
180
|
+
for (const entry of briefing.entryPoints) {
|
|
181
|
+
lines.push(`- **${entry.file}** - ${entry.description}`);
|
|
182
|
+
}
|
|
183
|
+
lines.push('');
|
|
184
|
+
}
|
|
185
|
+
// Key Files
|
|
186
|
+
if (briefing.keyFiles.length > 0) {
|
|
187
|
+
lines.push('## Key Files');
|
|
188
|
+
lines.push('');
|
|
189
|
+
for (const file of briefing.keyFiles) {
|
|
190
|
+
lines.push(`- **${file.path}** - ${file.purpose}`);
|
|
191
|
+
}
|
|
192
|
+
lines.push('');
|
|
193
|
+
}
|
|
194
|
+
// Detected Patterns
|
|
195
|
+
if (briefing.patterns.length > 0) {
|
|
196
|
+
lines.push('## Patterns & Conventions');
|
|
197
|
+
lines.push('');
|
|
198
|
+
for (const pattern of briefing.patterns) {
|
|
199
|
+
lines.push(`### ${pattern.name}`);
|
|
200
|
+
lines.push('');
|
|
201
|
+
lines.push(pattern.description);
|
|
202
|
+
if (pattern.evidence.length > 0) {
|
|
203
|
+
lines.push('');
|
|
204
|
+
lines.push('Evidence:');
|
|
205
|
+
for (const e of pattern.evidence) {
|
|
206
|
+
lines.push(`- ${e}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
lines.push('');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Dependencies
|
|
213
|
+
if (briefing.dependencies.length > 0) {
|
|
214
|
+
lines.push('## Key Dependencies');
|
|
215
|
+
lines.push('');
|
|
216
|
+
lines.push('| Package | Version | Purpose |');
|
|
217
|
+
lines.push('|---------|---------|---------|');
|
|
218
|
+
for (const dep of briefing.dependencies.slice(0, 15)) { // Top 15
|
|
219
|
+
lines.push(`| ${dep.name} | ${dep.version} | ${dep.purpose} |`);
|
|
220
|
+
}
|
|
221
|
+
lines.push('');
|
|
222
|
+
}
|
|
223
|
+
// Git Info
|
|
224
|
+
if (briefing.gitInfo.branch !== 'unknown') {
|
|
225
|
+
lines.push('## Git Status');
|
|
226
|
+
lines.push('');
|
|
227
|
+
lines.push(`- **Branch:** ${briefing.gitInfo.branch}${briefing.gitInfo.isDirty ? ' (uncommitted changes)' : ''}`);
|
|
228
|
+
if (briefing.gitInfo.recentCommits.length > 0) {
|
|
229
|
+
lines.push('- **Recent commits:**');
|
|
230
|
+
for (const commit of briefing.gitInfo.recentCommits.slice(0, 5)) {
|
|
231
|
+
lines.push(` - ${commit}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
lines.push('');
|
|
235
|
+
}
|
|
236
|
+
// Recent Activity
|
|
237
|
+
if (briefing.recentActivity.length > 0) {
|
|
238
|
+
lines.push('## Recent Activity');
|
|
239
|
+
lines.push('');
|
|
240
|
+
for (const change of briefing.recentActivity.slice(0, 10)) {
|
|
241
|
+
lines.push(`- ${change.type}: ${change.file}`);
|
|
242
|
+
}
|
|
243
|
+
lines.push('');
|
|
244
|
+
}
|
|
245
|
+
// Footer
|
|
246
|
+
lines.push('---');
|
|
247
|
+
lines.push('');
|
|
248
|
+
lines.push(`*Generated by [claude-memory](https://www.npmjs.com/package/@contextmirror/claude-memory) on ${new Date().toLocaleDateString()}*`);
|
|
249
|
+
return lines.join('\n');
|
|
250
|
+
}
|
|
251
|
+
// Helper functions
|
|
252
|
+
function detectName(projectPath) {
|
|
253
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
254
|
+
if (existsSync(pkgPath)) {
|
|
255
|
+
try {
|
|
256
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
257
|
+
if (pkg.name)
|
|
258
|
+
return pkg.name;
|
|
259
|
+
}
|
|
260
|
+
catch { }
|
|
261
|
+
}
|
|
262
|
+
return basename(projectPath);
|
|
263
|
+
}
|
|
264
|
+
function detectDescription(projectPath) {
|
|
265
|
+
// Try package.json
|
|
266
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
267
|
+
if (existsSync(pkgPath)) {
|
|
268
|
+
try {
|
|
269
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
270
|
+
if (pkg.description)
|
|
271
|
+
return pkg.description;
|
|
272
|
+
}
|
|
273
|
+
catch { }
|
|
274
|
+
}
|
|
275
|
+
// Try README first paragraph
|
|
276
|
+
const readmePath = join(projectPath, 'README.md');
|
|
277
|
+
if (existsSync(readmePath)) {
|
|
278
|
+
try {
|
|
279
|
+
const content = readFileSync(readmePath, 'utf-8');
|
|
280
|
+
const lines = content.split('\n');
|
|
281
|
+
for (const line of lines) {
|
|
282
|
+
const trimmed = line.trim();
|
|
283
|
+
if (trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('!') && !trimmed.startsWith('[')) {
|
|
284
|
+
return trimmed.slice(0, 200);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch { }
|
|
289
|
+
}
|
|
290
|
+
return 'No description available';
|
|
291
|
+
}
|
|
292
|
+
function detectStack(projectPath) {
|
|
293
|
+
const techStack = [];
|
|
294
|
+
let language = 'Unknown';
|
|
295
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
296
|
+
if (existsSync(pkgPath)) {
|
|
297
|
+
try {
|
|
298
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
299
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
300
|
+
if (deps.typescript || existsSync(join(projectPath, 'tsconfig.json'))) {
|
|
301
|
+
language = 'TypeScript';
|
|
302
|
+
techStack.push('TypeScript');
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
language = 'JavaScript';
|
|
306
|
+
techStack.push('JavaScript');
|
|
307
|
+
}
|
|
308
|
+
// Detect frameworks
|
|
309
|
+
if (deps.react)
|
|
310
|
+
techStack.push('React');
|
|
311
|
+
if (deps.vue)
|
|
312
|
+
techStack.push('Vue');
|
|
313
|
+
if (deps.next)
|
|
314
|
+
techStack.push('Next.js');
|
|
315
|
+
if (deps.express)
|
|
316
|
+
techStack.push('Express');
|
|
317
|
+
if (deps.fastify)
|
|
318
|
+
techStack.push('Fastify');
|
|
319
|
+
if (deps['@modelcontextprotocol/sdk'])
|
|
320
|
+
techStack.push('MCP');
|
|
321
|
+
}
|
|
322
|
+
catch { }
|
|
323
|
+
}
|
|
324
|
+
if (existsSync(join(projectPath, 'Cargo.toml'))) {
|
|
325
|
+
language = 'Rust';
|
|
326
|
+
techStack.push('Rust');
|
|
327
|
+
}
|
|
328
|
+
if (existsSync(join(projectPath, 'pyproject.toml')) || existsSync(join(projectPath, 'requirements.txt'))) {
|
|
329
|
+
language = 'Python';
|
|
330
|
+
techStack.push('Python');
|
|
331
|
+
}
|
|
332
|
+
if (existsSync(join(projectPath, 'go.mod'))) {
|
|
333
|
+
language = 'Go';
|
|
334
|
+
techStack.push('Go');
|
|
335
|
+
}
|
|
336
|
+
return { language, techStack };
|
|
337
|
+
}
|
|
338
|
+
function buildStructure(projectPath, maxDepth) {
|
|
339
|
+
const IGNORE = ['node_modules', '.git', 'dist', 'build', '__pycache__', 'target', '.next', '.nuxt', 'coverage', '.cache'];
|
|
340
|
+
function walk(dir, depth) {
|
|
341
|
+
const name = basename(dir);
|
|
342
|
+
const node = {
|
|
343
|
+
name,
|
|
344
|
+
type: 'directory',
|
|
345
|
+
description: DIR_PURPOSES[name],
|
|
346
|
+
children: [],
|
|
347
|
+
};
|
|
348
|
+
if (depth >= maxDepth)
|
|
349
|
+
return node;
|
|
350
|
+
try {
|
|
351
|
+
const entries = readdirSync(dir).sort();
|
|
352
|
+
for (const entry of entries) {
|
|
353
|
+
if (IGNORE.includes(entry) || entry.startsWith('.'))
|
|
354
|
+
continue;
|
|
355
|
+
const fullPath = join(dir, entry);
|
|
356
|
+
try {
|
|
357
|
+
const stat = statSync(fullPath);
|
|
358
|
+
if (stat.isDirectory()) {
|
|
359
|
+
node.children.push(walk(fullPath, depth + 1));
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
node.children.push({
|
|
363
|
+
name: entry,
|
|
364
|
+
type: 'file',
|
|
365
|
+
description: FILE_PURPOSES[entry],
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
catch { }
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
catch { }
|
|
373
|
+
return node;
|
|
374
|
+
}
|
|
375
|
+
return walk(projectPath, 0);
|
|
376
|
+
}
|
|
377
|
+
function renderStructure(node, indent) {
|
|
378
|
+
const lines = [];
|
|
379
|
+
const prefix = node.type === 'directory' ? `${node.name}/` : node.name;
|
|
380
|
+
const desc = node.description ? ` # ${node.description}` : '';
|
|
381
|
+
lines.push(`${indent}${prefix}${desc}`);
|
|
382
|
+
if (node.children) {
|
|
383
|
+
for (const child of node.children) {
|
|
384
|
+
lines.push(renderStructure(child, indent + ' '));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return lines.join('\n');
|
|
388
|
+
}
|
|
389
|
+
function detectEntryPoints(projectPath) {
|
|
390
|
+
const entries = [];
|
|
391
|
+
// Check package.json for main/bin
|
|
392
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
393
|
+
if (existsSync(pkgPath)) {
|
|
394
|
+
try {
|
|
395
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
396
|
+
if (pkg.main) {
|
|
397
|
+
entries.push({
|
|
398
|
+
file: pkg.main,
|
|
399
|
+
type: 'main',
|
|
400
|
+
description: 'Main entry point',
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
if (pkg.bin) {
|
|
404
|
+
if (typeof pkg.bin === 'string') {
|
|
405
|
+
entries.push({
|
|
406
|
+
file: pkg.bin,
|
|
407
|
+
type: 'cli',
|
|
408
|
+
description: 'CLI entry point',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
for (const [name, path] of Object.entries(pkg.bin)) {
|
|
413
|
+
entries.push({
|
|
414
|
+
file: path,
|
|
415
|
+
type: 'cli',
|
|
416
|
+
description: `CLI command: ${name}`,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// Check scripts for common patterns
|
|
422
|
+
if (pkg.scripts) {
|
|
423
|
+
if (pkg.scripts.start) {
|
|
424
|
+
const match = pkg.scripts.start.match(/node\s+(\S+)/);
|
|
425
|
+
if (match && !entries.find(e => e.file === match[1])) {
|
|
426
|
+
entries.push({
|
|
427
|
+
file: match[1],
|
|
428
|
+
type: 'server',
|
|
429
|
+
description: 'Server start script',
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
catch { }
|
|
436
|
+
}
|
|
437
|
+
// Look for common entry files
|
|
438
|
+
const commonEntries = [
|
|
439
|
+
{ file: 'src/index.ts', type: 'main', desc: 'Main source entry' },
|
|
440
|
+
{ file: 'src/index.js', type: 'main', desc: 'Main source entry' },
|
|
441
|
+
{ file: 'src/main.ts', type: 'main', desc: 'Main source entry' },
|
|
442
|
+
{ file: 'src/app.ts', type: 'server', desc: 'Application entry' },
|
|
443
|
+
{ file: 'src/server.ts', type: 'server', desc: 'Server entry' },
|
|
444
|
+
{ file: 'src/cli.ts', type: 'cli', desc: 'CLI entry' },
|
|
445
|
+
{ file: 'index.ts', type: 'main', desc: 'Root entry' },
|
|
446
|
+
{ file: 'index.js', type: 'main', desc: 'Root entry' },
|
|
447
|
+
];
|
|
448
|
+
for (const entry of commonEntries) {
|
|
449
|
+
if (existsSync(join(projectPath, entry.file)) && !entries.find(e => e.file === entry.file)) {
|
|
450
|
+
entries.push({
|
|
451
|
+
file: entry.file,
|
|
452
|
+
type: entry.type,
|
|
453
|
+
description: entry.desc,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return entries;
|
|
458
|
+
}
|
|
459
|
+
function detectPatterns(projectPath) {
|
|
460
|
+
const patterns = [];
|
|
461
|
+
// Check for MCP pattern
|
|
462
|
+
if (existsSync(join(projectPath, 'src/mcp')) || existsSync(join(projectPath, '.mcp.json'))) {
|
|
463
|
+
patterns.push({
|
|
464
|
+
name: 'MCP Server',
|
|
465
|
+
description: 'This project implements a Model Context Protocol server for AI assistant integration.',
|
|
466
|
+
evidence: ['src/mcp/ directory exists', '.mcp.json configuration'],
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
// Check for CLI pattern
|
|
470
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
471
|
+
if (existsSync(pkgPath)) {
|
|
472
|
+
try {
|
|
473
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
474
|
+
if (pkg.bin) {
|
|
475
|
+
patterns.push({
|
|
476
|
+
name: 'CLI Application',
|
|
477
|
+
description: 'This project provides a command-line interface.',
|
|
478
|
+
evidence: ['package.json has bin field'],
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
catch { }
|
|
483
|
+
}
|
|
484
|
+
// Check for monorepo pattern
|
|
485
|
+
if (existsSync(join(projectPath, 'packages')) || existsSync(join(projectPath, 'lerna.json')) || existsSync(join(projectPath, 'pnpm-workspace.yaml'))) {
|
|
486
|
+
patterns.push({
|
|
487
|
+
name: 'Monorepo',
|
|
488
|
+
description: 'This project uses a monorepo structure with multiple packages.',
|
|
489
|
+
evidence: ['packages/ directory', 'workspace configuration'],
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
// Check for VS Code extension
|
|
493
|
+
if (existsSync(join(projectPath, '.vscode')) && existsSync(join(projectPath, 'package.json'))) {
|
|
494
|
+
try {
|
|
495
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
496
|
+
if (pkg.engines?.vscode) {
|
|
497
|
+
patterns.push({
|
|
498
|
+
name: 'VS Code Extension',
|
|
499
|
+
description: 'This project is a Visual Studio Code extension.',
|
|
500
|
+
evidence: ['package.json has vscode engine', '.vscode/ configuration'],
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
catch { }
|
|
505
|
+
}
|
|
506
|
+
return patterns;
|
|
507
|
+
}
|
|
508
|
+
function extractDependencies(projectPath) {
|
|
509
|
+
const deps = [];
|
|
510
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
511
|
+
if (existsSync(pkgPath)) {
|
|
512
|
+
try {
|
|
513
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
514
|
+
for (const [name, version] of Object.entries(pkg.dependencies || {})) {
|
|
515
|
+
deps.push({
|
|
516
|
+
name,
|
|
517
|
+
version: version,
|
|
518
|
+
purpose: DEP_PURPOSES[name] || 'Dependency',
|
|
519
|
+
isDev: false,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
for (const [name, version] of Object.entries(pkg.devDependencies || {})) {
|
|
523
|
+
deps.push({
|
|
524
|
+
name,
|
|
525
|
+
version: version,
|
|
526
|
+
purpose: DEP_PURPOSES[name] || 'Dev dependency',
|
|
527
|
+
isDev: true,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
catch { }
|
|
532
|
+
}
|
|
533
|
+
// Sort by importance (known purposes first)
|
|
534
|
+
return deps.sort((a, b) => {
|
|
535
|
+
const aKnown = DEP_PURPOSES[a.name] ? 0 : 1;
|
|
536
|
+
const bKnown = DEP_PURPOSES[b.name] ? 0 : 1;
|
|
537
|
+
return aKnown - bKnown;
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
async function extractGitInfo(projectPath) {
|
|
541
|
+
const info = {
|
|
542
|
+
branch: 'unknown',
|
|
543
|
+
isDirty: false,
|
|
544
|
+
recentCommits: [],
|
|
545
|
+
contributors: [],
|
|
546
|
+
};
|
|
547
|
+
if (!existsSync(join(projectPath, '.git'))) {
|
|
548
|
+
return info;
|
|
549
|
+
}
|
|
550
|
+
try {
|
|
551
|
+
const git = simpleGit(projectPath);
|
|
552
|
+
const status = await git.status();
|
|
553
|
+
info.branch = status.current || 'unknown';
|
|
554
|
+
info.isDirty = !status.isClean();
|
|
555
|
+
const log = await git.log({ maxCount: 10 });
|
|
556
|
+
info.recentCommits = log.all.map(c => `${c.message.split('\n')[0]} (${c.author_name})`);
|
|
557
|
+
// Get unique contributors
|
|
558
|
+
const authors = new Set(log.all.map(c => c.author_name));
|
|
559
|
+
info.contributors = Array.from(authors);
|
|
560
|
+
}
|
|
561
|
+
catch { }
|
|
562
|
+
return info;
|
|
563
|
+
}
|
|
564
|
+
function identifyKeyFiles(projectPath) {
|
|
565
|
+
const keyFiles = [];
|
|
566
|
+
for (const [file, purpose] of Object.entries(FILE_PURPOSES)) {
|
|
567
|
+
if (existsSync(join(projectPath, file))) {
|
|
568
|
+
keyFiles.push({ path: file, purpose });
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return keyFiles;
|
|
572
|
+
}
|
|
573
|
+
async function getRecentActivity(projectPath) {
|
|
574
|
+
const changes = [];
|
|
575
|
+
if (!existsSync(join(projectPath, '.git'))) {
|
|
576
|
+
return changes;
|
|
577
|
+
}
|
|
578
|
+
try {
|
|
579
|
+
const git = simpleGit(projectPath);
|
|
580
|
+
const status = await git.status();
|
|
581
|
+
for (const file of status.modified) {
|
|
582
|
+
changes.push({ file, type: 'modified', when: 'uncommitted' });
|
|
583
|
+
}
|
|
584
|
+
for (const file of status.not_added) {
|
|
585
|
+
changes.push({ file, type: 'added', when: 'uncommitted' });
|
|
586
|
+
}
|
|
587
|
+
for (const file of status.deleted) {
|
|
588
|
+
changes.push({ file, type: 'deleted', when: 'uncommitted' });
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
catch { }
|
|
592
|
+
return changes;
|
|
593
|
+
}
|