codebase-context 1.2.1 → 1.5.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 (96) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +144 -87
  3. package/dist/analyzers/angular/index.d.ts +1 -1
  4. package/dist/analyzers/angular/index.d.ts.map +1 -1
  5. package/dist/analyzers/angular/index.js +298 -309
  6. package/dist/analyzers/angular/index.js.map +1 -1
  7. package/dist/analyzers/generic/index.d.ts +1 -1
  8. package/dist/analyzers/generic/index.d.ts.map +1 -1
  9. package/dist/analyzers/generic/index.js +93 -47
  10. package/dist/analyzers/generic/index.js.map +1 -1
  11. package/dist/constants/codebase-context.d.ts +6 -0
  12. package/dist/constants/codebase-context.d.ts.map +1 -0
  13. package/dist/constants/codebase-context.js +8 -0
  14. package/dist/constants/codebase-context.js.map +1 -0
  15. package/dist/core/analyzer-registry.d.ts.map +1 -1
  16. package/dist/core/analyzer-registry.js +5 -7
  17. package/dist/core/analyzer-registry.js.map +1 -1
  18. package/dist/core/indexer.d.ts +9 -1
  19. package/dist/core/indexer.d.ts.map +1 -1
  20. package/dist/core/indexer.js +206 -139
  21. package/dist/core/indexer.js.map +1 -1
  22. package/dist/core/search.d.ts +1 -1
  23. package/dist/core/search.d.ts.map +1 -1
  24. package/dist/core/search.js +63 -59
  25. package/dist/core/search.js.map +1 -1
  26. package/dist/embeddings/openai.d.ts.map +1 -1
  27. package/dist/embeddings/openai.js +2 -2
  28. package/dist/embeddings/openai.js.map +1 -1
  29. package/dist/embeddings/transformers.d.ts +1 -1
  30. package/dist/embeddings/transformers.d.ts.map +1 -1
  31. package/dist/embeddings/transformers.js +19 -15
  32. package/dist/embeddings/transformers.js.map +1 -1
  33. package/dist/embeddings/types.d.ts +1 -1
  34. package/dist/embeddings/types.d.ts.map +1 -1
  35. package/dist/embeddings/types.js +3 -3
  36. package/dist/embeddings/types.js.map +1 -1
  37. package/dist/errors/index.d.ts +8 -0
  38. package/dist/errors/index.d.ts.map +1 -0
  39. package/dist/errors/index.js +11 -0
  40. package/dist/errors/index.js.map +1 -0
  41. package/dist/index.d.ts +6 -28
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +691 -336
  44. package/dist/index.js.map +1 -1
  45. package/dist/lib.d.ts +18 -18
  46. package/dist/lib.d.ts.map +1 -1
  47. package/dist/lib.js +23 -23
  48. package/dist/lib.js.map +1 -1
  49. package/dist/memory/store.d.ts +22 -0
  50. package/dist/memory/store.d.ts.map +1 -0
  51. package/dist/memory/store.js +97 -0
  52. package/dist/memory/store.js.map +1 -0
  53. package/dist/storage/lancedb.d.ts.map +1 -1
  54. package/dist/storage/lancedb.js +27 -31
  55. package/dist/storage/lancedb.js.map +1 -1
  56. package/dist/storage/types.d.ts.map +1 -1
  57. package/dist/storage/types.js +2 -1
  58. package/dist/storage/types.js.map +1 -1
  59. package/dist/types/index.d.ts +27 -0
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/types/index.js +1 -0
  62. package/dist/types/index.js.map +1 -1
  63. package/dist/utils/chunking.d.ts.map +1 -1
  64. package/dist/utils/chunking.js +10 -9
  65. package/dist/utils/chunking.js.map +1 -1
  66. package/dist/utils/dependency-detection.d.ts +18 -0
  67. package/dist/utils/dependency-detection.d.ts.map +1 -0
  68. package/dist/utils/dependency-detection.js +102 -0
  69. package/dist/utils/dependency-detection.js.map +1 -0
  70. package/dist/utils/git-dates.d.ts.map +1 -1
  71. package/dist/utils/git-dates.js +3 -3
  72. package/dist/utils/git-dates.js.map +1 -1
  73. package/dist/utils/language-detection.d.ts.map +1 -1
  74. package/dist/utils/language-detection.js +69 -17
  75. package/dist/utils/language-detection.js.map +1 -1
  76. package/dist/utils/usage-tracker.d.ts +2 -2
  77. package/dist/utils/usage-tracker.d.ts.map +1 -1
  78. package/dist/utils/usage-tracker.js +64 -32
  79. package/dist/utils/usage-tracker.js.map +1 -1
  80. package/dist/utils/workspace-detection.d.ts +32 -0
  81. package/dist/utils/workspace-detection.d.ts.map +1 -0
  82. package/dist/utils/workspace-detection.js +107 -0
  83. package/dist/utils/workspace-detection.js.map +1 -0
  84. package/package.json +114 -97
  85. package/dist/core/file-watcher.d.ts +0 -63
  86. package/dist/core/file-watcher.d.ts.map +0 -1
  87. package/dist/core/file-watcher.js +0 -210
  88. package/dist/core/file-watcher.js.map +0 -1
  89. package/dist/utils/logger.d.ts +0 -36
  90. package/dist/utils/logger.d.ts.map +0 -1
  91. package/dist/utils/logger.js +0 -111
  92. package/dist/utils/logger.js.map +0 -1
  93. package/dist/utils/pattern-detector.d.ts +0 -41
  94. package/dist/utils/pattern-detector.d.ts.map +0 -1
  95. package/dist/utils/pattern-detector.js +0 -101
  96. package/dist/utils/pattern-detector.js.map +0 -1
@@ -3,22 +3,16 @@
3
3
  * Understands components, services, directives, pipes, modules, guards, interceptors, etc.
4
4
  * Detects state management patterns, architectural layers, and Angular-specific patterns
5
5
  */
