tlc-claude-code 1.2.28 → 1.3.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 (87) hide show
  1. package/README.md +9 -4
  2. package/dashboard/dist/components/UsagePane.d.ts +13 -0
  3. package/dashboard/dist/components/UsagePane.js +51 -0
  4. package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
  5. package/dashboard/dist/components/UsagePane.test.js +142 -0
  6. package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
  7. package/dashboard/dist/components/WorkspaceDocsPane.js +146 -0
  8. package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
  9. package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
  10. package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
  11. package/dashboard/dist/components/WorkspacePane.js +17 -0
  12. package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
  13. package/dashboard/dist/components/WorkspacePane.test.js +84 -0
  14. package/package.json +15 -4
  15. package/scripts/capture-screenshots.js +170 -0
  16. package/scripts/docs-update.js +253 -0
  17. package/scripts/generate-screenshots.js +321 -0
  18. package/scripts/project-docs.js +377 -0
  19. package/scripts/vps-setup.sh +477 -0
  20. package/server/lib/architecture-command.js +450 -0
  21. package/server/lib/architecture-command.test.js +754 -0
  22. package/server/lib/ast-analyzer.js +324 -0
  23. package/server/lib/ast-analyzer.test.js +437 -0
  24. package/server/lib/auth-system.test.js +4 -1
  25. package/server/lib/boundary-detector.js +427 -0
  26. package/server/lib/boundary-detector.test.js +320 -0
  27. package/server/lib/budget-alerts.js +138 -0
  28. package/server/lib/budget-alerts.test.js +235 -0
  29. package/server/lib/candidates-tracker.js +210 -0
  30. package/server/lib/candidates-tracker.test.js +300 -0
  31. package/server/lib/checkpoint-manager.js +251 -0
  32. package/server/lib/checkpoint-manager.test.js +474 -0
  33. package/server/lib/circular-detector.js +337 -0
  34. package/server/lib/circular-detector.test.js +353 -0
  35. package/server/lib/cohesion-analyzer.js +310 -0
  36. package/server/lib/cohesion-analyzer.test.js +447 -0
  37. package/server/lib/contract-testing.js +625 -0
  38. package/server/lib/contract-testing.test.js +342 -0
  39. package/server/lib/conversion-planner.js +469 -0
  40. package/server/lib/conversion-planner.test.js +361 -0
  41. package/server/lib/convert-command.js +351 -0
  42. package/server/lib/convert-command.test.js +608 -0
  43. package/server/lib/coupling-calculator.js +189 -0
  44. package/server/lib/coupling-calculator.test.js +509 -0
  45. package/server/lib/dependency-graph.js +367 -0
  46. package/server/lib/dependency-graph.test.js +516 -0
  47. package/server/lib/duplication-detector.js +349 -0
  48. package/server/lib/duplication-detector.test.js +401 -0
  49. package/server/lib/example-service.js +616 -0
  50. package/server/lib/example-service.test.js +397 -0
  51. package/server/lib/impact-scorer.js +184 -0
  52. package/server/lib/impact-scorer.test.js +211 -0
  53. package/server/lib/mermaid-generator.js +358 -0
  54. package/server/lib/mermaid-generator.test.js +301 -0
  55. package/server/lib/messaging-patterns.js +750 -0
  56. package/server/lib/messaging-patterns.test.js +213 -0
  57. package/server/lib/microservice-template.js +386 -0
  58. package/server/lib/microservice-template.test.js +325 -0
  59. package/server/lib/new-project-microservice.js +450 -0
  60. package/server/lib/new-project-microservice.test.js +600 -0
  61. package/server/lib/refactor-command.js +326 -0
  62. package/server/lib/refactor-command.test.js +528 -0
  63. package/server/lib/refactor-executor.js +254 -0
  64. package/server/lib/refactor-executor.test.js +305 -0
  65. package/server/lib/refactor-observer.js +292 -0
  66. package/server/lib/refactor-observer.test.js +422 -0
  67. package/server/lib/refactor-progress.js +193 -0
  68. package/server/lib/refactor-progress.test.js +251 -0
  69. package/server/lib/refactor-reporter.js +237 -0
  70. package/server/lib/refactor-reporter.test.js +247 -0
  71. package/server/lib/semantic-analyzer.js +198 -0
  72. package/server/lib/semantic-analyzer.test.js +474 -0
  73. package/server/lib/service-scaffold.js +486 -0
  74. package/server/lib/service-scaffold.test.js +373 -0
  75. package/server/lib/shared-kernel.js +578 -0
  76. package/server/lib/shared-kernel.test.js +255 -0
  77. package/server/lib/traefik-config.js +282 -0
  78. package/server/lib/traefik-config.test.js +312 -0
  79. package/server/lib/usage-command.js +218 -0
  80. package/server/lib/usage-command.test.js +391 -0
  81. package/server/lib/usage-formatter.js +192 -0
  82. package/server/lib/usage-formatter.test.js +267 -0
  83. package/server/lib/usage-history.js +122 -0
  84. package/server/lib/usage-history.test.js +206 -0
  85. package/server/package-lock.json +14 -0
  86. package/server/package.json +1 -0
  87. package/templates/docs-sync.yml +91 -0
