claude-self-reflect 3.3.0 → 3.3.1

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.
@@ -0,0 +1,556 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Unified AST-GREP Pattern Registry
4
+ Combines custom patterns with official catalog patterns
5
+ MANDATORY: Uses AST patterns only, no regex
6
+ """
7
+
8
+ from typing import Dict, List, Any
9
+ import json
10
+ import logging
11
+ from pathlib import Path
12
+
13
+ # Setup logger
14
+ logger = logging.getLogger(__name__)
15
+ logging.basicConfig(level=logging.INFO)
16
+
17
+ class UnifiedASTGrepRegistry:
18
+ """
19
+ Unified registry combining:
20
+ 1. Custom AST patterns for Python
21
+ 2. Official catalog patterns from AST-GREP
22
+ 3. TypeScript/JavaScript patterns
23
+ All patterns are AST-based, not regex.
24
+ """
25
+
26
+ def __init__(self):
27
+ self.patterns = self._load_unified_patterns()
28
+
29
+ # Merge auto-updated catalog if present
30
+ json_path = Path(__file__).parent / "unified_registry.json"
31
+ if json_path.exists():
32
+ try:
33
+ with open(json_path, 'r') as f:
34
+ data = json.load(f)
35
+ # Merge catalog patterns into existing patterns
36
+ catalog_patterns = data.get("patterns", {})
37
+ for category, patterns in catalog_patterns.items():
38
+ if category not in self.patterns:
39
+ self.patterns[category] = []
40
+ # Add patterns that don't already exist
41
+ existing_ids = {p['id'] for p in self.patterns[category]}
42
+ for pattern in patterns:
43
+ if pattern.get('id') not in existing_ids:
44
+ self.patterns[category].append(pattern)
45
+ except Exception as e:
46
+ # Continue with static patterns if catalog load fails
47
+ pass
48
+
49
+ def _load_unified_patterns(self) -> Dict[str, List[Dict[str, Any]]]:
50
+ """Load unified patterns from multiple sources."""
51
+ patterns = {}
52
+
53
+ # Python patterns (custom)
54
+ patterns.update(self._load_python_patterns())
55
+
56
+ # TypeScript patterns (from catalog)
57
+ patterns.update(self._load_typescript_patterns())
58
+
59
+ # JavaScript patterns (shared with TS)
60
+ patterns.update(self._load_javascript_patterns())
61
+
62
+ return patterns
63
+
64
+ def _load_python_patterns(self) -> Dict[str, List[Dict[str, Any]]]:
65
+ """Python-specific AST patterns."""
66
+ return {
67
+ "python_async": [
68
+ {
69
+ "id": "async-function",
70
+ "pattern": "async def $FUNC($$$): $$$",
71
+ "description": "Async function definition",
72
+ "quality": "good",
73
+ "weight": 2,
74
+ "language": "python"
75
+ },
76
+ {
77
+ "id": "async-with",
78
+ "pattern": "async with $RESOURCE: $$$",
79
+ "description": "Async context manager",
80
+ "quality": "good",
81
+ "weight": 3,
82
+ "language": "python"
83
+ },
84
+ {
85
+ "id": "await-gather",
86
+ "pattern": "await asyncio.gather($$$)",
87
+ "description": "Parallel async execution",
88
+ "quality": "good",
89
+ "weight": 4,
90
+ "language": "python"
91
+ },
92
+ {
93
+ "id": "await-call",
94
+ "pattern": "await $FUNC($$$)",
95
+ "description": "Awaited async call",
96
+ "quality": "neutral",
97
+ "weight": 1,
98
+ "language": "python"
99
+ }
100
+ ],
101
+ "python_error_handling": [
102
+ {
103
+ "id": "specific-except",
104
+ "pattern": "except $ERROR: $$$",
105
+ "description": "Specific exception handling",
106
+ "quality": "good",
107
+ "weight": 3,
108
+ "language": "python"
109
+ },
110
+ {
111
+ "id": "broad-except",
112
+ "pattern": "except: $$$",
113
+ "description": "Bare except clause",
114
+ "quality": "bad",
115
+ "weight": -3,
116
+ "language": "python"
117
+ },
118
+ {
119
+ "id": "try-finally",
120
+ "pattern": "try: $TRY finally: $FINALLY",
121
+ "description": "Try-finally block",
122
+ "quality": "good",
123
+ "weight": 2,
124
+ "language": "python"
125
+ }
126
+ ],
127
+ "python_logging": [
128
+ {
129
+ "id": "logger-call",
130
+ "pattern": "logger.$METHOD($$$)",
131
+ "description": "Logger usage",
132
+ "quality": "good",
133
+ "weight": 2,
134
+ "language": "python"
135
+ },
136
+ {
137
+ "id": "print-call",
138
+ "pattern": "print($$$)",
139
+ "description": "Print statement",
140
+ "quality": "bad",
141
+ "weight": -1,
142
+ "language": "python"
143
+ },
144
+ {
145
+ "id": "debug-print-f-sq",
146
+ "pattern": "print(f'$A')",
147
+ "description": "F-string print (single quote)",
148
+ "quality": "bad",
149
+ "weight": -2,
150
+ "language": "python"
151
+ },
152
+ {
153
+ "id": "debug-print-f-dq",
154
+ "pattern": "print(f\"$A\")",
155
+ "description": "F-string print (double quote)",
156
+ "quality": "bad",
157
+ "weight": -2,
158
+ "language": "python"
159
+ }
160
+ ],
161
+ "python_typing": [
162
+ {
163
+ "id": "typed-function",
164
+ "pattern": "def $FUNC($$$) -> $RETURN: $$$",
165
+ "description": "Function with return type",
166
+ "quality": "good",
167
+ "weight": 3,
168
+ "language": "python"
169
+ },
170
+ {
171
+ "id": "typed-async",
172
+ "pattern": "async def $FUNC($$$) -> $RETURN: $$$",
173
+ "description": "Async function with return type",
174
+ "quality": "good",
175
+ "weight": 4,
176
+ "language": "python"
177
+ },
178
+ {
179
+ "id": "type-annotation",
180
+ "pattern": "$VAR: $TYPE = $$$",
181
+ "description": "Variable type annotation",
182
+ "quality": "good",
183
+ "weight": 2,
184
+ "language": "python"
185
+ }
186
+ ],
187
+ "python_antipatterns": [
188
+ {
189
+ "id": "sync-sleep",
190
+ "pattern": "time.sleep($$$)",
191
+ "description": "Blocking sleep in async context",
192
+ "quality": "bad",
193
+ "weight": -5,
194
+ "language": "python"
195
+ },
196
+ {
197
+ "id": "sync-open",
198
+ "pattern": "open($$$)",
199
+ "description": "Sync file open (should use aiofiles)",
200
+ "quality": "bad",
201
+ "weight": -3,
202
+ "language": "python"
203
+ },
204
+ {
205
+ "id": "requests-call",
206
+ "pattern": "requests.$METHOD($$$)",
207
+ "description": "Sync HTTP request (should use aiohttp)",
208
+ "quality": "bad",
209
+ "weight": -4,
210
+ "language": "python"
211
+ },
212
+ {
213
+ "id": "global-var",
214
+ "pattern": "global $VAR",
215
+ "description": "Global variable usage",
216
+ "quality": "bad",
217
+ "weight": -2,
218
+ "language": "python"
219
+ },
220
+ {
221
+ "id": "mutable-default",
222
+ "pattern": "def $FUNC($$$, $ARG=[]): $$$",
223
+ "description": "Mutable default argument",
224
+ "quality": "bad",
225
+ "weight": -4,
226
+ "language": "python"
227
+ }
228
+ ],
229
+ "python_qdrant": [
230
+ {
231
+ "id": "qdrant-search",
232
+ "pattern": "$CLIENT.search($$$)",
233
+ "description": "Qdrant search operation",
234
+ "quality": "neutral",
235
+ "weight": 1,
236
+ "language": "python"
237
+ },
238
+ {
239
+ "id": "qdrant-upsert",
240
+ "pattern": "$CLIENT.upsert($$$)",
241
+ "description": "Qdrant upsert operation",
242
+ "quality": "neutral",
243
+ "weight": 1,
244
+ "language": "python"
245
+ },
246
+ {
247
+ "id": "collection-create",
248
+ "pattern": "create_collection($$$)",
249
+ "description": "Collection creation",
250
+ "quality": "neutral",
251
+ "weight": 1,
252
+ "language": "python"
253
+ }
254
+ ],
255
+ "python_mcp": [
256
+ {
257
+ "id": "mcp-tool",
258
+ "pattern": "@server.tool\nasync def $TOOL($$$): $$$",
259
+ "description": "MCP tool definition",
260
+ "quality": "good",
261
+ "weight": 5,
262
+ "language": "python"
263
+ },
264
+ {
265
+ "id": "mcp-resource",
266
+ "pattern": "@server.resource($$$)\nasync def $RESOURCE($$$): $$$",
267
+ "description": "MCP resource definition",
268
+ "quality": "good",
269
+ "weight": 5,
270
+ "language": "python"
271
+ }
272
+ ]
273
+ }
274
+
275
+ def _load_typescript_patterns(self) -> Dict[str, List[Dict[str, Any]]]:
276
+ """TypeScript-specific patterns from catalog."""
277
+ return {
278
+ "typescript_async": [
279
+ {
280
+ "id": "no-await-in-promise-all",
281
+ "pattern": "await $A",
282
+ "inside": "Promise.all($_)",
283
+ "description": "No await in Promise.all array",
284
+ "quality": "bad",
285
+ "weight": -4,
286
+ "language": "typescript",
287
+ "fix": "$A"
288
+ },
289
+ {
290
+ "id": "async-function-ts",
291
+ "pattern": "async function $FUNC($$$) { $$$ }",
292
+ "description": "Async function",
293
+ "quality": "good",
294
+ "weight": 2,
295
+ "language": "typescript"
296
+ },
297
+ {
298
+ "id": "async-arrow",
299
+ "pattern": "async ($$$) => { $$$ }",
300
+ "description": "Async arrow function",
301
+ "quality": "good",
302
+ "weight": 2,
303
+ "language": "typescript"
304
+ }
305
+ ],
306
+ "typescript_console": [
307
+ {
308
+ "id": "no-console-log",
309
+ "pattern": "console.log($$$)",
310
+ "description": "Console.log usage",
311
+ "quality": "bad",
312
+ "weight": -2,
313
+ "language": "typescript",
314
+ "fix": ""
315
+ },
316
+ {
317
+ "id": "no-console-debug",
318
+ "pattern": "console.debug($$$)",
319
+ "description": "Console.debug usage",
320
+ "quality": "bad",
321
+ "weight": -2,
322
+ "language": "typescript",
323
+ "fix": ""
324
+ },
325
+ {
326
+ "id": "console-error-in-catch",
327
+ "pattern": "console.error($$$)",
328
+ "inside": "catch ($_) { $$$ }",
329
+ "description": "Console.error in catch (OK)",
330
+ "quality": "neutral",
331
+ "weight": 0,
332
+ "language": "typescript"
333
+ }
334
+ ],
335
+ "typescript_react": [
336
+ {
337
+ "id": "useState-hook",
338
+ "pattern": "const [$STATE, $SETTER] = useState($$$)",
339
+ "description": "React useState hook",
340
+ "quality": "good",
341
+ "weight": 2,
342
+ "language": "typescript"
343
+ },
344
+ {
345
+ "id": "useEffect-hook",
346
+ "pattern": "useEffect(() => { $$$ }, $DEPS)",
347
+ "description": "React useEffect hook",
348
+ "quality": "neutral",
349
+ "weight": 1,
350
+ "language": "typescript"
351
+ },
352
+ {
353
+ "id": "useEffect-no-deps",
354
+ "pattern": "useEffect(() => { $$$ })",
355
+ "description": "useEffect without dependencies",
356
+ "quality": "bad",
357
+ "weight": -3,
358
+ "language": "typescript"
359
+ }
360
+ ],
361
+ "typescript_imports": [
362
+ {
363
+ "id": "barrel-import",
364
+ "pattern": "import { $$$ } from '$MODULE'",
365
+ "description": "Named import",
366
+ "quality": "neutral",
367
+ "weight": 0,
368
+ "language": "typescript"
369
+ },
370
+ {
371
+ "id": "default-import",
372
+ "pattern": "import $NAME from '$MODULE'",
373
+ "description": "Default import",
374
+ "quality": "neutral",
375
+ "weight": 0,
376
+ "language": "typescript"
377
+ },
378
+ {
379
+ "id": "import-all",
380
+ "pattern": "import * as $NAME from '$MODULE'",
381
+ "description": "Import all",
382
+ "quality": "neutral",
383
+ "weight": -1,
384
+ "language": "typescript"
385
+ }
386
+ ]
387
+ }
388
+
389
+ def _load_javascript_patterns(self) -> Dict[str, List[Dict[str, Any]]]:
390
+ """JavaScript patterns (subset of TypeScript)."""
391
+ return {
392
+ "javascript_async": [
393
+ {
394
+ "id": "callback-hell",
395
+ "pattern": "$FUNC($$$, function($$$) { $$$ })",
396
+ "description": "Callback pattern (consider promises)",
397
+ "quality": "bad",
398
+ "weight": -2,
399
+ "language": "javascript"
400
+ },
401
+ {
402
+ "id": "promise-then",
403
+ "pattern": "$PROMISE.then($$$)",
404
+ "description": "Promise then chain",
405
+ "quality": "neutral",
406
+ "weight": 0,
407
+ "language": "javascript"
408
+ },
409
+ {
410
+ "id": "async-await",
411
+ "pattern": "await $PROMISE",
412
+ "description": "Async/await usage",
413
+ "quality": "good",
414
+ "weight": 2,
415
+ "language": "javascript"
416
+ }
417
+ ],
418
+ "javascript_var": [
419
+ {
420
+ "id": "var-declaration",
421
+ "pattern": "var $VAR = $$$",
422
+ "description": "Var declaration (use const/let)",
423
+ "quality": "bad",
424
+ "weight": -3,
425
+ "language": "javascript"
426
+ },
427
+ {
428
+ "id": "const-declaration",
429
+ "pattern": "const $VAR = $$$",
430
+ "description": "Const declaration",
431
+ "quality": "good",
432
+ "weight": 2,
433
+ "language": "javascript"
434
+ },
435
+ {
436
+ "id": "let-declaration",
437
+ "pattern": "let $VAR = $$$",
438
+ "description": "Let declaration",
439
+ "quality": "good",
440
+ "weight": 1,
441
+ "language": "javascript"
442
+ }
443
+ ]
444
+ }
445
+
446
+ def get_all_patterns(self) -> List[Dict[str, Any]]:
447
+ """Get all patterns as a flat list."""
448
+ all_patterns = []
449
+ for category, patterns in self.patterns.items():
450
+ for pattern in patterns:
451
+ # Avoid mutating source; create a copy
452
+ item = dict(pattern)
453
+ item['category'] = category
454
+ all_patterns.append(item)
455
+ return all_patterns
456
+
457
+ def get_patterns_by_language(self, language: str) -> List[Dict[str, Any]]:
458
+ """Get patterns for a specific language."""
459
+ return [p for p in self.get_all_patterns() if p.get('language') == language]
460
+
461
+ def get_good_patterns(self) -> List[Dict[str, Any]]:
462
+ """Get only good quality patterns."""
463
+ return [p for p in self.get_all_patterns() if p.get('quality') == 'good']
464
+
465
+ def get_bad_patterns(self) -> List[Dict[str, Any]]:
466
+ """Get only bad quality patterns (anti-patterns)."""
467
+ return [p for p in self.get_all_patterns() if p.get('quality') == 'bad']
468
+
469
+ def calculate_quality_score(self, matches: List[Dict]) -> float:
470
+ """
471
+ Calculate quality score based on pattern matches.
472
+ Each match includes the pattern and count.
473
+ """
474
+ total_weight = 0
475
+ total_count = 0
476
+
477
+ for match in matches:
478
+ weight = match.get('weight', 0)
479
+ count = match.get('count', 0)
480
+ total_weight += weight * count
481
+ total_count += abs(weight) * count
482
+
483
+ if total_count == 0:
484
+ return 0.5
485
+
486
+ # Normalize to 0-1 range
487
+ normalized = (total_weight + 100) / 200
488
+ return max(0.0, min(1.0, normalized))
489
+
490
+ def export_to_json(self, path: str):
491
+ """Export registry to JSON file."""
492
+ data = {
493
+ 'source': 'unified-ast-grep',
494
+ 'version': '2.0.0',
495
+ 'patterns': self.patterns,
496
+ 'stats': {
497
+ 'total_patterns': len(self.get_all_patterns()),
498
+ 'good_patterns': len(self.get_good_patterns()),
499
+ 'bad_patterns': len(self.get_bad_patterns()),
500
+ 'languages': list(set(p.get('language') for p in self.get_all_patterns())),
501
+ 'categories': list(self.patterns.keys())
502
+ }
503
+ }
504
+
505
+ with open(path, 'w') as f:
506
+ json.dump(data, f, indent=2)
507
+
508
+
509
+ # Singleton instance
510
+ _unified_registry = None
511
+
512
+ def get_unified_registry() -> UnifiedASTGrepRegistry:
513
+ """Get or create the unified AST-GREP pattern registry."""
514
+ global _unified_registry
515
+ if _unified_registry is None:
516
+ _unified_registry = UnifiedASTGrepRegistry()
517
+ return _unified_registry
518
+
519
+
520
+ if __name__ == "__main__":
521
+ # Test the unified registry
522
+ registry = get_unified_registry()
523
+
524
+ print("Unified AST-GREP Pattern Registry")
525
+ print("=" * 60)
526
+
527
+ all_patterns = registry.get_all_patterns()
528
+ print(f"\nTotal patterns: {len(all_patterns)}")
529
+ print(f"Good patterns: {len(registry.get_good_patterns())}")
530
+ print(f"Bad patterns: {len(registry.get_bad_patterns())}")
531
+
532
+ # Count by language
533
+ languages = {}
534
+ for pattern in all_patterns:
535
+ lang = pattern.get('language', 'unknown')
536
+ languages[lang] = languages.get(lang, 0) + 1
537
+
538
+ print(f"\nPatterns by language:")
539
+ for lang, count in languages.items():
540
+ print(f" - {lang}: {count} patterns")
541
+
542
+ print(f"\nCategories ({len(registry.patterns)}):")
543
+ for category in registry.patterns.keys():
544
+ count = len(registry.patterns[category])
545
+ print(f" - {category}: {count} patterns")
546
+
547
+ # Export to JSON
548
+ export_path = "/Users/ramakrishnanannaswamy/projects/claude-self-reflect/scripts/unified_registry.json"
549
+ registry.export_to_json(export_path)
550
+ print(f"\n✅ Exported unified registry to {export_path}")
551
+
552
+ # Show sample patterns
553
+ print("\nSample patterns:")
554
+ for pattern in all_patterns[:5]:
555
+ print(f" - {pattern['id']} ({pattern['language']}): {pattern.get('pattern', 'N/A')[:40]}...")
556
+ print(f" Quality: {pattern['quality']}, Weight: {pattern['weight']}")