tlc-claude-code 1.3.0 → 1.4.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/dashboard/dist/components/AuditPane.d.ts +30 -0
- package/dashboard/dist/components/AuditPane.js +127 -0
- package/dashboard/dist/components/AuditPane.test.d.ts +1 -0
- package/dashboard/dist/components/AuditPane.test.js +339 -0
- package/dashboard/dist/components/CompliancePane.d.ts +39 -0
- package/dashboard/dist/components/CompliancePane.js +96 -0
- package/dashboard/dist/components/CompliancePane.test.d.ts +1 -0
- package/dashboard/dist/components/CompliancePane.test.js +183 -0
- package/dashboard/dist/components/SSOPane.d.ts +36 -0
- package/dashboard/dist/components/SSOPane.js +71 -0
- package/dashboard/dist/components/SSOPane.test.d.ts +1 -0
- package/dashboard/dist/components/SSOPane.test.js +155 -0
- package/dashboard/dist/components/WorkspaceDocsPane.js +0 -16
- package/dashboard/dist/components/WorkspacePane.d.ts +1 -1
- package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
- package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
- package/package.json +1 -1
- package/server/lib/access-control-doc.js +541 -0
- package/server/lib/access-control-doc.test.js +672 -0
- package/server/lib/adr-generator.js +423 -0
- package/server/lib/adr-generator.test.js +586 -0
- package/server/lib/agent-progress-monitor.js +223 -0
- package/server/lib/agent-progress-monitor.test.js +202 -0
- package/server/lib/audit-attribution.js +191 -0
- package/server/lib/audit-attribution.test.js +359 -0
- package/server/lib/audit-classifier.js +202 -0
- package/server/lib/audit-classifier.test.js +209 -0
- package/server/lib/audit-command.js +275 -0
- package/server/lib/audit-command.test.js +325 -0
- package/server/lib/audit-exporter.js +380 -0
- package/server/lib/audit-exporter.test.js +464 -0
- package/server/lib/audit-logger.js +236 -0
- package/server/lib/audit-logger.test.js +364 -0
- package/server/lib/audit-query.js +257 -0
- package/server/lib/audit-query.test.js +352 -0
- package/server/lib/audit-storage.js +269 -0
- package/server/lib/audit-storage.test.js +272 -0
- package/server/lib/bulk-repo-init.js +342 -0
- package/server/lib/bulk-repo-init.test.js +388 -0
- package/server/lib/compliance-checklist.js +866 -0
- package/server/lib/compliance-checklist.test.js +476 -0
- package/server/lib/compliance-command.js +616 -0
- package/server/lib/compliance-command.test.js +551 -0
- package/server/lib/compliance-reporter.js +692 -0
- package/server/lib/compliance-reporter.test.js +707 -0
- package/server/lib/data-flow-doc.js +665 -0
- package/server/lib/data-flow-doc.test.js +659 -0
- package/server/lib/ephemeral-storage.js +249 -0
- package/server/lib/ephemeral-storage.test.js +254 -0
- package/server/lib/evidence-collector.js +627 -0
- package/server/lib/evidence-collector.test.js +901 -0
- package/server/lib/flow-diagram-generator.js +474 -0
- package/server/lib/flow-diagram-generator.test.js +446 -0
- package/server/lib/idp-manager.js +626 -0
- package/server/lib/idp-manager.test.js +587 -0
- package/server/lib/memory-exclusion.js +326 -0
- package/server/lib/memory-exclusion.test.js +241 -0
- package/server/lib/mfa-handler.js +452 -0
- package/server/lib/mfa-handler.test.js +490 -0
- package/server/lib/oauth-flow.js +375 -0
- package/server/lib/oauth-flow.test.js +487 -0
- package/server/lib/oauth-registry.js +190 -0
- package/server/lib/oauth-registry.test.js +306 -0
- package/server/lib/readme-generator.js +490 -0
- package/server/lib/readme-generator.test.js +493 -0
- package/server/lib/repo-dependency-tracker.js +261 -0
- package/server/lib/repo-dependency-tracker.test.js +350 -0
- package/server/lib/retention-policy.js +281 -0
- package/server/lib/retention-policy.test.js +486 -0
- package/server/lib/role-mapper.js +236 -0
- package/server/lib/role-mapper.test.js +395 -0
- package/server/lib/saml-provider.js +765 -0
- package/server/lib/saml-provider.test.js +643 -0
- package/server/lib/security-policy-generator.js +682 -0
- package/server/lib/security-policy-generator.test.js +544 -0
- package/server/lib/sensitive-detector.js +112 -0
- package/server/lib/sensitive-detector.test.js +209 -0
- package/server/lib/service-interaction-diagram.js +700 -0
- package/server/lib/service-interaction-diagram.test.js +638 -0
- package/server/lib/service-summary.js +553 -0
- package/server/lib/service-summary.test.js +619 -0
- package/server/lib/session-purge.js +460 -0
- package/server/lib/session-purge.test.js +312 -0
- package/server/lib/sso-command.js +544 -0
- package/server/lib/sso-command.test.js +552 -0
- package/server/lib/sso-session.js +492 -0
- package/server/lib/sso-session.test.js +670 -0
- package/server/lib/workspace-command.js +249 -0
- package/server/lib/workspace-command.test.js +264 -0
- package/server/lib/workspace-config.js +270 -0
- package/server/lib/workspace-config.test.js +312 -0
- package/server/lib/workspace-docs-command.js +547 -0
- package/server/lib/workspace-docs-command.test.js +692 -0
- package/server/lib/workspace-memory.js +451 -0
- package/server/lib/workspace-memory.test.js +403 -0
- package/server/lib/workspace-scanner.js +452 -0
- package/server/lib/workspace-scanner.test.js +677 -0
- package/server/lib/workspace-test-runner.js +315 -0
- package/server/lib/workspace-test-runner.test.js +294 -0
- package/server/lib/zero-retention-command.js +439 -0
- package/server/lib/zero-retention-command.test.js +448 -0
- package/server/lib/zero-retention.js +322 -0
- package/server/lib/zero-retention.test.js +258 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* README Generator Module
|
|
3
|
+
* Generates README.md files for repositories based on their characteristics
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { extractRoutes } = require('./route-detector.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Glob patterns for route files
|
|
12
|
+
*/
|
|
13
|
+
const ROUTE_FILE_PATTERNS = [
|
|
14
|
+
'**/routes/**/*.js',
|
|
15
|
+
'**/routes/**/*.ts',
|
|
16
|
+
'**/api/**/*.js',
|
|
17
|
+
'**/api/**/*.ts',
|
|
18
|
+
'**/controllers/**/*.js',
|
|
19
|
+
'**/controllers/**/*.ts',
|
|
20
|
+
'src/**/*.routes.js',
|
|
21
|
+
'src/**/*.routes.ts',
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Directories to ignore when scanning
|
|
26
|
+
*/
|
|
27
|
+
const IGNORE_DIRS = ['node_modules', '.git', 'dist', 'build', 'coverage', '.next', '.nuxt'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* README Generator class
|
|
31
|
+
*/
|
|
32
|
+
class ReadmeGenerator {
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} repoPath - Path to the repository
|
|
35
|
+
*/
|
|
36
|
+
constructor(repoPath) {
|
|
37
|
+
this.repoPath = repoPath;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Read package.json if it exists
|
|
42
|
+
* @returns {Object|null} Parsed package.json or null
|
|
43
|
+
*/
|
|
44
|
+
readPackageJson() {
|
|
45
|
+
const pkgPath = path.join(this.repoPath, 'package.json');
|
|
46
|
+
try {
|
|
47
|
+
if (fs.existsSync(pkgPath)) {
|
|
48
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// Ignore parse errors
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Extract project name and description from package.json
|
|
58
|
+
* @returns {Object} Project info with name and description
|
|
59
|
+
*/
|
|
60
|
+
extractProjectInfo() {
|
|
61
|
+
const pkg = this.readPackageJson();
|
|
62
|
+
const defaultName = path.basename(this.repoPath);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
name: pkg?.name || defaultName,
|
|
66
|
+
description: pkg?.description || '',
|
|
67
|
+
version: pkg?.version || '',
|
|
68
|
+
license: pkg?.license || '',
|
|
69
|
+
author: pkg?.author || '',
|
|
70
|
+
repository: pkg?.repository || null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extract npm scripts from package.json
|
|
76
|
+
* @returns {Array} Array of {name, command} objects
|
|
77
|
+
*/
|
|
78
|
+
extractScripts() {
|
|
79
|
+
const pkg = this.readPackageJson();
|
|
80
|
+
if (!pkg?.scripts) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return Object.entries(pkg.scripts).map(([name, command]) => ({
|
|
85
|
+
name,
|
|
86
|
+
command,
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Extract dependencies from package.json
|
|
92
|
+
* @returns {Object} Object with runtime and dev dependency arrays
|
|
93
|
+
*/
|
|
94
|
+
extractDependencies() {
|
|
95
|
+
const pkg = this.readPackageJson();
|
|
96
|
+
|
|
97
|
+
const runtime = pkg?.dependencies
|
|
98
|
+
? Object.entries(pkg.dependencies).map(([name, version]) => ({
|
|
99
|
+
name,
|
|
100
|
+
version,
|
|
101
|
+
}))
|
|
102
|
+
: [];
|
|
103
|
+
|
|
104
|
+
const dev = pkg?.devDependencies
|
|
105
|
+
? Object.entries(pkg.devDependencies).map(([name, version]) => ({
|
|
106
|
+
name,
|
|
107
|
+
version,
|
|
108
|
+
}))
|
|
109
|
+
: [];
|
|
110
|
+
|
|
111
|
+
return { runtime, dev };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Extract environment variables from .env.example
|
|
116
|
+
* @returns {Array} Array of env var objects with name, example, and comment
|
|
117
|
+
*/
|
|
118
|
+
extractEnvVars() {
|
|
119
|
+
const envPath = path.join(this.repoPath, '.env.example');
|
|
120
|
+
const envVars = [];
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
if (!fs.existsSync(envPath)) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const content = fs.readFileSync(envPath, 'utf-8');
|
|
128
|
+
const lines = content.split('\n');
|
|
129
|
+
let lastComment = '';
|
|
130
|
+
|
|
131
|
+
for (const line of lines) {
|
|
132
|
+
const trimmed = line.trim();
|
|
133
|
+
|
|
134
|
+
// Track comments
|
|
135
|
+
if (trimmed.startsWith('#')) {
|
|
136
|
+
lastComment = trimmed.slice(1).trim();
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Skip empty lines
|
|
141
|
+
if (!trimmed) {
|
|
142
|
+
lastComment = '';
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Parse variable
|
|
147
|
+
const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
148
|
+
if (match) {
|
|
149
|
+
envVars.push({
|
|
150
|
+
name: match[1],
|
|
151
|
+
example: match[2],
|
|
152
|
+
comment: lastComment,
|
|
153
|
+
});
|
|
154
|
+
lastComment = '';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
// Ignore read errors
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return envVars;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Recursively find files matching patterns
|
|
166
|
+
* @param {string} dir - Directory to search
|
|
167
|
+
* @param {string[]} extensions - File extensions to match
|
|
168
|
+
* @returns {string[]} Array of file paths
|
|
169
|
+
*/
|
|
170
|
+
findFiles(dir, extensions = ['.js', '.ts']) {
|
|
171
|
+
const files = [];
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
175
|
+
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
const fullPath = path.join(dir, entry.name);
|
|
178
|
+
|
|
179
|
+
if (entry.isDirectory()) {
|
|
180
|
+
if (!IGNORE_DIRS.includes(entry.name) && !entry.name.startsWith('.')) {
|
|
181
|
+
files.push(...this.findFiles(fullPath, extensions));
|
|
182
|
+
}
|
|
183
|
+
} else if (entry.isFile()) {
|
|
184
|
+
const ext = path.extname(entry.name);
|
|
185
|
+
if (extensions.includes(ext)) {
|
|
186
|
+
files.push(fullPath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
// Ignore read errors
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return files;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Detect API endpoints from route files
|
|
199
|
+
* @returns {Array} Array of endpoint objects with method and path
|
|
200
|
+
*/
|
|
201
|
+
detectApiEndpoints() {
|
|
202
|
+
const endpoints = [];
|
|
203
|
+
|
|
204
|
+
// Find potential route files
|
|
205
|
+
const allFiles = this.findFiles(this.repoPath);
|
|
206
|
+
const routeFiles = allFiles.filter(f => {
|
|
207
|
+
const rel = path.relative(this.repoPath, f).toLowerCase();
|
|
208
|
+
return (
|
|
209
|
+
rel.includes('route') ||
|
|
210
|
+
rel.includes('api') ||
|
|
211
|
+
rel.includes('controller') ||
|
|
212
|
+
rel.includes('endpoint')
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
for (const file of routeFiles) {
|
|
217
|
+
try {
|
|
218
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
219
|
+
const routes = extractRoutes(content, file);
|
|
220
|
+
endpoints.push(...routes);
|
|
221
|
+
} catch (err) {
|
|
222
|
+
// Ignore read errors
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Deduplicate
|
|
227
|
+
const seen = new Set();
|
|
228
|
+
return endpoints.filter(e => {
|
|
229
|
+
const key = `${e.method}:${e.path}`;
|
|
230
|
+
if (seen.has(key)) return false;
|
|
231
|
+
seen.add(key);
|
|
232
|
+
return true;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Generate the installation section
|
|
238
|
+
* @returns {string} Markdown for installation section
|
|
239
|
+
*/
|
|
240
|
+
generateInstallationSection() {
|
|
241
|
+
const info = this.extractProjectInfo();
|
|
242
|
+
const lines = ['## Installation', ''];
|
|
243
|
+
|
|
244
|
+
// Add git clone if repository is available
|
|
245
|
+
if (info.repository) {
|
|
246
|
+
let repoUrl = '';
|
|
247
|
+
if (typeof info.repository === 'string') {
|
|
248
|
+
repoUrl = info.repository;
|
|
249
|
+
} else if (info.repository.url) {
|
|
250
|
+
repoUrl = info.repository.url;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (repoUrl) {
|
|
254
|
+
// Clean up git URL
|
|
255
|
+
repoUrl = repoUrl.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
256
|
+
lines.push('```bash');
|
|
257
|
+
lines.push(`git clone ${repoUrl}`);
|
|
258
|
+
lines.push(`cd ${info.name.replace(/^@[^/]+\//, '')}`);
|
|
259
|
+
lines.push('```');
|
|
260
|
+
lines.push('');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
lines.push('```bash');
|
|
265
|
+
lines.push('npm install');
|
|
266
|
+
lines.push('```');
|
|
267
|
+
|
|
268
|
+
return lines.join('\n');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Generate scripts section markdown
|
|
273
|
+
* @param {Array} scripts - Array of script objects
|
|
274
|
+
* @returns {string} Markdown content
|
|
275
|
+
*/
|
|
276
|
+
generateScriptsSection(scripts) {
|
|
277
|
+
if (!scripts.length) return '';
|
|
278
|
+
|
|
279
|
+
const lines = ['## Scripts', ''];
|
|
280
|
+
|
|
281
|
+
for (const script of scripts) {
|
|
282
|
+
lines.push(`### \`npm run ${script.name}\``);
|
|
283
|
+
lines.push('');
|
|
284
|
+
lines.push('```bash');
|
|
285
|
+
lines.push(`npm run ${script.name}`);
|
|
286
|
+
lines.push('```');
|
|
287
|
+
lines.push('');
|
|
288
|
+
lines.push(`Runs: \`${script.command}\``);
|
|
289
|
+
lines.push('');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return lines.join('\n');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Generate dependencies section markdown
|
|
297
|
+
* @param {Object} deps - Dependencies object with runtime and dev arrays
|
|
298
|
+
* @returns {string} Markdown content
|
|
299
|
+
*/
|
|
300
|
+
generateDependenciesSection(deps) {
|
|
301
|
+
if (!deps.runtime.length && !deps.dev.length) return '';
|
|
302
|
+
|
|
303
|
+
const lines = ['## Dependencies', ''];
|
|
304
|
+
|
|
305
|
+
if (deps.runtime.length) {
|
|
306
|
+
lines.push('### Runtime Dependencies');
|
|
307
|
+
lines.push('');
|
|
308
|
+
lines.push('| Package | Version |');
|
|
309
|
+
lines.push('|---------|---------|');
|
|
310
|
+
for (const dep of deps.runtime.slice(0, 15)) {
|
|
311
|
+
// Limit to top 15
|
|
312
|
+
lines.push(`| ${dep.name} | ${dep.version} |`);
|
|
313
|
+
}
|
|
314
|
+
if (deps.runtime.length > 15) {
|
|
315
|
+
lines.push(`| ... | *(${deps.runtime.length - 15} more)* |`);
|
|
316
|
+
}
|
|
317
|
+
lines.push('');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (deps.dev.length) {
|
|
321
|
+
lines.push('### Development Dependencies');
|
|
322
|
+
lines.push('');
|
|
323
|
+
lines.push('| Package | Version |');
|
|
324
|
+
lines.push('|---------|---------|');
|
|
325
|
+
for (const dep of deps.dev.slice(0, 10)) {
|
|
326
|
+
// Limit to top 10
|
|
327
|
+
lines.push(`| ${dep.name} | ${dep.version} |`);
|
|
328
|
+
}
|
|
329
|
+
if (deps.dev.length > 10) {
|
|
330
|
+
lines.push(`| ... | *(${deps.dev.length - 10} more)* |`);
|
|
331
|
+
}
|
|
332
|
+
lines.push('');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return lines.join('\n');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Generate environment variables section markdown
|
|
340
|
+
* @param {Array} envVars - Array of env var objects
|
|
341
|
+
* @returns {string} Markdown content
|
|
342
|
+
*/
|
|
343
|
+
generateEnvVarsSection(envVars) {
|
|
344
|
+
if (!envVars.length) return '';
|
|
345
|
+
|
|
346
|
+
const lines = ['## Environment Variables', ''];
|
|
347
|
+
lines.push('Copy `.env.example` to `.env` and configure the following variables:');
|
|
348
|
+
lines.push('');
|
|
349
|
+
lines.push('| Variable | Description | Example |');
|
|
350
|
+
lines.push('|----------|-------------|---------|');
|
|
351
|
+
|
|
352
|
+
for (const envVar of envVars) {
|
|
353
|
+
const desc = envVar.comment || '-';
|
|
354
|
+
const example = envVar.example || '-';
|
|
355
|
+
lines.push(`| \`${envVar.name}\` | ${desc} | \`${example}\` |`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
lines.push('');
|
|
359
|
+
return lines.join('\n');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Generate API endpoints section markdown
|
|
364
|
+
* @param {Array} endpoints - Array of endpoint objects
|
|
365
|
+
* @returns {string} Markdown content
|
|
366
|
+
*/
|
|
367
|
+
generateApiSection(endpoints) {
|
|
368
|
+
if (!endpoints.length) return '';
|
|
369
|
+
|
|
370
|
+
const lines = ['## API Endpoints', ''];
|
|
371
|
+
lines.push('| Method | Path |');
|
|
372
|
+
lines.push('|--------|------|');
|
|
373
|
+
|
|
374
|
+
for (const endpoint of endpoints) {
|
|
375
|
+
lines.push(`| ${endpoint.method} | \`${endpoint.path}\` |`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
lines.push('');
|
|
379
|
+
return lines.join('\n');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Generate the full README content
|
|
384
|
+
* @returns {string} Complete README markdown
|
|
385
|
+
*/
|
|
386
|
+
generate() {
|
|
387
|
+
const info = this.extractProjectInfo();
|
|
388
|
+
const scripts = this.extractScripts();
|
|
389
|
+
const deps = this.extractDependencies();
|
|
390
|
+
const envVars = this.extractEnvVars();
|
|
391
|
+
const endpoints = this.detectApiEndpoints();
|
|
392
|
+
|
|
393
|
+
const sections = [];
|
|
394
|
+
|
|
395
|
+
// Title
|
|
396
|
+
sections.push(`# ${info.name}`);
|
|
397
|
+
sections.push('');
|
|
398
|
+
|
|
399
|
+
// Description
|
|
400
|
+
if (info.description) {
|
|
401
|
+
sections.push(info.description);
|
|
402
|
+
sections.push('');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Installation
|
|
406
|
+
sections.push(this.generateInstallationSection());
|
|
407
|
+
sections.push('');
|
|
408
|
+
|
|
409
|
+
// Scripts
|
|
410
|
+
const scriptsSection = this.generateScriptsSection(scripts);
|
|
411
|
+
if (scriptsSection) {
|
|
412
|
+
sections.push(scriptsSection);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Environment variables
|
|
416
|
+
const envSection = this.generateEnvVarsSection(envVars);
|
|
417
|
+
if (envSection) {
|
|
418
|
+
sections.push(envSection);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// API endpoints
|
|
422
|
+
const apiSection = this.generateApiSection(endpoints);
|
|
423
|
+
if (apiSection) {
|
|
424
|
+
sections.push(apiSection);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Dependencies
|
|
428
|
+
const depsSection = this.generateDependenciesSection(deps);
|
|
429
|
+
if (depsSection) {
|
|
430
|
+
sections.push(depsSection);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// License
|
|
434
|
+
if (info.license) {
|
|
435
|
+
sections.push('## License');
|
|
436
|
+
sections.push('');
|
|
437
|
+
sections.push(info.license);
|
|
438
|
+
sections.push('');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return sections.join('\n').trim() + '\n';
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Write README to disk
|
|
446
|
+
* @param {string} outputPath - Custom output path (defaults to README.md in repo)
|
|
447
|
+
*/
|
|
448
|
+
write(outputPath) {
|
|
449
|
+
const readme = this.generate();
|
|
450
|
+
const target = outputPath || path.join(this.repoPath, 'README.md');
|
|
451
|
+
fs.writeFileSync(target, readme, 'utf-8');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Convenience function to generate README for a path
|
|
457
|
+
* @param {string} repoPath - Path to repository
|
|
458
|
+
* @returns {string} Generated README content
|
|
459
|
+
*/
|
|
460
|
+
function generateReadme(repoPath) {
|
|
461
|
+
const generator = new ReadmeGenerator(repoPath);
|
|
462
|
+
return generator.generate();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Factory function to create a README generator
|
|
467
|
+
* @param {string} repoPath - Path to repository
|
|
468
|
+
* @returns {Object} Generator instance with bound methods
|
|
469
|
+
*/
|
|
470
|
+
function createReadmeGenerator(repoPath) {
|
|
471
|
+
const generator = new ReadmeGenerator(repoPath);
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
generate: () => generator.generate(),
|
|
475
|
+
write: (outputPath) => generator.write(outputPath),
|
|
476
|
+
extractProjectInfo: () => generator.extractProjectInfo(),
|
|
477
|
+
extractScripts: () => generator.extractScripts(),
|
|
478
|
+
extractDependencies: () => generator.extractDependencies(),
|
|
479
|
+
extractEnvVars: () => generator.extractEnvVars(),
|
|
480
|
+
detectApiEndpoints: () => generator.detectApiEndpoints(),
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
module.exports = {
|
|
485
|
+
ReadmeGenerator,
|
|
486
|
+
generateReadme,
|
|
487
|
+
createReadmeGenerator,
|
|
488
|
+
ROUTE_FILE_PATTERNS,
|
|
489
|
+
IGNORE_DIRS,
|
|
490
|
+
};
|