tlc-claude-code 1.2.29 → 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 (80) hide show
  1. package/dashboard/dist/components/UsagePane.d.ts +13 -0
  2. package/dashboard/dist/components/UsagePane.js +51 -0
  3. package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
  4. package/dashboard/dist/components/UsagePane.test.js +142 -0
  5. package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
  6. package/dashboard/dist/components/WorkspaceDocsPane.js +146 -0
  7. package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
  8. package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
  9. package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
  10. package/dashboard/dist/components/WorkspacePane.js +17 -0
  11. package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
  12. package/dashboard/dist/components/WorkspacePane.test.js +84 -0
  13. package/package.json +1 -1
  14. package/server/lib/architecture-command.js +450 -0
  15. package/server/lib/architecture-command.test.js +754 -0
  16. package/server/lib/ast-analyzer.js +324 -0
  17. package/server/lib/ast-analyzer.test.js +437 -0
  18. package/server/lib/auth-system.test.js +4 -1
  19. package/server/lib/boundary-detector.js +427 -0
  20. package/server/lib/boundary-detector.test.js +320 -0
  21. package/server/lib/budget-alerts.js +138 -0
  22. package/server/lib/budget-alerts.test.js +235 -0
  23. package/server/lib/candidates-tracker.js +210 -0
  24. package/server/lib/candidates-tracker.test.js +300 -0
  25. package/server/lib/checkpoint-manager.js +251 -0
  26. package/server/lib/checkpoint-manager.test.js +474 -0
  27. package/server/lib/circular-detector.js +337 -0
  28. package/server/lib/circular-detector.test.js +353 -0
  29. package/server/lib/cohesion-analyzer.js +310 -0
  30. package/server/lib/cohesion-analyzer.test.js +447 -0
  31. package/server/lib/contract-testing.js +625 -0
  32. package/server/lib/contract-testing.test.js +342 -0
  33. package/server/lib/conversion-planner.js +469 -0
  34. package/server/lib/conversion-planner.test.js +361 -0
  35. package/server/lib/convert-command.js +351 -0
  36. package/server/lib/convert-command.test.js +608 -0
  37. package/server/lib/coupling-calculator.js +189 -0
  38. package/server/lib/coupling-calculator.test.js +509 -0
  39. package/server/lib/dependency-graph.js +367 -0
  40. package/server/lib/dependency-graph.test.js +516 -0
  41. package/server/lib/duplication-detector.js +349 -0
  42. package/server/lib/duplication-detector.test.js +401 -0
  43. package/server/lib/example-service.js +616 -0
  44. package/server/lib/example-service.test.js +397 -0
  45. package/server/lib/impact-scorer.js +184 -0
  46. package/server/lib/impact-scorer.test.js +211 -0
  47. package/server/lib/mermaid-generator.js +358 -0
  48. package/server/lib/mermaid-generator.test.js +301 -0
  49. package/server/lib/messaging-patterns.js +750 -0
  50. package/server/lib/messaging-patterns.test.js +213 -0
  51. package/server/lib/microservice-template.js +386 -0
  52. package/server/lib/microservice-template.test.js +325 -0
  53. package/server/lib/new-project-microservice.js +450 -0
  54. package/server/lib/new-project-microservice.test.js +600 -0
  55. package/server/lib/refactor-command.js +326 -0
  56. package/server/lib/refactor-command.test.js +528 -0
  57. package/server/lib/refactor-executor.js +254 -0
  58. package/server/lib/refactor-executor.test.js +305 -0
  59. package/server/lib/refactor-observer.js +292 -0
  60. package/server/lib/refactor-observer.test.js +422 -0
  61. package/server/lib/refactor-progress.js +193 -0
  62. package/server/lib/refactor-progress.test.js +251 -0
  63. package/server/lib/refactor-reporter.js +237 -0
  64. package/server/lib/refactor-reporter.test.js +247 -0
  65. package/server/lib/semantic-analyzer.js +198 -0
  66. package/server/lib/semantic-analyzer.test.js +474 -0
  67. package/server/lib/service-scaffold.js +486 -0
  68. package/server/lib/service-scaffold.test.js +373 -0
  69. package/server/lib/shared-kernel.js +578 -0
  70. package/server/lib/shared-kernel.test.js +255 -0
  71. package/server/lib/traefik-config.js +282 -0
  72. package/server/lib/traefik-config.test.js +312 -0
  73. package/server/lib/usage-command.js +218 -0
  74. package/server/lib/usage-command.test.js +391 -0
  75. package/server/lib/usage-formatter.js +192 -0
  76. package/server/lib/usage-formatter.test.js +267 -0
  77. package/server/lib/usage-history.js +122 -0
  78. package/server/lib/usage-history.test.js +206 -0
  79. package/server/package-lock.json +14 -0
  80. package/server/package.json +1 -0
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Cohesion Analyzer
3
+ * Analyze module cohesion based on dependency relationships
4
+ */
5
+
6
+ const path = require('path');
7
+
8
+ class CohesionAnalyzer {
9
+ constructor(options = {}) {
10
+ this.options = options;
11
+ this.basePath = options.basePath || process.cwd();
12
+ this.lowCohesionThreshold = options.lowCohesionThreshold || 0.3;
13
+ }
14
+
15
+ /**
16
+ * Analyze cohesion for a dependency graph
17
+ * @param {Object} graph - DependencyGraph instance
18
+ * @returns {Object} Cohesion analysis results
19
+ */
20
+ analyze(graph) {
21
+ const graphData = graph.getGraph();
22
+ const modules = this.groupByDirectory(graphData.nodes);
23
+ const moduleAnalysis = {};
24
+
25
+ for (const [modulePath, files] of Object.entries(modules)) {
26
+ moduleAnalysis[modulePath] = this.analyzeModule(modulePath, files, graph);
27
+ }
28
+
29
+ const lowCohesion = this.identifyLowCohesion(moduleAnalysis);
30
+ const suggestions = this.generateSuggestions(moduleAnalysis, graph);
31
+
32
+ return {
33
+ modules: moduleAnalysis,
34
+ lowCohesion,
35
+ suggestions,
36
+ summary: {
37
+ totalModules: Object.keys(moduleAnalysis).length,
38
+ averageCohesion: this.calculateAverageCohesion(moduleAnalysis),
39
+ lowCohesionCount: lowCohesion.length,
40
+ },
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Group files by their directory (module)
46
+ */
47
+ groupByDirectory(nodes) {
48
+ const modules = {};
49
+
50
+ for (const node of nodes) {
51
+ const filePath = node.id;
52
+ const relativePath = node.name;
53
+ const dir = path.dirname(relativePath);
54
+
55
+ // Normalize the directory path
56
+ const modulePath = dir === '.' ? '(root)' : dir;
57
+
58
+ if (!modules[modulePath]) {
59
+ modules[modulePath] = [];
60
+ }
61
+
62
+ modules[modulePath].push({
63
+ path: filePath,
64
+ name: relativePath,
65
+ basename: path.basename(relativePath),
66
+ });
67
+ }
68
+
69
+ return modules;
70
+ }
71
+
72
+ /**
73
+ * Analyze cohesion for a single module (directory)
74
+ */
75
+ analyzeModule(modulePath, files, graph) {
76
+ if (files.length === 0) {
77
+ return {
78
+ path: modulePath,
79
+ files: [],
80
+ cohesion: 1,
81
+ internalDeps: 0,
82
+ externalDeps: 0,
83
+ ratio: 1,
84
+ };
85
+ }
86
+
87
+ if (files.length === 1) {
88
+ // Single file module - check its dependencies
89
+ const file = files[0];
90
+ const imports = graph.getImports(file.path);
91
+ const importers = graph.getImporters(file.path);
92
+
93
+ // A single file with no internal deps is cohesive by definition
94
+ return {
95
+ path: modulePath,
96
+ files: files.map(f => f.name),
97
+ cohesion: 1,
98
+ internalDeps: 0,
99
+ externalDeps: imports.length + importers.length,
100
+ ratio: 1,
101
+ singleFile: true,
102
+ };
103
+ }
104
+
105
+ const filePaths = new Set(files.map(f => f.path));
106
+ let internalDeps = 0;
107
+ let externalDeps = 0;
108
+
109
+ for (const file of files) {
110
+ const imports = graph.getImports(file.path);
111
+ const importers = graph.getImporters(file.path);
112
+
113
+ // Count internal vs external dependencies
114
+ for (const imp of imports) {
115
+ if (filePaths.has(imp)) {
116
+ internalDeps++;
117
+ } else {
118
+ externalDeps++;
119
+ }
120
+ }
121
+
122
+ for (const importer of importers) {
123
+ if (!filePaths.has(importer)) {
124
+ externalDeps++;
125
+ }
126
+ // Internal importers are counted when processing the importer file
127
+ }
128
+ }
129
+
130
+ const totalDeps = internalDeps + externalDeps;
131
+ const cohesion = totalDeps === 0 ? 1 : internalDeps / totalDeps;
132
+
133
+ return {
134
+ path: modulePath,
135
+ files: files.map(f => f.name),
136
+ cohesion: Math.round(cohesion * 1000) / 1000,
137
+ internalDeps,
138
+ externalDeps,
139
+ ratio: totalDeps === 0 ? 1 : Math.round(cohesion * 1000) / 1000,
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Identify modules with low cohesion
145
+ */
146
+ identifyLowCohesion(moduleAnalysis) {
147
+ const lowCohesion = [];
148
+
149
+ for (const [modulePath, analysis] of Object.entries(moduleAnalysis)) {
150
+ if (analysis.cohesion < this.lowCohesionThreshold && !analysis.singleFile) {
151
+ lowCohesion.push({
152
+ module: modulePath,
153
+ cohesion: analysis.cohesion,
154
+ files: analysis.files,
155
+ internalDeps: analysis.internalDeps,
156
+ externalDeps: analysis.externalDeps,
157
+ });
158
+ }
159
+ }
160
+
161
+ return lowCohesion.sort((a, b) => a.cohesion - b.cohesion);
162
+ }
163
+
164
+ /**
165
+ * Generate suggestions for improving cohesion
166
+ */
167
+ generateSuggestions(moduleAnalysis, graph) {
168
+ const suggestions = [];
169
+
170
+ for (const [modulePath, analysis] of Object.entries(moduleAnalysis)) {
171
+ if (analysis.singleFile || analysis.files.length <= 1) {
172
+ continue;
173
+ }
174
+
175
+ // Find files that might be better placed elsewhere
176
+ const outliers = this.findOutliers(modulePath, analysis, graph);
177
+
178
+ for (const outlier of outliers) {
179
+ suggestions.push({
180
+ type: 'move',
181
+ file: outlier.file,
182
+ from: modulePath,
183
+ to: outlier.suggestedModule,
184
+ reason: outlier.reason,
185
+ impact: outlier.impact,
186
+ });
187
+ }
188
+ }
189
+
190
+ return suggestions;
191
+ }
192
+
193
+ /**
194
+ * Find files that don't fit well in their current module
195
+ */
196
+ findOutliers(modulePath, analysis, graph) {
197
+ const outliers = [];
198
+ const filePaths = new Set();
199
+
200
+ // Build a set of file paths in this module
201
+ for (const fileName of analysis.files) {
202
+ const fullPath = this.resolveFilePath(fileName, graph);
203
+ if (fullPath) {
204
+ filePaths.add(fullPath);
205
+ }
206
+ }
207
+
208
+ for (const fileName of analysis.files) {
209
+ const fullPath = this.resolveFilePath(fileName, graph);
210
+ if (!fullPath) continue;
211
+
212
+ const imports = graph.getImports(fullPath);
213
+ const importers = graph.getImporters(fullPath);
214
+
215
+ // Count dependencies by module
216
+ const depsByModule = {};
217
+ let internalDeps = 0;
218
+ let externalDeps = 0;
219
+
220
+ for (const imp of imports) {
221
+ if (filePaths.has(imp)) {
222
+ internalDeps++;
223
+ } else {
224
+ externalDeps++;
225
+ const impModule = this.getModuleForFile(imp, graph);
226
+ depsByModule[impModule] = (depsByModule[impModule] || 0) + 1;
227
+ }
228
+ }
229
+
230
+ for (const importer of importers) {
231
+ if (!filePaths.has(importer)) {
232
+ const impModule = this.getModuleForFile(importer, graph);
233
+ depsByModule[impModule] = (depsByModule[impModule] || 0) + 1;
234
+ }
235
+ }
236
+
237
+ // Check if file has more dependencies with another module
238
+ const totalDeps = internalDeps + externalDeps;
239
+ if (totalDeps === 0) continue;
240
+
241
+ const fileCohesion = internalDeps / totalDeps;
242
+
243
+ // Find the module with most dependencies
244
+ let maxModule = null;
245
+ let maxDeps = 0;
246
+
247
+ for (const [mod, count] of Object.entries(depsByModule)) {
248
+ if (count > maxDeps) {
249
+ maxDeps = count;
250
+ maxModule = mod;
251
+ }
252
+ }
253
+
254
+ // Suggest move if file has more dependencies with another module
255
+ if (maxModule && maxDeps > internalDeps && fileCohesion < 0.5) {
256
+ outliers.push({
257
+ file: fileName,
258
+ suggestedModule: maxModule,
259
+ reason: `File has ${maxDeps} dependencies with ${maxModule} vs ${internalDeps} internal`,
260
+ impact: Math.round((maxDeps / totalDeps) * 100) / 100,
261
+ });
262
+ }
263
+ }
264
+
265
+ return outliers;
266
+ }
267
+
268
+ /**
269
+ * Resolve a file name to its full path
270
+ */
271
+ resolveFilePath(fileName, graph) {
272
+ const graphData = graph.getGraph();
273
+ for (const node of graphData.nodes) {
274
+ if (node.name === fileName) {
275
+ return node.id;
276
+ }
277
+ }
278
+ return null;
279
+ }
280
+
281
+ /**
282
+ * Get the module (directory) for a file path
283
+ */
284
+ getModuleForFile(filePath, graph) {
285
+ const graphData = graph.getGraph();
286
+ for (const node of graphData.nodes) {
287
+ if (node.id === filePath) {
288
+ const dir = path.dirname(node.name);
289
+ return dir === '.' ? '(root)' : dir;
290
+ }
291
+ }
292
+ // If not found in graph, derive from path
293
+ const relativePath = path.relative(this.basePath, filePath);
294
+ const dir = path.dirname(relativePath);
295
+ return dir === '.' ? '(root)' : dir;
296
+ }
297
+
298
+ /**
299
+ * Calculate average cohesion across all modules
300
+ */
301
+ calculateAverageCohesion(moduleAnalysis) {
302
+ const modules = Object.values(moduleAnalysis);
303
+ if (modules.length === 0) return 1;
304
+
305
+ const total = modules.reduce((sum, m) => sum + m.cohesion, 0);
306
+ return Math.round((total / modules.length) * 1000) / 1000;
307
+ }
308
+ }
309
+
310
+ module.exports = { CohesionAnalyzer };