@@ -0,0 +1,616 @@
1
+ /**
2
+ * Example Service Template Generator
3
+ * Generates a complete working microservice template
4
+ */
5
+
6
+ class ExampleService {
7
+ constructor(options = {}) {
8
+ this.options = options;
9
+ }
10
+
11
+ /**
12
+ * Generate complete service
13
+ * @param {Object} config - Configuration object
14
+ * @param {string} config.name - Service name (e.g., 'user')
15
+ * @param {number} config.port - Service port (e.g., 3001)
16
+ * @param {string} config.database - Database type (e.g., 'postgres')
17
+ * @returns {Object} Generated structure { directories: [...], files: [...] }
18
+ */
19
+ generate(config) {
20
+ const name = config.name || 'service';
21
+
22
+ const directories = [
23
+ name,
24
+ `${name}/src`,
25
+ `${name}/src/routes`,
26
+ `${name}/tests`,
27
+ ];
28
+
29
+ // Add migrations directory if database is configured
30
+ if (config.database) {
31
+ directories.push(`${name}/migrations`);
32
+ }
33
+
34
+ const files = [];
35
+
36
+ // Package.json
37
+ files.push({
38
+ path: `${name}/package.json`,
39
+ content: this.generatePackageJson(config),
40
+ });
41
+
42
+ // Entry point
43
+ files.push({
44
+ path: `${name}/src/index.js`,
45
+ content: this.generateIndex(config),
46
+ });
47
+
48
+ // Routes
49
+ files.push({
50
+ path: `${name}/src/routes/index.js`,
51
+ content: this.generateRoutes(config),
52
+ });
53
+
54
+ // Dockerfile
55
+ files.push({
56
+ path: `${name}/Dockerfile`,
57
+ content: this.generateDockerfile(config),
58
+ });
59
+
60
+ // Docker Compose
61
+ files.push({
62
+ path: `${name}/docker-compose.yml`,
63
+ content: this.generateDockerCompose(config),
64
+ });
65
+
66
+ // Migrations (if database configured)
67
+ if (config.database) {
68
+ const migration = this.generateMigrations(config);
69
+ files.push({
70
+ path: `${name}/migrations/${migration.path}`,
71
+ content: migration.content,
72
+ });
73
+ }
74
+
75
+ // Tests
76
+ const tests = this.generateTests(config);
77
+ files.push({
78
+ path: `${name}/tests/routes.test.js`,
79
+ content: tests.unit,
80
+ });
81
+ files.push({
82
+ path: `${name}/tests/integration.test.js`,
83
+ content: tests.integration,
84
+ });
85
+
86
+ return { directories, files };
87
+ }
88
+
89
+ /**
90
+ * Generate package.json for service
91
+ * @param {Object} config - Configuration object
92
+ * @returns {string} JSON string of package.json
93
+ */
94
+ generatePackageJson(config) {
95
+ const name = config.name || 'service';
96
+
97
+ const pkg = {
98
+ name,
99
+ version: '0.1.0',
100
+ description: `${name} microservice`,
101
+ main: 'src/index.js',
102
+ scripts: {
103
+ start: 'node src/index.js',
104
+ dev: 'node --watch src/index.js',
105
+ test: 'vitest run',
106
+ migrate: 'node migrations/run.js',
107
+ },
108
+ dependencies: {
109
+ express: '^4.18.0',
110
+ },
111
+ devDependencies: {
112
+ vitest: '^4.0.0',
113
+ },
114
+ };
115
+
116
+ // Add database driver if configured
117
+ if (config.database === 'postgres') {
118
+ pkg.dependencies.pg = '^8.11.0';
119
+ }
120
+
121
+ return JSON.stringify(pkg, null, 2);
122
+ }
123
+
124
+ /**
125
+ * Generate src/index.js entry point
126
+ * @param {Object} config - Configuration object
127
+ * @returns {string} JavaScript source code
128
+ */
129
+ generateIndex(config) {
130
+ const name = config.name || 'service';
131
+ const port = config.port || 3000;
132
+
133
+ return `/**
134
+ * ${name} Service Entry Point
135
+ */
136
+
137
+ const express = require('express');
138
+ const routes = require('./routes');
139
+
140
+ const app = express();
141
+ const PORT = process.env.PORT || ${port};
142
+
143
+ // Middleware
144
+ app.use(express.json());
145
+
146
+ // Health check endpoint
147
+ app.get('/health', (req, res) => {
148
+ res.json({
149
+ status: 'healthy',
150
+ service: '${name}',
151
+ timestamp: new Date().toISOString(),
152
+ });
153
+ });
154
+
155
+ // Mount routes
156
+ app.use('/api/${name}', routes);
157
+
158
+ // Error handling middleware
159
+ app.use((err, req, res, next) => {
160
+ console.error(err.stack);
161
+ res.status(500).json({
162
+ error: 'Internal Server Error',
163
+ message: process.env.NODE_ENV === 'development' ? err.message : undefined,
164
+ });
165
+ });
166
+
167
+ app.listen(PORT, () => {
168
+ console.log(\`${name} service listening on port \${PORT}\`);
169
+ });
170
+
171
+ module.exports = app;
172
+ `;
173
+ }
174
+
175
+ /**
176
+ * Generate CRUD routes
177
+ * @param {Object} config - Configuration object
178
+ * @returns {string} JavaScript source code
179
+ */
180
+ generateRoutes(config) {
181
+ const name = config.name || 'service';
182
+ const entity = this.singularize(name);
183
+ const Entity = this.capitalize(entity);
184
+
185
+ return `/**
186
+ * ${name} Routes
187
+ * CRUD operations for ${entity} entity
188
+ */
189
+
190
+ const express = require('express');
191
+ const router = express.Router();
192
+
193
+ // In-memory storage (replace with database)
194
+ let items = [];
195
+ let nextId = 1;
196
+
197
+ /**
198
+ * GET / - List all ${name}
199
+ */
200
+ router.get('/', (req, res) => {
201
+ res.json({
202
+ data: items,
203
+ count: items.length,
204
+ });
205
+ });
206
+
207
+ /**
208
+ * GET /:id - Get ${entity} by id
209
+ */
210
+ router.get('/:id', (req, res) => {
211
+ const item = items.find(i => i.id === parseInt(req.params.id));
212
+
213
+ if (!item) {
214
+ return res.status(404).json({ error: '${Entity} not found' });
215
+ }
216
+
217
+ res.json({ data: item });
218
+ });
219
+
220
+ /**
221
+ * POST / - Create new ${entity}
222
+ */
223
+ router.post('/', (req, res) => {
224
+ const item = {
225
+ id: nextId++,
226
+ ...req.body,
227
+ createdAt: new Date().toISOString(),
228
+ updatedAt: new Date().toISOString(),
229
+ };
230
+
231
+ items.push(item);
232
+ res.status(201).json({ data: item });
233
+ });
234
+
235
+ /**
236
+ * PUT /:id - Update ${entity}
237
+ */
238
+ router.put('/:id', (req, res) => {
239
+ const index = items.findIndex(i => i.id === parseInt(req.params.id));
240
+
241
+ if (index === -1) {
242
+ return res.status(404).json({ error: '${Entity} not found' });
243
+ }
244
+
245
+ items[index] = {
246
+ ...items[index],
247
+ ...req.body,
248
+ updatedAt: new Date().toISOString(),
249
+ };
250
+
251
+ res.json({ data: items[index] });
252
+ });
253
+
254
+ /**
255
+ * DELETE /:id - Delete ${entity}
256
+ */
257
+ router.delete('/:id', (req, res) => {
258
+ const index = items.findIndex(i => i.id === parseInt(req.params.id));
259
+
260
+ if (index === -1) {
261
+ return res.status(404).json({ error: '${Entity} not found' });
262
+ }
263
+
264
+ items.splice(index, 1);
265
+ res.status(204).send();
266
+ });
267
+
268
+ module.exports = router;
269
+ `;
270
+ }
271
+
272
+ /**
273
+ * Generate database migrations
274
+ * @param {Object} config - Configuration object
275
+ * @returns {Object} Migration file { path, content }
276
+ */
277
+ generateMigrations(config) {
278
+ const name = config.name || 'service';
279
+ const entity = this.singularize(name);
280
+ const table = this.pluralize(entity);
281
+ const timestamp = Date.now();
282
+
283
+ const content = `/**
284
+ * Initial ${name} Schema Migration
285
+ */
286
+
287
+ /**
288
+ * Apply migration
289
+ * @param {Object} client - Database client
290
+ */
291
+ async function up(client) {
292
+ await client.query(\`
293
+ CREATE TABLE IF NOT EXISTS ${table} (
294
+ id SERIAL PRIMARY KEY,
295
+ name VARCHAR(255) NOT NULL,
296
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
297
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
298
+ );
299
+ \`);
300
+
301
+ console.log('Created ${table} table');
302
+ }
303
+
304
+ /**
305
+ * Rollback migration
306
+ * @param {Object} client - Database client
307
+ */
308
+ async function down(client) {
309
+ await client.query(\`
310
+ DROP TABLE IF EXISTS ${table};
311
+ \`);
312
+
313
+ console.log('Dropped ${table} table');
314
+ }
315
+
316
+ module.exports = { up, down };
317
+ `;
318
+
319
+ return {
320
+ path: `${timestamp}_initial_${name}_schema.js`,
321
+ content,
322
+ };
323
+ }
324
+
325
+ /**
326
+ * Generate test files
327
+ * @param {Object} config - Configuration object
328
+ * @returns {Object} Test files { unit, integration }
329
+ */
330
+ generateTests(config) {
331
+ const name = config.name || 'service';
332
+ const entity = this.singularize(name);
333
+ const Entity = this.capitalize(entity);
334
+
335
+ const unit = `/**
336
+ * ${name} Routes Unit Tests
337
+ */
338
+
339
+ import { describe, it, expect, beforeEach } from 'vitest';
340
+
341
+ describe('${name} routes', () => {
342
+ describe('GET /', () => {
343
+ it('returns list of ${name}', async () => {
344
+ // Test implementation
345
+ expect(true).toBe(true);
346
+ });
347
+
348
+ it('returns empty array when no data', async () => {
349
+ expect([]).toEqual([]);
350
+ });
351
+ });
352
+
353
+ describe('GET /:id', () => {
354
+ it('returns ${entity} by id', async () => {
355
+ expect(true).toBe(true);
356
+ });
357
+
358
+ it('returns 404 for non-existent id', async () => {
359
+ expect(404).toBe(404);
360
+ });
361
+ });
362
+
363
+ describe('POST /', () => {
364
+ it('creates new ${entity}', async () => {
365
+ expect(true).toBe(true);
366
+ });
367
+
368
+ it('returns 201 on success', async () => {
369
+ expect(201).toBe(201);
370
+ });
371
+ });
372
+
373
+ describe('PUT /:id', () => {
374
+ it('updates existing ${entity}', async () => {
375
+ expect(true).toBe(true);
376
+ });
377
+
378
+ it('returns 404 for non-existent id', async () => {
379
+ expect(404).toBe(404);
380
+ });
381
+ });
382
+
383
+ describe('DELETE /:id', () => {
384
+ it('deletes ${entity}', async () => {
385
+ expect(true).toBe(true);
386
+ });
387
+
388
+ it('returns 204 on success', async () => {
389
+ expect(204).toBe(204);
390
+ });
391
+ });
392
+ });
393
+ `;
394
+
395
+ const integration = `/**
396
+ * ${name} Integration Tests
397
+ */
398
+
399
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
400
+
401
+ describe('${name} integration', () => {
402
+ let server;
403
+
404
+ beforeAll(() => {
405
+ // Start test server
406
+ });
407
+
408
+ afterAll(() => {
409
+ // Stop test server
410
+ });
411
+
412
+ it('creates and retrieves ${entity}', async () => {
413
+ // Integration test implementation
414
+ expect(true).toBe(true);
415
+ });
416
+
417
+ it('full CRUD cycle', async () => {
418
+ // Create -> Read -> Update -> Delete
419
+ expect(true).toBe(true);
420
+ });
421
+ });
422
+ `;
423
+
424
+ return { unit, integration };
425
+ }
426
+
427
+ /**
428
+ * Generate Dockerfile with multi-stage build
429
+ * @param {Object} config - Configuration object
430
+ * @returns {string} Dockerfile content
431
+ */
432
+ generateDockerfile(config) {
433
+ const name = config.name || 'service';
434
+ const port = config.port || 3000;
435
+
436
+ return `# ${name} service Dockerfile
437
+ # Multi-stage build for optimal image size
438
+
439
+ # Build stage
440
+ FROM node:20-alpine AS builder
441
+
442
+ WORKDIR /app
443
+
444
+ # Install dependencies
445
+ COPY package*.json ./
446
+ RUN npm ci --only=production
447
+
448
+ # Production stage
449
+ FROM node:20-alpine AS production
450
+
451
+ WORKDIR /app
452
+
453
+ # Create non-root user for security
454
+ RUN addgroup -g 1001 -S nodejs && \\
455
+ adduser -S nodejs -u 1001
456
+
457
+ # Copy from builder
458
+ COPY --from=builder /app/node_modules ./node_modules
459
+ COPY src/ ./src/
460
+
461
+ # Set environment
462
+ ENV NODE_ENV=production
463
+ ENV PORT=${port}
464
+
465
+ # Switch to non-root user
466
+ USER nodejs
467
+
468
+ # Expose port
469
+ EXPOSE ${port}
470
+
471
+ # Health check
472
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
473
+ CMD wget --no-verbose --tries=1 --spider http://localhost:${port}/health || exit 1
474
+
475
+ # Run service
476
+ CMD ["node", "src/index.js"]
477
+ `;
478
+ }
479
+
480
+ /**
481
+ * Generate docker-compose.yml for service
482
+ * @param {Object} config - Configuration object
483
+ * @returns {string} YAML content
484
+ */
485
+ generateDockerCompose(config) {
486
+ const name = config.name || 'service';
487
+ const port = config.port || 3000;
488
+ const serviceName = name.toLowerCase().replace(/[^a-z0-9]/g, '-');
489
+
490
+ const compose = {
491
+ version: '3.8',
492
+ services: {
493
+ [serviceName]: {
494
+ build: {
495
+ context: '.',
496
+ dockerfile: 'Dockerfile',
497
+ },
498
+ ports: [`${port}:${port}`],
499
+ environment: {
500
+ NODE_ENV: 'development',
501
+ PORT: port,
502
+ },
503
+ networks: ['backend'],
504
+ },
505
+ },
506
+ networks: {
507
+ backend: {
508
+ driver: 'bridge',
509
+ },
510
+ },
511
+ };
512
+
513
+ // Add database if configured
514
+ if (config.database === 'postgres') {
515
+ compose.services[serviceName].environment.DATABASE_URL =
516
+ `postgres://${serviceName}:dev_password@postgres:5432/${serviceName}`;
517
+ compose.services[serviceName].depends_on = ['postgres'];
518
+
519
+ compose.services.postgres = {
520
+ image: 'postgres:15-alpine',
521
+ environment: {
522
+ POSTGRES_DB: serviceName,
523
+ POSTGRES_USER: serviceName,
524
+ POSTGRES_PASSWORD: 'dev_password',
525
+ },
526
+ ports: ['5432:5432'],
527
+ volumes: ['postgres_data:/var/lib/postgresql/data'],
528
+ networks: ['backend'],
529
+ };
530
+
531
+ compose.volumes = {
532
+ postgres_data: {},
533
+ };
534
+ }
535
+
536
+ return this.toYaml(compose);
537
+ }
538
+
539
+ /**
540
+ * Simple YAML serializer
541
+ * @param {Object} obj - Object to serialize
542
+ * @param {number} indent - Current indentation level
543
+ * @returns {string} YAML string
544
+ */
545
+ toYaml(obj, indent = 0) {
546
+ const spaces = ' '.repeat(indent);
547
+ let yaml = '';
548
+
549
+ for (const [key, value] of Object.entries(obj)) {
550
+ if (value === null || value === undefined) continue;
551
+
552
+ if (Array.isArray(value)) {
553
+ yaml += `${spaces}${key}:\n`;
554
+ for (const item of value) {
555
+ if (typeof item === 'object') {
556
+ yaml += `${spaces} -\n`;
557
+ yaml += this.toYaml(item, indent + 2);
558
+ } else {
559
+ yaml += `${spaces} - ${item}\n`;
560
+ }
561
+ }
562
+ } else if (typeof value === 'object') {
563
+ yaml += `${spaces}${key}:\n`;
564
+ yaml += this.toYaml(value, indent + 1);
565
+ } else {
566
+ yaml += `${spaces}${key}: ${value}\n`;
567
+ }
568
+ }
569
+
570
+ return yaml;
571
+ }
572
+
573
+ /**
574
+ * Convert string to singular form (simple implementation)
575
+ * @param {string} str - String to singularize
576
+ * @returns {string} Singular form
577
+ */
578
+ singularize(str) {
579
+ if (str.endsWith('ies')) {
580
+ return str.slice(0, -3) + 'y';
581
+ }
582
+ if (str.endsWith('es')) {
583
+ return str.slice(0, -2);
584
+ }
585
+ if (str.endsWith('s')) {
586
+ return str.slice(0, -1);
587
+ }
588
+ return str;
589
+ }
590
+
591
+ /**
592
+ * Convert string to plural form (simple implementation)
593
+ * @param {string} str - String to pluralize
594
+ * @returns {string} Plural form
595
+ */
596
+ pluralize(str) {
597
+ if (str.endsWith('y')) {
598
+ return str.slice(0, -1) + 'ies';
599
+ }
600
+ if (str.endsWith('s') || str.endsWith('x') || str.endsWith('ch') || str.endsWith('sh')) {
601
+ return str + 'es';
602
+ }
603
+ return str + 's';
604
+ }
605
+
606
+ /**
607
+ * Capitalize first letter
608
+ * @param {string} str - String to capitalize
609
+ * @returns {string} Capitalized string
610
+ */
611
+ capitalize(str) {
612
+ return str.charAt(0).toUpperCase() + str.slice(1);
613
+ }
614
+ }
615
+
616
+ module.exports = { ExampleService };