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.
Files changed (105) hide show
  1. package/dashboard/dist/components/AuditPane.d.ts +30 -0
  2. package/dashboard/dist/components/AuditPane.js +127 -0
  3. package/dashboard/dist/components/AuditPane.test.d.ts +1 -0
  4. package/dashboard/dist/components/AuditPane.test.js +339 -0
  5. package/dashboard/dist/components/CompliancePane.d.ts +39 -0
  6. package/dashboard/dist/components/CompliancePane.js +96 -0
  7. package/dashboard/dist/components/CompliancePane.test.d.ts +1 -0
  8. package/dashboard/dist/components/CompliancePane.test.js +183 -0
  9. package/dashboard/dist/components/SSOPane.d.ts +36 -0
  10. package/dashboard/dist/components/SSOPane.js +71 -0
  11. package/dashboard/dist/components/SSOPane.test.d.ts +1 -0
  12. package/dashboard/dist/components/SSOPane.test.js +155 -0
  13. package/dashboard/dist/components/WorkspaceDocsPane.js +0 -16
  14. package/dashboard/dist/components/WorkspacePane.d.ts +1 -1
  15. package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
  16. package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
  17. package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
  18. package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
  19. package/package.json +1 -1
  20. package/server/lib/access-control-doc.js +541 -0
  21. package/server/lib/access-control-doc.test.js +672 -0
  22. package/server/lib/adr-generator.js +423 -0
  23. package/server/lib/adr-generator.test.js +586 -0
  24. package/server/lib/agent-progress-monitor.js +223 -0
  25. package/server/lib/agent-progress-monitor.test.js +202 -0
  26. package/server/lib/audit-attribution.js +191 -0
  27. package/server/lib/audit-attribution.test.js +359 -0
  28. package/server/lib/audit-classifier.js +202 -0
  29. package/server/lib/audit-classifier.test.js +209 -0
  30. package/server/lib/audit-command.js +275 -0
  31. package/server/lib/audit-command.test.js +325 -0
  32. package/server/lib/audit-exporter.js +380 -0
  33. package/server/lib/audit-exporter.test.js +464 -0
  34. package/server/lib/audit-logger.js +236 -0
  35. package/server/lib/audit-logger.test.js +364 -0
  36. package/server/lib/audit-query.js +257 -0
  37. package/server/lib/audit-query.test.js +352 -0
  38. package/server/lib/audit-storage.js +269 -0
  39. package/server/lib/audit-storage.test.js +272 -0
  40. package/server/lib/bulk-repo-init.js +342 -0
  41. package/server/lib/bulk-repo-init.test.js +388 -0
  42. package/server/lib/compliance-checklist.js +866 -0
  43. package/server/lib/compliance-checklist.test.js +476 -0
  44. package/server/lib/compliance-command.js +616 -0
  45. package/server/lib/compliance-command.test.js +551 -0
  46. package/server/lib/compliance-reporter.js +692 -0
  47. package/server/lib/compliance-reporter.test.js +707 -0
  48. package/server/lib/data-flow-doc.js +665 -0
  49. package/server/lib/data-flow-doc.test.js +659 -0
  50. package/server/lib/ephemeral-storage.js +249 -0
  51. package/server/lib/ephemeral-storage.test.js +254 -0
  52. package/server/lib/evidence-collector.js +627 -0
  53. package/server/lib/evidence-collector.test.js +901 -0
  54. package/server/lib/flow-diagram-generator.js +474 -0
  55. package/server/lib/flow-diagram-generator.test.js +446 -0
  56. package/server/lib/idp-manager.js +626 -0
  57. package/server/lib/idp-manager.test.js +587 -0
  58. package/server/lib/memory-exclusion.js +326 -0
  59. package/server/lib/memory-exclusion.test.js +241 -0
  60. package/server/lib/mfa-handler.js +452 -0
  61. package/server/lib/mfa-handler.test.js +490 -0
  62. package/server/lib/oauth-flow.js +375 -0
  63. package/server/lib/oauth-flow.test.js +487 -0
  64. package/server/lib/oauth-registry.js +190 -0
  65. package/server/lib/oauth-registry.test.js +306 -0
  66. package/server/lib/readme-generator.js +490 -0
  67. package/server/lib/readme-generator.test.js +493 -0
  68. package/server/lib/repo-dependency-tracker.js +261 -0
  69. package/server/lib/repo-dependency-tracker.test.js +350 -0
  70. package/server/lib/retention-policy.js +281 -0
  71. package/server/lib/retention-policy.test.js +486 -0
  72. package/server/lib/role-mapper.js +236 -0
  73. package/server/lib/role-mapper.test.js +395 -0
  74. package/server/lib/saml-provider.js +765 -0
  75. package/server/lib/saml-provider.test.js +643 -0
  76. package/server/lib/security-policy-generator.js +682 -0
  77. package/server/lib/security-policy-generator.test.js +544 -0
  78. package/server/lib/sensitive-detector.js +112 -0
  79. package/server/lib/sensitive-detector.test.js +209 -0
  80. package/server/lib/service-interaction-diagram.js +700 -0
  81. package/server/lib/service-interaction-diagram.test.js +638 -0
  82. package/server/lib/service-summary.js +553 -0
  83. package/server/lib/service-summary.test.js +619 -0
  84. package/server/lib/session-purge.js +460 -0
  85. package/server/lib/session-purge.test.js +312 -0
  86. package/server/lib/sso-command.js +544 -0
  87. package/server/lib/sso-command.test.js +552 -0
  88. package/server/lib/sso-session.js +492 -0
  89. package/server/lib/sso-session.test.js +670 -0
  90. package/server/lib/workspace-command.js +249 -0
  91. package/server/lib/workspace-command.test.js +264 -0
  92. package/server/lib/workspace-config.js +270 -0
  93. package/server/lib/workspace-config.test.js +312 -0
  94. package/server/lib/workspace-docs-command.js +547 -0
  95. package/server/lib/workspace-docs-command.test.js +692 -0
  96. package/server/lib/workspace-memory.js +451 -0
  97. package/server/lib/workspace-memory.test.js +403 -0
  98. package/server/lib/workspace-scanner.js +452 -0
  99. package/server/lib/workspace-scanner.test.js +677 -0
  100. package/server/lib/workspace-test-runner.js +315 -0
  101. package/server/lib/workspace-test-runner.test.js +294 -0
  102. package/server/lib/zero-retention-command.js +439 -0
  103. package/server/lib/zero-retention-command.test.js +448 -0
  104. package/server/lib/zero-retention.js +322 -0
  105. 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
+ };