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,324 @@
1
+ /**
2
+ * AST Code Analyzer
3
+ * Parse JavaScript/TypeScript files and extract complexity metrics
4
+ */
5
+
6
+ const ts = require('typescript');
7
+
8
+ class AstAnalyzer {
9
+ constructor(options = {}) {
10
+ this.options = {
11
+ longFunctionThreshold: options.longFunctionThreshold || 50,
12
+ deepNestingThreshold: options.deepNestingThreshold || 4,
13
+ highComplexityThreshold: options.highComplexityThreshold || 10,
14
+ };
15
+ }
16
+
17
+ /**
18
+ * Analyze code and extract metrics
19
+ * @param {string} code - Source code to analyze
20
+ * @param {string} filename - Filename (used to determine language)
21
+ * @returns {Object} Analysis result with functions and metrics
22
+ */
23
+ analyze(code, filename) {
24
+ if (!code || code.trim() === '') {
25
+ return {
26
+ functions: [],
27
+ fileMetrics: {
28
+ totalFunctions: 0,
29
+ averageComplexity: 0,
30
+ maxComplexity: 0,
31
+ },
32
+ };
33
+ }
34
+
35
+ try {
36
+ const scriptKind = this.getScriptKind(filename);
37
+ const sourceFile = ts.createSourceFile(
38
+ filename,
39
+ code,
40
+ ts.ScriptTarget.Latest,
41
+ true,
42
+ scriptKind
43
+ );
44
+
45
+ // Check for parse errors
46
+ const diagnostics = this.getParseErrors(sourceFile);
47
+ if (diagnostics.length > 0) {
48
+ return {
49
+ error: diagnostics[0].messageText.toString(),
50
+ functions: [],
51
+ fileMetrics: {
52
+ totalFunctions: 0,
53
+ averageComplexity: 0,
54
+ maxComplexity: 0,
55
+ },
56
+ };
57
+ }
58
+
59
+ const functions = [];
60
+ this.visitNode(sourceFile, sourceFile, functions, 0);
61
+
62
+ const fileMetrics = this.calculateFileMetrics(functions);
63
+
64
+ return {
65
+ functions,
66
+ fileMetrics,
67
+ };
68
+ } catch (error) {
69
+ return {
70
+ error: error.message,
71
+ functions: [],
72
+ fileMetrics: {
73
+ totalFunctions: 0,
74
+ averageComplexity: 0,
75
+ maxComplexity: 0,
76
+ },
77
+ };
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Get parse errors from source file
83
+ */
84
+ getParseErrors(sourceFile) {
85
+ // TypeScript parser is error-tolerant, but we can check for obvious issues
86
+ const errors = [];
87
+
88
+ const visit = (node) => {
89
+ // Check for missing tokens or syntax issues
90
+ if (node.kind === ts.SyntaxKind.Unknown) {
91
+ errors.push({ messageText: 'Syntax error: unknown token' });
92
+ }
93
+ ts.forEachChild(node, visit);
94
+ };
95
+
96
+ // Check if file has unbalanced braces/parens by looking at structure
97
+ const text = sourceFile.text;
98
+ const openBraces = (text.match(/\{/g) || []).length;
99
+ const closeBraces = (text.match(/\}/g) || []).length;
100
+ const openParens = (text.match(/\(/g) || []).length;
101
+ const closeParens = (text.match(/\)/g) || []).length;
102
+
103
+ if (openBraces !== closeBraces) {
104
+ errors.push({ messageText: 'Syntax error: unbalanced braces' });
105
+ }
106
+ if (openParens !== closeParens) {
107
+ errors.push({ messageText: 'Syntax error: unbalanced parentheses' });
108
+ }
109
+
110
+ return errors;
111
+ }
112
+
113
+ /**
114
+ * Get TypeScript script kind based on filename
115
+ */
116
+ getScriptKind(filename) {
117
+ const ext = filename.split('.').pop().toLowerCase();
118
+ switch (ext) {
119
+ case 'tsx':
120
+ return ts.ScriptKind.TSX;
121
+ case 'ts':
122
+ return ts.ScriptKind.TS;
123
+ case 'jsx':
124
+ return ts.ScriptKind.JSX;
125
+ case 'js':
126
+ default:
127
+ return ts.ScriptKind.JS;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Visit AST nodes recursively
133
+ */
134
+ visitNode(node, sourceFile, functions, depth) {
135
+ if (this.isFunctionNode(node)) {
136
+ const funcInfo = this.analyzeFunctionNode(node, sourceFile);
137
+ functions.push(funcInfo);
138
+ }
139
+
140
+ ts.forEachChild(node, (child) => {
141
+ this.visitNode(child, sourceFile, functions, depth + 1);
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Check if node is a function-like node
147
+ */
148
+ isFunctionNode(node) {
149
+ return (
150
+ ts.isFunctionDeclaration(node) ||
151
+ ts.isFunctionExpression(node) ||
152
+ ts.isArrowFunction(node) ||
153
+ ts.isMethodDeclaration(node)
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Analyze a function node and extract metrics
159
+ */
160
+ analyzeFunctionNode(node, sourceFile) {
161
+ const name = this.getFunctionName(node, sourceFile);
162
+ const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
163
+ const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
164
+ const lineCount = endLine - startLine + 1;
165
+
166
+ const complexity = this.calculateComplexity(node);
167
+ const maxNesting = this.calculateMaxNesting(node);
168
+
169
+ return {
170
+ name,
171
+ startLine: startLine + 1,
172
+ endLine: endLine + 1,
173
+ lineCount,
174
+ complexity,
175
+ maxNesting,
176
+ isLong: lineCount >= this.options.longFunctionThreshold,
177
+ isDeeplyNested: maxNesting > this.options.deepNestingThreshold,
178
+ isComplex: complexity > this.options.highComplexityThreshold,
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Get function name from node
184
+ */
185
+ getFunctionName(node, sourceFile) {
186
+ // Function declaration with name
187
+ if (node.name) {
188
+ return node.name.getText(sourceFile);
189
+ }
190
+
191
+ // Arrow function or function expression assigned to variable
192
+ if (node.parent && ts.isVariableDeclaration(node.parent)) {
193
+ return node.parent.name.getText(sourceFile);
194
+ }
195
+
196
+ // Method in class
197
+ if (ts.isMethodDeclaration(node) && node.name) {
198
+ return node.name.getText(sourceFile);
199
+ }
200
+
201
+ return '<anonymous>';
202
+ }
203
+
204
+ /**
205
+ * Calculate cyclomatic complexity
206
+ * Base of 1 + decision points
207
+ */
208
+ calculateComplexity(node) {
209
+ let complexity = 1;
210
+
211
+ const visit = (n) => {
212
+ // Conditional statements
213
+ if (ts.isIfStatement(n)) complexity++;
214
+ if (ts.isConditionalExpression(n)) complexity++; // ternary
215
+
216
+ // Loops
217
+ if (ts.isForStatement(n)) complexity++;
218
+ if (ts.isForInStatement(n)) complexity++;
219
+ if (ts.isForOfStatement(n)) complexity++;
220
+ if (ts.isWhileStatement(n)) complexity++;
221
+ if (ts.isDoStatement(n)) complexity++;
222
+
223
+ // Switch cases (each case except default)
224
+ if (ts.isCaseClause(n)) complexity++;
225
+
226
+ // Logical operators
227
+ if (ts.isBinaryExpression(n)) {
228
+ if (
229
+ n.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
230
+ n.operatorToken.kind === ts.SyntaxKind.BarBarToken
231
+ ) {
232
+ complexity++;
233
+ }
234
+ }
235
+
236
+ // Catch clauses
237
+ if (ts.isCatchClause(n)) complexity++;
238
+
239
+ // Nullish coalescing
240
+ if (ts.isBinaryExpression(n) && n.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
241
+ complexity++;
242
+ }
243
+
244
+ ts.forEachChild(n, visit);
245
+ };
246
+
247
+ if (node.body) {
248
+ visit(node.body);
249
+ }
250
+
251
+ return complexity;
252
+ }
253
+
254
+ /**
255
+ * Calculate maximum nesting depth
256
+ */
257
+ calculateMaxNesting(node) {
258
+ let maxDepth = 0;
259
+
260
+ const visit = (n, depth) => {
261
+ // Track nesting for control structures
262
+ if (
263
+ ts.isIfStatement(n) ||
264
+ ts.isForStatement(n) ||
265
+ ts.isForInStatement(n) ||
266
+ ts.isForOfStatement(n) ||
267
+ ts.isWhileStatement(n) ||
268
+ ts.isDoStatement(n) ||
269
+ ts.isTryStatement(n) ||
270
+ ts.isSwitchStatement(n)
271
+ ) {
272
+ const newDepth = depth + 1;
273
+ maxDepth = Math.max(maxDepth, newDepth);
274
+ ts.forEachChild(n, (child) => visit(child, newDepth));
275
+ } else {
276
+ ts.forEachChild(n, (child) => visit(child, depth));
277
+ }
278
+ };
279
+
280
+ if (node.body) {
281
+ visit(node.body, 0);
282
+ }
283
+
284
+ return maxDepth;
285
+ }
286
+
287
+ /**
288
+ * Calculate file-level metrics
289
+ */
290
+ calculateFileMetrics(functions) {
291
+ if (functions.length === 0) {
292
+ return {
293
+ totalFunctions: 0,
294
+ averageComplexity: 0,
295
+ maxComplexity: 0,
296
+ };
297
+ }
298
+
299
+ const complexities = functions.map((f) => f.complexity);
300
+ const totalComplexity = complexities.reduce((a, b) => a + b, 0);
301
+
302
+ return {
303
+ totalFunctions: functions.length,
304
+ averageComplexity: totalComplexity / functions.length,
305
+ maxComplexity: Math.max(...complexities),
306
+ longFunctions: functions.filter((f) => f.isLong).length,
307
+ deeplyNestedFunctions: functions.filter((f) => f.isDeeplyNested).length,
308
+ complexFunctions: functions.filter((f) => f.isComplex).length,
309
+ };
310
+ }
311
+
312
+ /**
313
+ * Analyze a file from path
314
+ * @param {string} filePath - Path to file
315
+ * @returns {Object} Analysis result
316
+ */
317
+ async analyzeFile(filePath) {
318
+ const fs = require('fs').promises;
319
+ const code = await fs.readFile(filePath, 'utf-8');
320
+ return this.analyze(code, filePath);
321
+ }
322
+ }
323
+
324
+ module.exports = { AstAnalyzer };