6
- import { promises as fs } from "fs";
7
- import path from "path";
8
- import { parse } from "@typescript-eslint/typescript-estree";
9
- import { createChunksFromCode } from "../../utils/chunking.js";
6
+ /* eslint-disable @typescript-eslint/no-explicit-any */
7
+ import { promises as fs } from 'fs';
8
+ import path from 'path';
9
+ import { parse } from '@typescript-eslint/typescript-estree';
10
+ import { createChunksFromCode } from '../../utils/chunking.js';
11
+ import { CODEBASE_CONTEXT_DIRNAME, KEYWORD_INDEX_FILENAME } from '../../constants/codebase-context.js';
10
12
  export class AngularAnalyzer {
11
- name = "angular";
12
- version = "1.0.0";
13
- supportedExtensions = [
14
- ".ts",
15
- ".js",
16
- ".html",
17
- ".scss",
18
- ".css",
19
- ".sass",
20
- ".less",
21
- ];
13
+ name = 'angular';
14
+ version = '1.0.0';
15
+ supportedExtensions = ['.ts', '.js', '.html', '.scss', '.css', '.sass', '.less'];
22
16
  priority = 100; // Highest priority for Angular files
23
17
  angularPatterns = {
24
18
  component: /@Component\s*\(/,
@@ -30,14 +24,14 @@ export class AngularAnalyzer {
30
24
  guard: /(?:implements\s+(?:CanActivate|CanDeactivate|CanLoad|CanMatch)|canActivate\s*\(|canDeactivate\s*\(|canLoad\s*\(|canMatch\s*\(|CanActivateFn|CanDeactivateFn|CanMatchFn)/,
31
25
  interceptor: /(?:implements\s+HttpInterceptor|intercept\s*\(|HttpInterceptorFn)/,
32
26
  resolver: /(?:implements\s+Resolve|resolve\s*\(|ResolveFn)/,
33
- validator: /(?:implements\s+(?:Validator|AsyncValidator)|validate\s*\()/,
27
+ validator: /(?:implements\s+(?:Validator|AsyncValidator)|validate\s*\()/
34
28
  };
35
29
  stateManagementPatterns = {
36
30
  ngrx: /@ngrx\/store|createAction|createReducer|createSelector/,
37
31
  akita: /@datorama\/akita|Query|Store\.update/,
38
32
  elf: /@ngneat\/elf|createStore|withEntities/,
39
33
  signals: /\bsignal\s*[<(]|\bcomputed\s*[<(]|\beffect\s*\(|\blinkedSignal\s*[<(]/,
40
- rxjsState: /BehaviorSubject|ReplaySubject|shareReplay/,
34
+ rxjsState: /BehaviorSubject|ReplaySubject|shareReplay/
41
35
  };
42
36
  modernAngularPatterns = {
43
37
  signalInput: /\binput\s*[<(]|\binput\.required\s*[<(]/,
@@ -51,7 +45,7 @@ export class AngularAnalyzer {
51
45
  controlFlowFor: /@for\s*\(/,
52
46
  controlFlowSwitch: /@switch\s*\(/,
53
47
  controlFlowDefer: /@defer\s*[({]/,
54
- injectFunction: /\binject\s*[<(]/,
48
+ injectFunction: /\binject\s*[<(]/
55
49
  };
56
50
  canAnalyze(filePath, content) {
57
51
  const ext = path.extname(filePath).toLowerCase();
@@ -59,13 +53,13 @@ export class AngularAnalyzer {
59
53
  return false;
60
54
  }
61
55
  // For TypeScript files, check if it contains Angular decorators
62
- if (ext === ".ts" && content) {
56
+ if (ext === '.ts' && content) {
63
57
  return Object.values(this.angularPatterns).some((pattern) => pattern.test(content));
64
58
  }
65
59
  // Angular component templates and styles
66
- if ([".html", ".scss", ".css", ".sass", ".less"].includes(ext)) {
60
+ if (['.html', '.scss', '.css', '.sass', '.less'].includes(ext)) {
67
61
  // Check if there's a corresponding .ts file
68
- const baseName = filePath.replace(/\.(html|scss|css|sass|less)$/, "");
62
+ // const baseName = filePath.replace(/\.(html|scss|css|sass|less)$/, '');
69
63
  return true; // We'll verify during analysis
70
64
  }
71
65
  return false;
@@ -73,26 +67,26 @@ export class AngularAnalyzer {
73
67
  async analyze(filePath, content) {
74
68
  const ext = path.extname(filePath).toLowerCase();
75
69
  const relativePath = path.relative(process.cwd(), filePath);
76
- if (ext === ".ts") {
70
+ if (ext === '.ts') {
77
71
  return this.analyzeTypeScriptFile(filePath, content, relativePath);
78
72
  }
79
- else if (ext === ".html") {
73
+ else if (ext === '.html') {
80
74
  return this.analyzeTemplateFile(filePath, content, relativePath);
81
75
  }
82
- else if ([".scss", ".css", ".sass", ".less"].includes(ext)) {
76
+ else if (['.scss', '.css', '.sass', '.less'].includes(ext)) {
83
77
  return this.analyzeStyleFile(filePath, content, relativePath);
84
78
  }
85
79
  // Fallback
86
80
  return {
87
81
  filePath,
88
- language: "unknown",
89
- framework: "angular",
82
+ language: 'unknown',
83
+ framework: 'angular',
90
84
  components: [],
91
85
  imports: [],
92
86
  exports: [],
93
87
  dependencies: [],
94
88
  metadata: {},
95
- chunks: [],
89
+ chunks: []
96
90
  };
97
91
  }
98
92
  async analyzeTypeScriptFile(filePath, content, relativePath) {
@@ -104,33 +98,33 @@ export class AngularAnalyzer {
104
98
  const ast = parse(content, {
105
99
  loc: true,
106
100
  range: true,
107
- comment: true,
101
+ comment: true
108
102
  });
109
103
  // Extract imports
110
104
  for (const node of ast.body) {
111
- if (node.type === "ImportDeclaration" && node.source.value) {
105
+ if (node.type === 'ImportDeclaration' && node.source.value) {
112
106
  const source = node.source.value;
113
107
  imports.push({
114
108
  source,
115
109
  imports: node.specifiers.map((s) => {
116
- if (s.type === "ImportDefaultSpecifier")
117
- return "default";
118
- if (s.type === "ImportNamespaceSpecifier")
119
- return "*";
110
+ if (s.type === 'ImportDefaultSpecifier')
111
+ return 'default';
112
+ if (s.type === 'ImportNamespaceSpecifier')
113
+ return '*';
120
114
  return s.imported?.name || s.local.name;
121
115
  }),
122
- isDefault: node.specifiers.some((s) => s.type === "ImportDefaultSpecifier"),
116
+ isDefault: node.specifiers.some((s) => s.type === 'ImportDefaultSpecifier'),
123
117
  isDynamic: false,
124
- line: node.loc?.start.line,
118
+ line: node.loc?.start.line
125
119
  });
126
120
  // Track dependencies
127
- if (!source.startsWith(".") && !source.startsWith("/")) {
128
- dependencies.push(source.split("/")[0]);
121
+ if (!source.startsWith('.') && !source.startsWith('/')) {
122
+ dependencies.push(source.split('/')[0]);
129
123
  }
130
124
  }
131
125
  // Extract class declarations with decorators
132
- if (node.type === "ExportNamedDeclaration" &&
133
- node.declaration?.type === "ClassDeclaration") {
126
+ if (node.type === 'ExportNamedDeclaration' &&
127
+ node.declaration?.type === 'ClassDeclaration') {
134
128
  const classNode = node.declaration;
135
129
  if (classNode.id && classNode.decorators) {
136
130
  const component = await this.extractAngularComponent(classNode, content);
@@ -140,33 +134,30 @@ export class AngularAnalyzer {
140
134
  }
141
135
  }
142
136
  // Handle direct class exports
143
- if (node.type === "ClassDeclaration" && node.id && node.decorators) {
137
+ if (node.type === 'ClassDeclaration' && node.id && node.decorators) {
144
138
  const component = await this.extractAngularComponent(node, content);
145
139
  if (component) {
146
140
  components.push(component);
147
141
  }
148
142
  }
149
143
  // Extract exports
150
- if (node.type === "ExportNamedDeclaration") {
144
+ if (node.type === 'ExportNamedDeclaration') {
151
145
  if (node.declaration) {
152
- if (node.declaration.type === "ClassDeclaration" &&
153
- node.declaration.id) {
146
+ if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
154
147
  exports.push({
155
148
  name: node.declaration.id.name,
156
149
  isDefault: false,
157
- type: "class",
150
+ type: 'class'
158
151
  });
159
152
  }
160
153
  }
161
154
  }
162
- if (node.type === "ExportDefaultDeclaration") {
163
- const name = node.declaration.type === "Identifier"
164
- ? node.declaration.name
165
- : "default";
155
+ if (node.type === 'ExportDefaultDeclaration') {
156
+ const name = node.declaration.type === 'Identifier' ? node.declaration.name : 'default';
166
157
  exports.push({
167
158
  name,
168
159
  isDefault: true,
169
- type: "default",
160
+ type: 'default'
170
161
  });
171
162
  }
172
163
  }
@@ -181,77 +172,80 @@ export class AngularAnalyzer {
181
172
  // Determine architectural layer
182
173
  const layer = this.determineLayer(filePath, components);
183
174
  // Create chunks with Angular-specific metadata
184
- const chunks = await createChunksFromCode(content, filePath, relativePath, "typescript", components, {
185
- framework: "angular",
175
+ const chunks = await createChunksFromCode(content, filePath, relativePath, 'typescript', components, {
176
+ framework: 'angular',
186
177
  layer,
187
178
  statePattern,
188
179
  dependencies,
189
- modernPatterns,
180
+ modernPatterns
190
181
  });
191
182
  // Build detected patterns for the indexer to forward
192
183
  const detectedPatterns = [];
193
184
  // Dependency Injection pattern
194
- if (modernPatterns.includes("injectFunction")) {
195
- detectedPatterns.push({ category: "dependencyInjection", name: "inject() function" });
185
+ if (modernPatterns.includes('injectFunction')) {
186
+ detectedPatterns.push({ category: 'dependencyInjection', name: 'inject() function' });
196
187
  }
197
- else if (content.includes("constructor(") && content.includes("private") &&
198
- (relativePath.endsWith(".service.ts") || relativePath.endsWith(".component.ts"))) {
199
- detectedPatterns.push({ category: "dependencyInjection", name: "Constructor injection" });
188
+ else if (content.includes('constructor(') &&
189
+ content.includes('private') &&
190
+ (relativePath.endsWith('.service.ts') || relativePath.endsWith('.component.ts'))) {
191
+ detectedPatterns.push({ category: 'dependencyInjection', name: 'Constructor injection' });
200
192
  }
201
193
  // State Management pattern
202
194
  if (/BehaviorSubject|ReplaySubject|Subject|Observable/.test(content)) {
203
- detectedPatterns.push({ category: "stateManagement", name: "RxJS" });
195
+ detectedPatterns.push({ category: 'stateManagement', name: 'RxJS' });
204
196
  }
205
- if (modernPatterns.some((p) => p.startsWith("signal"))) {
206
- detectedPatterns.push({ category: "stateManagement", name: "Signals" });
197
+ if (modernPatterns.some((p) => p.startsWith('signal'))) {
198
+ detectedPatterns.push({ category: 'stateManagement', name: 'Signals' });
207
199
  }
208
200
  // Reactivity patterns
209
201
  if (/\beffect\s*\(/.test(content)) {
210
- detectedPatterns.push({ category: "reactivity", name: "Effect" });
202
+ detectedPatterns.push({ category: 'reactivity', name: 'Effect' });
211
203
  }
212
204
  if (/\bcomputed\s*[<(]/.test(content)) {
213
- detectedPatterns.push({ category: "reactivity", name: "Computed" });
205
+ detectedPatterns.push({ category: 'reactivity', name: 'Computed' });
214
206
  }
215
207
  // Component Style pattern detection
216
208
  // Logic: explicit standalone: true → Standalone
217
209
  // explicit standalone: false → NgModule-based
218
210
  // no explicit flag + uses modern patterns (inject, signals) → likely Standalone (Angular v19+ default)
219
211
  // no explicit flag + no modern patterns → ambiguous, don't classify
220
- const hasExplicitStandalone = content.includes("standalone: true");
221
- const hasExplicitNgModule = content.includes("standalone: false");
222
- const usesModernPatterns = modernPatterns.includes("injectFunction") ||
223
- modernPatterns.some(p => p.startsWith("signal"));
224
- if (relativePath.endsWith("component.ts") || relativePath.endsWith("directive.ts") || relativePath.endsWith("pipe.ts")) {
212
+ const hasExplicitStandalone = content.includes('standalone: true');
213
+ const hasExplicitNgModule = content.includes('standalone: false');
214
+ const usesModernPatterns = modernPatterns.includes('injectFunction') ||
215
+ modernPatterns.some((p) => p.startsWith('signal'));
216
+ if (relativePath.endsWith('component.ts') ||
217
+ relativePath.endsWith('directive.ts') ||
218
+ relativePath.endsWith('pipe.ts')) {
225
219
  if (hasExplicitStandalone) {
226
- detectedPatterns.push({ category: "componentStyle", name: "Standalone" });
220
+ detectedPatterns.push({ category: 'componentStyle', name: 'Standalone' });
227
221
  }
228
222
  else if (hasExplicitNgModule) {
229
- detectedPatterns.push({ category: "componentStyle", name: "NgModule-based" });
223
+ detectedPatterns.push({ category: 'componentStyle', name: 'NgModule-based' });
230
224
  }
231
225
  else if (usesModernPatterns) {
232
226
  // No explicit flag but uses modern patterns → likely v19+ standalone default
233
- detectedPatterns.push({ category: "componentStyle", name: "Standalone" });
227
+ detectedPatterns.push({ category: 'componentStyle', name: 'Standalone' });
234
228
  }
235
229
  // If no explicit flag and no modern patterns, don't classify (ambiguous)
236
230
  }
237
231
  // Input style pattern
238
- if (modernPatterns.includes("signalInput")) {
239
- detectedPatterns.push({ category: "componentInputs", name: "Signal-based inputs" });
232
+ if (modernPatterns.includes('signalInput')) {
233
+ detectedPatterns.push({ category: 'componentInputs', name: 'Signal-based inputs' });
240
234
  }
241
- else if (content.includes("@Input()")) {
242
- detectedPatterns.push({ category: "componentInputs", name: "Decorator-based @Input" });
235
+ else if (content.includes('@Input()')) {
236
+ detectedPatterns.push({ category: 'componentInputs', name: 'Decorator-based @Input' });
243
237
  }
244
238
  return {
245
239
  filePath,
246
- language: "typescript",
247
- framework: "angular",
240
+ language: 'typescript',
241
+ framework: 'angular',
248
242
  components,
249
243
  imports,
250
244
  exports,
251
245
  dependencies: dependencies.map((name) => ({
252
246
  name,
253
247
  category: this.categorizeDependency(name),
254
- layer,
248
+ layer
255
249
  })),
256
250
  metadata: {
257
251
  analyzer: this.name,
@@ -259,22 +253,22 @@ export class AngularAnalyzer {
259
253
  statePattern,
260
254
  modernPatterns,
261
255
  // isStandalone: true if explicit standalone: true, or if uses modern patterns (implying v19+ default)
262
- isStandalone: content.includes("standalone: true") ||
263
- (!content.includes("standalone: false") &&
264
- (modernPatterns.includes("injectFunction") || modernPatterns.some(p => p.startsWith("signal")))),
265
- hasRoutes: content.includes("RouterModule") || content.includes("routes"),
266
- usesSignals: modernPatterns.length > 0 &&
267
- modernPatterns.some((p) => p.startsWith("signal")),
268
- usesControlFlow: modernPatterns.some((p) => p.startsWith("controlFlow")),
269
- usesInject: modernPatterns.includes("injectFunction"),
256
+ isStandalone: content.includes('standalone: true') ||
257
+ (!content.includes('standalone: false') &&
258
+ (modernPatterns.includes('injectFunction') ||
259
+ modernPatterns.some((p) => p.startsWith('signal')))),
260
+ hasRoutes: content.includes('RouterModule') || content.includes('routes'),
261
+ usesSignals: modernPatterns.length > 0 && modernPatterns.some((p) => p.startsWith('signal')),
262
+ usesControlFlow: modernPatterns.some((p) => p.startsWith('controlFlow')),
263
+ usesInject: modernPatterns.includes('injectFunction'),
270
264
  usesRxJS: /BehaviorSubject|ReplaySubject|Subject|Observable/.test(content),
271
265
  usesEffect: /\beffect\s*\(/.test(content),
272
266
  usesComputed: /\bcomputed\s*[<(]/.test(content),
273
267
  componentType: components.length > 0 ? components[0].metadata.angularType : undefined,
274
268
  // NEW: Patterns for the indexer to forward generically
275
- detectedPatterns,
269
+ detectedPatterns
276
270
  },
277
- chunks,
271
+ chunks
278
272
  };
279
273
  }
280
274
  /**
@@ -298,66 +292,66 @@ export class AngularAnalyzer {
298
292
  let componentType;
299
293
  let angularType;
300
294
  // Determine Angular component type
301
- if (decoratorName === "Component") {
302
- componentType = "component";
303
- angularType = "component";
295
+ if (decoratorName === 'Component') {
296
+ componentType = 'component';
297
+ angularType = 'component';
304
298
  }
305
- else if (decoratorName === "Directive") {
306
- componentType = "directive";
307
- angularType = "directive";
299
+ else if (decoratorName === 'Directive') {
300
+ componentType = 'directive';
301
+ angularType = 'directive';
308
302
  }
309
- else if (decoratorName === "Pipe") {
310
- componentType = "pipe";
311
- angularType = "pipe";
303
+ else if (decoratorName === 'Pipe') {
304
+ componentType = 'pipe';
305
+ angularType = 'pipe';
312
306
  }
313
- else if (decoratorName === "NgModule") {
314
- componentType = "module";
315
- angularType = "module";
307
+ else if (decoratorName === 'NgModule') {
308
+ componentType = 'module';
309
+ angularType = 'module';
316
310
  }
317
- else if (decoratorName === "Injectable") {
311
+ else if (decoratorName === 'Injectable') {
318
312
  // For @Injectable, check if it's actually a guard/interceptor/resolver/validator
319
313
  // before defaulting to 'service'
320
314
  const classContent = content.substring(classNode.range[0], classNode.range[1]);
321
315
  if (this.angularPatterns.guard.test(classContent)) {
322
- componentType = "guard";
323
- angularType = "guard";
316
+ componentType = 'guard';
317
+ angularType = 'guard';
324
318
  }
325
319
  else if (this.angularPatterns.interceptor.test(classContent)) {
326
- componentType = "interceptor";
327
- angularType = "interceptor";
320
+ componentType = 'interceptor';
321
+ angularType = 'interceptor';
328
322
  }
329
323
  else if (this.angularPatterns.resolver.test(classContent)) {
330
- componentType = "resolver";
331
- angularType = "resolver";
324
+ componentType = 'resolver';
325
+ angularType = 'resolver';
332
326
  }
333
327
  else if (this.angularPatterns.validator.test(classContent)) {
334
- componentType = "validator";
335
- angularType = "validator";
328
+ componentType = 'validator';
329
+ angularType = 'validator';
336
330
  }
337
331
  else {
338
332
  // Default to service if no specific pattern matches
339
- componentType = "service";
340
- angularType = "service";
333
+ componentType = 'service';
334
+ angularType = 'service';
341
335
  }
342
336
  }
343
337
  // If still no type, check patterns one more time (for classes without decorators)
344
338
  if (!componentType) {
345
339
  const classContent = content.substring(classNode.range[0], classNode.range[1]);
346
340
  if (this.angularPatterns.guard.test(classContent)) {
347
- componentType = "guard";
348
- angularType = "guard";
341
+ componentType = 'guard';
342
+ angularType = 'guard';
349
343
  }
350
344
  else if (this.angularPatterns.interceptor.test(classContent)) {
351
- componentType = "interceptor";
352
- angularType = "interceptor";
345
+ componentType = 'interceptor';
346
+ angularType = 'interceptor';
353
347
  }
354
348
  else if (this.angularPatterns.resolver.test(classContent)) {
355
- componentType = "resolver";
356
- angularType = "resolver";
349
+ componentType = 'resolver';
350
+ angularType = 'resolver';
357
351
  }
358
352
  else if (this.angularPatterns.validator.test(classContent)) {
359
- componentType = "validator";
360
- angularType = "validator";
353
+ componentType = 'validator';
354
+ angularType = 'validator';
361
355
  }
362
356
  }
363
357
  // Extract decorator metadata
@@ -371,15 +365,15 @@ export class AngularAnalyzer {
371
365
  const outputs = this.extractOutputs(classNode);
372
366
  return {
373
367
  name: classNode.id.name,
374
- type: "class",
368
+ type: 'class',
375
369
  componentType,
376
370
  startLine: classNode.loc.start.line,
377
371
  endLine: classNode.loc.end.line,
378
372
  decorators: [
379
373
  {
380
374
  name: decoratorName,
381
- properties: decoratorMetadata,
382
- },
375
+ properties: decoratorMetadata
376
+ }
383
377
  ],
384
378
  lifecycle,
385
379
  dependencies: injectedServices,
@@ -392,8 +386,8 @@ export class AngularAnalyzer {
392
386
  templateUrl: decoratorMetadata.templateUrl,
393
387
  styleUrls: decoratorMetadata.styleUrls,
394
388
  inputs: inputs.map((i) => i.name),
395
- outputs: outputs.map((o) => o.name),
396
- },
389
+ outputs: outputs.map((o) => o.name)
390
+ }
397
391
  };
398
392
  }
399
393
  extractDecoratorMetadata(decorator) {
@@ -401,19 +395,19 @@ export class AngularAnalyzer {
401
395
  try {
402
396
  if (decorator.expression.arguments && decorator.expression.arguments[0]) {
403
397
  const arg = decorator.expression.arguments[0];
404
- if (arg.type === "ObjectExpression") {
398
+ if (arg.type === 'ObjectExpression') {
405
399
  for (const prop of arg.properties) {
406
400
  if (prop.key && prop.value) {
407
401
  const key = prop.key.name || prop.key.value;
408
- if (prop.value.type === "Literal") {
402
+ if (prop.value.type === 'Literal') {
409
403
  metadata[key] = prop.value.value;
410
404
  }
411
- else if (prop.value.type === "ArrayExpression") {
405
+ else if (prop.value.type === 'ArrayExpression') {
412
406
  metadata[key] = prop.value.elements
413
- .map((el) => (el.type === "Literal" ? el.value : null))
407
+ .map((el) => (el.type === 'Literal' ? el.value : null))
414
408
  .filter(Boolean);
415
409
  }
416
- else if (prop.value.type === "Identifier") {
410
+ else if (prop.value.type === 'Identifier') {
417
411
  metadata[key] = prop.value.name;
418
412
  }
419
413
  }
@@ -422,25 +416,25 @@ export class AngularAnalyzer {
422
416
  }
423
417
  }
424
418
  catch (error) {
425
- console.warn("Failed to extract decorator metadata:", error);
419
+ console.warn('Failed to extract decorator metadata:', error);
426
420
  }
427
421
  return metadata;
428
422
  }
429
423
  extractLifecycleHooks(classNode) {
430
424
  const hooks = [];
431
425
  const lifecycleHooks = [
432
- "ngOnChanges",
433
- "ngOnInit",
434
- "ngDoCheck",
435
- "ngAfterContentInit",
436
- "ngAfterContentChecked",
437
- "ngAfterViewInit",
438
- "ngAfterViewChecked",
439
- "ngOnDestroy",
426
+ 'ngOnChanges',
427
+ 'ngOnInit',
428
+ 'ngDoCheck',
429
+ 'ngAfterContentInit',
430
+ 'ngAfterContentChecked',
431
+ 'ngAfterViewInit',
432
+ 'ngAfterViewChecked',
433
+ 'ngOnDestroy'
440
434
  ];
441
435
  if (classNode.body && classNode.body.body) {
442
436
  for (const member of classNode.body.body) {
443
- if (member.type === "MethodDefinition" && member.key) {
437
+ if (member.type === 'MethodDefinition' && member.key) {
444
438
  const methodName = member.key.name;
445
439
  if (lifecycleHooks.includes(methodName)) {
446
440
  hooks.push(methodName);
@@ -455,8 +449,7 @@ export class AngularAnalyzer {
455
449
  // Look for constructor parameters
456
450
  if (classNode.body && classNode.body.body) {
457
451
  for (const member of classNode.body.body) {
458
- if (member.type === "MethodDefinition" &&
459
- member.kind === "constructor") {
452
+ if (member.type === 'MethodDefinition' && member.kind === 'constructor') {
460
453
  if (member.value.params) {
461
454
  for (const param of member.value.params) {
462
455
  if (param.typeAnnotation?.typeAnnotation?.typeName) {
@@ -473,30 +466,29 @@ export class AngularAnalyzer {
473
466
  const inputs = [];
474
467
  if (classNode.body && classNode.body.body) {
475
468
  for (const member of classNode.body.body) {
476
- if (member.type === "PropertyDefinition") {
469
+ if (member.type === 'PropertyDefinition') {
477
470
  // Check for decorator-based @Input()
478
471
  if (member.decorators) {
479
- const hasInput = member.decorators.some((d) => d.expression?.callee?.name === "Input" ||
480
- d.expression?.name === "Input");
472
+ const hasInput = member.decorators.some((d) => d.expression?.callee?.name === 'Input' || d.expression?.name === 'Input');
481
473
  if (hasInput && member.key) {
482
474
  inputs.push({
483
475
  name: member.key.name,
484
- type: member.typeAnnotation?.typeAnnotation?.type || "any",
485
- style: "decorator",
476
+ type: member.typeAnnotation?.typeAnnotation?.type || 'any',
477
+ style: 'decorator'
486
478
  });
487
479
  }
488
480
  }
489
481
  // Check for signal-based input() (Angular v17.1+)
490
482
  if (member.value && member.key) {
491
- const valueStr = member.value.type === "CallExpression"
483
+ const valueStr = member.value.type === 'CallExpression'
492
484
  ? member.value.callee?.name || member.value.callee?.object?.name
493
485
  : null;
494
- if (valueStr === "input") {
486
+ if (valueStr === 'input') {
495
487
  inputs.push({
496
488
  name: member.key.name,
497
- type: "InputSignal",
498
- style: "signal",
499
- required: member.value.callee?.property?.name === "required",
489
+ type: 'InputSignal',
490
+ style: 'signal',
491
+ required: member.value.callee?.property?.name === 'required'
500
492
  });
501
493
  }
502
494
  }
@@ -509,29 +501,26 @@ export class AngularAnalyzer {
509
501
  const outputs = [];
510
502
  if (classNode.body && classNode.body.body) {
511
503
  for (const member of classNode.body.body) {
512
- if (member.type === "PropertyDefinition") {
504
+ if (member.type === 'PropertyDefinition') {
513
505
  // Check for decorator-based @Output()
514
506
  if (member.decorators) {
515
- const hasOutput = member.decorators.some((d) => d.expression?.callee?.name === "Output" ||
516
- d.expression?.name === "Output");
507
+ const hasOutput = member.decorators.some((d) => d.expression?.callee?.name === 'Output' || d.expression?.name === 'Output');
517
508
  if (hasOutput && member.key) {
518
509
  outputs.push({
519
510
  name: member.key.name,
520
- type: "EventEmitter",
521
- style: "decorator",
511
+ type: 'EventEmitter',
512
+ style: 'decorator'
522
513
  });
523
514
  }
524
515
  }
525
516
  // Check for signal-based output() (Angular v17.1+)
526
517
  if (member.value && member.key) {
527
- const valueStr = member.value.type === "CallExpression"
528
- ? member.value.callee?.name
529
- : null;
530
- if (valueStr === "output") {
518
+ const valueStr = member.value.type === 'CallExpression' ? member.value.callee?.name : null;
519
+ if (valueStr === 'output') {
531
520
  outputs.push({
532
521
  name: member.key.name,
533
- type: "OutputEmitterRef",
534
- style: "signal",
522
+ type: 'OutputEmitterRef',
523
+ style: 'signal'
535
524
  });
536
525
  }
537
526
  }
@@ -542,28 +531,28 @@ export class AngularAnalyzer {
542
531
  }
543
532
  async analyzeTemplateFile(filePath, content, relativePath) {
544
533
  // Find corresponding component file
545
- const componentPath = filePath.replace(/\.html$/, ".ts");
534
+ const componentPath = filePath.replace(/\.html$/, '.ts');
546
535
  // Detect legacy vs modern control flow
547
536
  const hasLegacyDirectives = /\*ng(?:If|For|Switch)/.test(content);
548
537
  const hasModernControlFlow = /@(?:if|for|switch|defer)\s*[({]/.test(content);
549
538
  return {
550
539
  filePath,
551
- language: "html",
552
- framework: "angular",
540
+ language: 'html',
541
+ framework: 'angular',
553
542
  components: [],
554
543
  imports: [],
555
544
  exports: [],
556
545
  dependencies: [],
557
546
  metadata: {
558
547
  analyzer: this.name,
559
- type: "template",
548
+ type: 'template',
560
549
  componentPath,
561
550
  hasLegacyDirectives,
562
551
  hasModernControlFlow,
563
552
  hasBindings: /\[|\(|{{/.test(content),
564
- hasDefer: /@defer\s*[({]/.test(content),
553
+ hasDefer: /@defer\s*[({]/.test(content)
565
554
  },
566
- chunks: await createChunksFromCode(content, filePath, relativePath, "html", []),
555
+ chunks: await createChunksFromCode(content, filePath, relativePath, 'html', [])
567
556
  };
568
557
  }
569
558
  async analyzeStyleFile(filePath, content, relativePath) {
@@ -572,16 +561,16 @@ export class AngularAnalyzer {
572
561
  return {
573
562
  filePath,
574
563
  language,
575
- framework: "angular",
564
+ framework: 'angular',
576
565
  components: [],
577
566
  imports: [],
578
567
  exports: [],
579
568
  dependencies: [],
580
569
  metadata: {
581
570
  analyzer: this.name,
582
- type: "style",
571
+ type: 'style'
583
572
  },
584
- chunks: await createChunksFromCode(content, filePath, relativePath, language, []),
573
+ chunks: await createChunksFromCode(content, filePath, relativePath, language, [])
585
574
  };
586
575
  }
587
576
  detectStateManagement(content) {
@@ -595,79 +584,72 @@ export class AngularAnalyzer {
595
584
  determineLayer(filePath, components) {
596
585
  const lowerPath = filePath.toLowerCase();
597
586
  // Check path-based patterns
598
- if (lowerPath.includes("/component") ||
599
- lowerPath.includes("/view") ||
600
- lowerPath.includes("/page")) {
601
- return "presentation";
587
+ if (lowerPath.includes('/component') ||
588
+ lowerPath.includes('/view') ||
589
+ lowerPath.includes('/page')) {
590
+ return 'presentation';
602
591
  }
603
- if (lowerPath.includes("/service")) {
604
- return "business";
592
+ if (lowerPath.includes('/service')) {
593
+ return 'business';
605
594
  }
606
- if (lowerPath.includes("/data") ||
607
- lowerPath.includes("/repository") ||
608
- lowerPath.includes("/api")) {
609
- return "data";
595
+ if (lowerPath.includes('/data') ||
596
+ lowerPath.includes('/repository') ||
597
+ lowerPath.includes('/api')) {
598
+ return 'data';
610
599
  }
611
- if (lowerPath.includes("/store") ||
612
- lowerPath.includes("/state") ||
613
- lowerPath.includes("/ngrx")) {
614
- return "state";
600
+ if (lowerPath.includes('/store') ||
601
+ lowerPath.includes('/state') ||
602
+ lowerPath.includes('/ngrx')) {
603
+ return 'state';
615
604
  }
616
- if (lowerPath.includes("/core")) {
617
- return "core";
605
+ if (lowerPath.includes('/core')) {
606
+ return 'core';
618
607
  }
619
- if (lowerPath.includes("/shared")) {
620
- return "shared";
608
+ if (lowerPath.includes('/shared')) {
609
+ return 'shared';
621
610
  }
622
- if (lowerPath.includes("/feature")) {
623
- return "feature";
611
+ if (lowerPath.includes('/feature')) {
612
+ return 'feature';
624
613
  }
625
614
  // Check component types
626
615
  for (const component of components) {
627
- if (component.componentType === "component" ||
628
- component.componentType === "directive" ||
629
- component.componentType === "pipe") {
630
- return "presentation";
616
+ if (component.componentType === 'component' ||
617
+ component.componentType === 'directive' ||
618
+ component.componentType === 'pipe') {
619
+ return 'presentation';
631
620
  }
632
- if (component.componentType === "service") {
633
- return lowerPath.includes("http") || lowerPath.includes("api")
634
- ? "data"
635
- : "business";
621
+ if (component.componentType === 'service') {
622
+ return lowerPath.includes('http') || lowerPath.includes('api') ? 'data' : 'business';
636
623
  }
637
- if (component.componentType === "guard" ||
638
- component.componentType === "interceptor") {
639
- return "core";
624
+ if (component.componentType === 'guard' || component.componentType === 'interceptor') {
625
+ return 'core';
640
626
  }
641
627
  }
642
- return "unknown";
628
+ return 'unknown';
643
629
  }
644
630
  categorizeDependency(name) {
645
- if (name.startsWith("@angular/")) {
646
- return "framework";
631
+ if (name.startsWith('@angular/')) {
632
+ return 'framework';
647
633
  }
648
- if (name.includes("ngrx") ||
649
- name.includes("akita") ||
650
- name.includes("elf")) {
651
- return "state";
634
+ if (name.includes('ngrx') || name.includes('akita') || name.includes('elf')) {
635
+ return 'state';
652
636
  }
653
- if (name.includes("material") ||
654
- name.includes("primeng") ||
655
- name.includes("ng-bootstrap")) {
656
- return "ui";
637
+ if (name.includes('material') || name.includes('primeng') || name.includes('ng-bootstrap')) {
638
+ return 'ui';
657
639
  }
658
- if (name.includes("router")) {
659
- return "routing";
640
+ if (name.includes('router')) {
641
+ return 'routing';
660
642
  }
661
- if (name.includes("http") || name.includes("common/http")) {
662
- return "http";
643
+ if (name.includes('http') || name.includes('common/http')) {
644
+ return 'http';
663
645
  }
664
- if (name.includes("test") ||
665
- name.includes("jest") ||
666
- name.includes("jasmine") ||
667
- name.includes("karma")) {
668
- return "testing";
646
+ if (name.includes('test') ||
647
+ name.includes('jest') ||
648
+ name.includes('jasmine') ||
649
+ name.includes('karma')) {
650
+ return 'testing';
669
651
  }
670
- return "other";
652
+ return 'other';
671
653
  }
672
654
  async detectCodebaseMetadata(rootPath) {
673
655
  const metadata = {
@@ -676,7 +658,7 @@ export class AngularAnalyzer {
676
658
  languages: [],
677
659
  dependencies: [],
678
660
  architecture: {
679
- type: "feature-based",
661
+ type: 'feature-based',
680
662
  layers: {
681
663
  presentation: 0,
682
664
  business: 0,
@@ -686,14 +668,14 @@ export class AngularAnalyzer {
686
668
  shared: 0,
687
669
  feature: 0,
688
670
  infrastructure: 0,
689
- unknown: 0,
671
+ unknown: 0
690
672
  },
691
- patterns: [],
673
+ patterns: []
692
674
  },
693
675
  styleGuides: [],
694
676
  documentation: [],
695
677
  projectStructure: {
696
- type: "single-app",
678
+ type: 'single-app'
697
679
  },
698
680
  statistics: {
699
681
  totalFiles: 0,
@@ -709,69 +691,69 @@ export class AngularAnalyzer {
709
691
  shared: 0,
710
692
  feature: 0,
711
693
  infrastructure: 0,
712
- unknown: 0,
713
- },
694
+ unknown: 0
695
+ }
714
696
  },
715
- customMetadata: {},
697
+ customMetadata: {}
716
698
  };
717
699
  try {
718
700
  // Read package.json
719
- const packageJsonPath = path.join(rootPath, "package.json");
720
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
701
+ const packageJsonPath = path.join(rootPath, 'package.json');
702
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
721
703
  metadata.name = packageJson.name || metadata.name;
722
704
  // Extract Angular version and dependencies
723
705
  const allDeps = {
724
706
  ...packageJson.dependencies,
725
- ...packageJson.devDependencies,
707
+ ...packageJson.devDependencies
726
708
  };
727
- const angularVersion = allDeps["@angular/core"]?.replace(/[\^~]/, "") || "unknown";
709
+ const angularVersion = allDeps['@angular/core']?.replace(/[\^~]/, '') || 'unknown';
728
710
  // Detect state management
729
711
  const stateManagement = [];
730
- if (allDeps["@ngrx/store"])
731
- stateManagement.push("ngrx");
732
- if (allDeps["@datorama/akita"])
733
- stateManagement.push("akita");
734
- if (allDeps["@ngneat/elf"])
735
- stateManagement.push("elf");
712
+ if (allDeps['@ngrx/store'])
713
+ stateManagement.push('ngrx');
714
+ if (allDeps['@datorama/akita'])
715
+ stateManagement.push('akita');
716
+ if (allDeps['@ngneat/elf'])
717
+ stateManagement.push('elf');
736
718
  // Detect UI libraries
737
719
  const uiLibraries = [];
738
- if (allDeps["@angular/material"])
739
- uiLibraries.push("Angular Material");
740
- if (allDeps["primeng"])
741
- uiLibraries.push("PrimeNG");
742
- if (allDeps["@ng-bootstrap/ng-bootstrap"])
743
- uiLibraries.push("ng-bootstrap");
720
+ if (allDeps['@angular/material'])
721
+ uiLibraries.push('Angular Material');
722
+ if (allDeps['primeng'])
723
+ uiLibraries.push('PrimeNG');
724
+ if (allDeps['@ng-bootstrap/ng-bootstrap'])
725
+ uiLibraries.push('ng-bootstrap');
744
726
  // Detect testing frameworks
745
727
  const testingFrameworks = [];
746
- if (allDeps["jasmine-core"])
747
- testingFrameworks.push("Jasmine");
748
- if (allDeps["karma"])
749
- testingFrameworks.push("Karma");
750
- if (allDeps["jest"])
751
- testingFrameworks.push("Jest");
728
+ if (allDeps['jasmine-core'])
729
+ testingFrameworks.push('Jasmine');
730
+ if (allDeps['karma'])
731
+ testingFrameworks.push('Karma');
732
+ if (allDeps['jest'])
733
+ testingFrameworks.push('Jest');
752
734
  metadata.framework = {
753
- name: "Angular",
735
+ name: 'Angular',
754
736
  version: angularVersion,
755
- type: "angular",
756
- variant: "unknown", // Will be determined during analysis
737
+ type: 'angular',
738
+ variant: 'unknown', // Will be determined during analysis
757
739
  stateManagement,
758
740
  uiLibraries,
759
- testingFrameworks,
741
+ testingFrameworks
760
742
  };
761
743
  // Convert dependencies
762
744
  metadata.dependencies = Object.entries(allDeps).map(([name, version]) => ({
763
745
  name,
764
746
  version: version,
765
- category: this.categorizeDependency(name),
747
+ category: this.categorizeDependency(name)
766
748
  }));
767
749
  }
768
750
  catch (error) {
769
- console.warn("Failed to read Angular project metadata:", error);
751
+ console.warn('Failed to read Angular project metadata:', error);
770
752
  }
771
753
  // Calculate statistics from existing index if available
772
754
  try {
773
- const indexPath = path.join(rootPath, ".codebase-index.json");
774
- const indexContent = await fs.readFile(indexPath, "utf-8");
755
+ const indexPath = path.join(rootPath, CODEBASE_CONTEXT_DIRNAME, KEYWORD_INDEX_FILENAME);
756
+ const indexContent = await fs.readFile(indexPath, 'utf-8');
775
757
  const chunks = JSON.parse(indexContent);
776
758
  console.error(`Loading statistics from ${indexPath}: ${chunks.length} chunks`);
777
759
  if (Array.isArray(chunks) && chunks.length > 0) {
@@ -788,12 +770,11 @@ export class AngularAnalyzer {
788
770
  shared: 0,
789
771
  feature: 0,
790
772
  infrastructure: 0,
791
- unknown: 0,
773
+ unknown: 0
792
774
  };
793
775
  for (const chunk of chunks) {
794
776
  if (chunk.componentType) {
795
- componentCounts[chunk.componentType] =
796
- (componentCounts[chunk.componentType] || 0) + 1;
777
+ componentCounts[chunk.componentType] = (componentCounts[chunk.componentType] || 0) + 1;
797
778
  metadata.statistics.totalComponents++;
798
779
  }
799
780
  if (chunk.layer) {
@@ -808,7 +789,7 @@ export class AngularAnalyzer {
808
789
  }
809
790
  catch (error) {
810
791
  // Index doesn't exist yet, keep statistics at 0
811
- console.warn("Failed to calculate statistics from index:", error);
792
+ console.warn('Failed to calculate statistics from index:', error);
812
793
  }
813
794
  return metadata;
814
795
  }
@@ -822,101 +803,109 @@ export class AngularAnalyzer {
822
803
  const classMatch = content.match(/(?:export\s+)?class\s+(\w+)/);
823
804
  const className = classMatch ? classMatch[1] : fileName;
824
805
  switch (componentType) {
825
- case "component":
826
- const selector = metadata.decorator?.selector || "unknown";
806
+ case 'component': {
807
+ const selector = metadata.decorator?.selector || 'unknown';
827
808
  const inputs = metadata.decorator?.inputs?.length || 0;
828
809
  const outputs = metadata.decorator?.outputs?.length || 0;
829
810
  const lifecycle = this.extractLifecycleMethods(content);
830
- return `Angular component '${className}' (selector: ${selector})${lifecycle ? ` with ${lifecycle}` : ""}${inputs ? `, ${inputs} inputs` : ""}${outputs ? `, ${outputs} outputs` : ""}.`;
831
- case "service":
832
- const providedIn = metadata.decorator?.providedIn || "unknown";
811
+ return `Angular component '${className}' (selector: ${selector})${lifecycle ? ` with ${lifecycle}` : ''}${inputs ? `, ${inputs} inputs` : ''}${outputs ? `, ${outputs} outputs` : ''}.`;
812
+ }
813
+ case 'service': {
814
+ const providedIn = metadata.decorator?.providedIn || 'unknown';
833
815
  const methods = this.extractPublicMethods(content);
834
- return `Angular service '${className}' (providedIn: ${providedIn})${methods ? ` providing ${methods}` : ""}.`;
835
- case "guard":
816
+ return `Angular service '${className}' (providedIn: ${providedIn})${methods ? ` providing ${methods}` : ''}.`;
817
+ }
818
+ case 'guard': {
836
819
  const guardType = this.detectGuardType(content);
837
820
  return `Angular ${guardType} guard '${className}' protecting routes.`;
838
- case "directive":
839
- const directiveSelector = metadata.decorator?.selector || "unknown";
821
+ }
822
+ case 'directive': {
823
+ const directiveSelector = metadata.decorator?.selector || 'unknown';
840
824
  return `Angular directive '${className}' (selector: ${directiveSelector}).`;
841
- case "pipe":
842
- const pipeName = metadata.decorator?.name || "unknown";
825
+ }
826
+ case 'pipe': {
827
+ const pipeName = metadata.decorator?.name || 'unknown';
843
828
  return `Angular pipe '${className}' (name: ${pipeName}) for data transformation.`;
844
- case "module":
829
+ }
830
+ case 'module': {
845
831
  const imports = metadata.decorator?.imports?.length || 0;
846
832
  const declarations = metadata.decorator?.declarations?.length || 0;
847
833
  return `Angular module '${className}' with ${declarations} declarations and ${imports} imports.`;
848
- case "interceptor":
834
+ }
835
+ case 'interceptor':
849
836
  return `Angular HTTP interceptor '${className}' modifying HTTP requests/responses.`;
850
- case "resolver":
837
+ case 'resolver':
851
838
  return `Angular resolver '${className}' pre-fetching route data.`;
852
- case "validator":
839
+ case 'validator':
853
840
  return `Angular validator '${className}' for form validation.`;
854
841
  default:
855
842
  // Try to provide a meaningful fallback
856
843
  if (className && className !== fileName) {
857
844
  // Check for common patterns
858
- if (content.includes("signal(") ||
859
- content.includes("computed(") ||
860
- content.includes("effect(")) {
845
+ if (content.includes('signal(') ||
846
+ content.includes('computed(') ||
847
+ content.includes('effect(')) {
861
848
  return `Angular code '${className}' using signals.`;
862
849
  }
863
- if (content.includes("inject(")) {
850
+ if (content.includes('inject(')) {
864
851
  return `Angular code '${className}' using dependency injection.`;
865
852
  }
866
- if (content.includes("Observable") || content.includes("Subject")) {
853
+ if (content.includes('Observable') || content.includes('Subject')) {
867
854
  return `Angular code '${className}' with reactive streams.`;
868
855
  }
869
856
  return `Angular code '${className}' in ${fileName}.`;
870
857
  }
871
- // Extract first meaningful export or declaration
872
- const exportMatch = content.match(/export\s+(?:const|function|class|interface|type|enum)\s+(\w+)/);
873
- if (exportMatch) {
874
- return `Exports '${exportMatch[1]}' from ${fileName}.`;
858
+ {
859
+ // Extract first meaningful export or declaration
860
+ const exportMatch = content.match(/export\s+(?:const|function|class|interface|type|enum)\s+(\w+)/);
861
+ if (exportMatch) {
862
+ return `Exports '${exportMatch[1]}' from ${fileName}.`;
863
+ }
864
+ return `Angular code in ${fileName}.`;
875
865
  }
876
- return `Angular code in ${fileName}.`;
877
866
  }
878
867
  }
879
868
  extractLifecycleMethods(content) {
880
869
  const lifecycles = [
881
- "ngOnInit",
882
- "ngOnChanges",
883
- "ngOnDestroy",
884
- "ngAfterViewInit",
885
- "ngAfterContentInit",
870
+ 'ngOnInit',
871
+ 'ngOnChanges',
872
+ 'ngOnDestroy',
873
+ 'ngAfterViewInit',
874
+ 'ngAfterContentInit'
886
875
  ];
887
876
  const found = lifecycles.filter((method) => content.includes(method));
888
- return found.length > 0 ? found.join(", ") : "";
877
+ return found.length > 0 ? found.join(', ') : '';
889
878
  }
890
879
  extractPublicMethods(content) {
891
880
  const methodMatches = content.match(/public\s+(\w+)\s*\(/g);
892
881
  if (!methodMatches || methodMatches.length === 0)
893
- return "";
882
+ return '';
894
883
  const methods = methodMatches
895
884
  .slice(0, 3)
896
885
  .map((m) => m.match(/public\s+(\w+)/)?.[1])
897
886
  .filter(Boolean);
898
- return methods.length > 0 ? `methods: ${methods.join(", ")}` : "";
887
+ return methods.length > 0 ? `methods: ${methods.join(', ')}` : '';
899
888
  }
900
889
  detectGuardType(content) {
901
- if (content.includes("CanActivate"))
902
- return "CanActivate";
903
- if (content.includes("CanDeactivate"))
904
- return "CanDeactivate";
905
- if (content.includes("CanLoad"))
906
- return "CanLoad";
907
- if (content.includes("CanMatch"))
908
- return "CanMatch";
909
- return "route";
890
+ if (content.includes('CanActivate'))
891
+ return 'CanActivate';
892
+ if (content.includes('CanDeactivate'))
893
+ return 'CanDeactivate';
894
+ if (content.includes('CanLoad'))
895
+ return 'CanLoad';
896
+ if (content.includes('CanMatch'))
897
+ return 'CanMatch';
898
+ return 'route';
910
899
  }
911
900
  extractFirstComment(content) {
912
901
  const commentMatch = content.match(/\/\*\*\s*\n?\s*\*\s*(.+?)(?:\n|\*\/)/);
913
- return commentMatch ? commentMatch[1].trim() : "";
902
+ return commentMatch ? commentMatch[1].trim() : '';
914
903
  }
915
904
  extractFirstLine(content) {
916
905
  const firstLine = content
917
- .split("\n")
918
- .find((line) => line.trim() && !line.trim().startsWith("import"));
919
- return firstLine ? firstLine.trim().slice(0, 60) + "..." : "";
906
+ .split('\n')
907
+ .find((line) => line.trim() && !line.trim().startsWith('import'));
908
+ return firstLine ? firstLine.trim().slice(0, 60) + '...' : '';
920
909
  }
921
910
  }
922
911
  //# sourceMappingURL=index.js.map