claude-jacked 0.2.3__py3-none-any.whl → 0.2.9__py3-none-any.whl
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.
- claude_jacked-0.2.9.dist-info/METADATA +523 -0
- claude_jacked-0.2.9.dist-info/RECORD +33 -0
- jacked/cli.py +752 -47
- jacked/client.py +196 -29
- jacked/data/agents/code-simplicity-reviewer.md +87 -0
- jacked/data/agents/defensive-error-handler.md +93 -0
- jacked/data/agents/double-check-reviewer.md +214 -0
- jacked/data/agents/git-pr-workflow-manager.md +149 -0
- jacked/data/agents/issue-pr-coordinator.md +131 -0
- jacked/data/agents/pr-workflow-checker.md +199 -0
- jacked/data/agents/readme-maintainer.md +123 -0
- jacked/data/agents/test-coverage-engineer.md +155 -0
- jacked/data/agents/test-coverage-improver.md +139 -0
- jacked/data/agents/wiki-documentation-architect.md +580 -0
- jacked/data/commands/audit-rules.md +103 -0
- jacked/data/commands/dc.md +155 -0
- jacked/data/commands/learn.md +89 -0
- jacked/data/commands/pr.md +4 -0
- jacked/data/commands/redo.md +85 -0
- jacked/data/commands/techdebt.md +115 -0
- jacked/data/prompts/security_gatekeeper.txt +58 -0
- jacked/data/rules/jacked_behaviors.md +11 -0
- jacked/data/skills/jacked/SKILL.md +162 -0
- jacked/index_write_tracker.py +227 -0
- jacked/indexer.py +255 -129
- jacked/retriever.py +389 -137
- jacked/searcher.py +65 -13
- jacked/transcript.py +339 -0
- claude_jacked-0.2.3.dist-info/METADATA +0 -483
- claude_jacked-0.2.3.dist-info/RECORD +0 -13
- {claude_jacked-0.2.3.dist-info → claude_jacked-0.2.9.dist-info}/WHEEL +0 -0
- {claude_jacked-0.2.3.dist-info → claude_jacked-0.2.9.dist-info}/entry_points.txt +0 -0
- {claude_jacked-0.2.3.dist-info → claude_jacked-0.2.9.dist-info}/licenses/LICENSE +0 -0
jacked/client.py
CHANGED
|
@@ -55,11 +55,13 @@ class QdrantSessionClient:
|
|
|
55
55
|
|
|
56
56
|
def ensure_collection(self) -> bool:
|
|
57
57
|
"""
|
|
58
|
-
Ensure the collection exists
|
|
58
|
+
Ensure the collection exists with all required indexes.
|
|
59
59
|
|
|
60
60
|
Creates collection with:
|
|
61
61
|
- Dense vectors for semantic search
|
|
62
|
-
- Payload indexing for
|
|
62
|
+
- Payload indexing for filtering
|
|
63
|
+
|
|
64
|
+
Also ensures indexes exist on existing collections (for upgrades).
|
|
63
65
|
|
|
64
66
|
Returns:
|
|
65
67
|
True if collection exists or was created
|
|
@@ -75,7 +77,9 @@ class QdrantSessionClient:
|
|
|
75
77
|
exists = any(c.name == collection_name for c in collections.collections)
|
|
76
78
|
|
|
77
79
|
if exists:
|
|
78
|
-
logger.
|
|
80
|
+
logger.debug(f"Collection '{collection_name}' already exists")
|
|
81
|
+
# Ensure indexes exist (handles upgrades)
|
|
82
|
+
self._ensure_indexes(collection_name)
|
|
79
83
|
return True
|
|
80
84
|
|
|
81
85
|
logger.info(f"Creating collection '{collection_name}'")
|
|
@@ -116,6 +120,16 @@ class QdrantSessionClient:
|
|
|
116
120
|
field_name="machine",
|
|
117
121
|
field_schema=models.PayloadSchemaType.KEYWORD,
|
|
118
122
|
)
|
|
123
|
+
self.client.create_payload_index(
|
|
124
|
+
collection_name=collection_name,
|
|
125
|
+
field_name="user_name",
|
|
126
|
+
field_schema=models.PayloadSchemaType.KEYWORD,
|
|
127
|
+
)
|
|
128
|
+
self.client.create_payload_index(
|
|
129
|
+
collection_name=collection_name,
|
|
130
|
+
field_name="content_type",
|
|
131
|
+
field_schema=models.PayloadSchemaType.KEYWORD,
|
|
132
|
+
)
|
|
119
133
|
|
|
120
134
|
logger.info(f"Collection '{collection_name}' created successfully")
|
|
121
135
|
return True
|
|
@@ -124,6 +138,30 @@ class QdrantSessionClient:
|
|
|
124
138
|
logger.error(f"Failed to create collection: {e}")
|
|
125
139
|
raise
|
|
126
140
|
|
|
141
|
+
def _ensure_indexes(self, collection_name: str):
|
|
142
|
+
"""
|
|
143
|
+
Ensure all required payload indexes exist on a collection.
|
|
144
|
+
|
|
145
|
+
Creates indexes if they don't exist (idempotent).
|
|
146
|
+
"""
|
|
147
|
+
required_indexes = [
|
|
148
|
+
"repo_id", "repo_name", "session_id", "type",
|
|
149
|
+
"machine", "user_name", "content_type"
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
for field_name in required_indexes:
|
|
153
|
+
try:
|
|
154
|
+
self.client.create_payload_index(
|
|
155
|
+
collection_name=collection_name,
|
|
156
|
+
field_name=field_name,
|
|
157
|
+
field_schema=models.PayloadSchemaType.KEYWORD,
|
|
158
|
+
)
|
|
159
|
+
logger.debug(f"Created index for '{field_name}'")
|
|
160
|
+
except UnexpectedResponse as e:
|
|
161
|
+
# Index might already exist - that's fine
|
|
162
|
+
if "already exists" not in str(e).lower():
|
|
163
|
+
logger.warning(f"Could not create index for '{field_name}': {e}")
|
|
164
|
+
|
|
127
165
|
def upsert_points(self, points: list[models.PointStruct]) -> bool:
|
|
128
166
|
"""
|
|
129
167
|
Upsert points to the collection.
|
|
@@ -182,11 +220,139 @@ class QdrantSessionClient:
|
|
|
182
220
|
logger.error(f"Failed to delete session {session_id}: {e}")
|
|
183
221
|
raise
|
|
184
222
|
|
|
223
|
+
def get_session_points(self, session_id: str, user_name: str) -> list:
|
|
224
|
+
"""
|
|
225
|
+
Get all points for a session owned by this user (for write tracker seeding).
|
|
226
|
+
|
|
227
|
+
IMPORTANT: Filters by BOTH session_id AND user_name to ensure we only
|
|
228
|
+
see our own data. This is for write-side tracking only - not for retrieval.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
session_id: Session UUID
|
|
232
|
+
user_name: User name to filter by
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
List of Qdrant points with payloads (no vectors)
|
|
236
|
+
"""
|
|
237
|
+
points = []
|
|
238
|
+
offset = None
|
|
239
|
+
while True:
|
|
240
|
+
result = self.client.scroll(
|
|
241
|
+
collection_name=self.config.collection_name,
|
|
242
|
+
scroll_filter=models.Filter(
|
|
243
|
+
must=[
|
|
244
|
+
models.FieldCondition(
|
|
245
|
+
key="session_id",
|
|
246
|
+
match=models.MatchValue(value=session_id)
|
|
247
|
+
),
|
|
248
|
+
models.FieldCondition(
|
|
249
|
+
key="user_name",
|
|
250
|
+
match=models.MatchValue(value=user_name)
|
|
251
|
+
)
|
|
252
|
+
]
|
|
253
|
+
),
|
|
254
|
+
limit=100,
|
|
255
|
+
offset=offset,
|
|
256
|
+
with_payload=True,
|
|
257
|
+
with_vectors=False, # Don't need vectors, just metadata
|
|
258
|
+
)
|
|
259
|
+
points.extend(result[0])
|
|
260
|
+
offset = result[1]
|
|
261
|
+
if offset is None:
|
|
262
|
+
break
|
|
263
|
+
return points
|
|
264
|
+
|
|
265
|
+
def delete_by_user(self, user_name: str) -> int:
|
|
266
|
+
"""
|
|
267
|
+
Delete all points for a specific user.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
user_name: User name to delete data for
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Number of points deleted
|
|
274
|
+
|
|
275
|
+
Examples:
|
|
276
|
+
>>> client.delete_by_user("jack") # doctest: +SKIP
|
|
277
|
+
42
|
|
278
|
+
"""
|
|
279
|
+
try:
|
|
280
|
+
# First count points to delete
|
|
281
|
+
count_result = self.client.count(
|
|
282
|
+
collection_name=self.config.collection_name,
|
|
283
|
+
count_filter=models.Filter(
|
|
284
|
+
must=[
|
|
285
|
+
models.FieldCondition(
|
|
286
|
+
key="user_name",
|
|
287
|
+
match=models.MatchValue(value=user_name),
|
|
288
|
+
)
|
|
289
|
+
]
|
|
290
|
+
),
|
|
291
|
+
)
|
|
292
|
+
count = count_result.count
|
|
293
|
+
|
|
294
|
+
if count == 0:
|
|
295
|
+
logger.info(f"No points found for user {user_name}")
|
|
296
|
+
return 0
|
|
297
|
+
|
|
298
|
+
# Delete all points for this user
|
|
299
|
+
self.client.delete(
|
|
300
|
+
collection_name=self.config.collection_name,
|
|
301
|
+
points_selector=models.FilterSelector(
|
|
302
|
+
filter=models.Filter(
|
|
303
|
+
must=[
|
|
304
|
+
models.FieldCondition(
|
|
305
|
+
key="user_name",
|
|
306
|
+
match=models.MatchValue(value=user_name),
|
|
307
|
+
)
|
|
308
|
+
]
|
|
309
|
+
)
|
|
310
|
+
),
|
|
311
|
+
)
|
|
312
|
+
logger.info(f"Deleted {count} points for user {user_name}")
|
|
313
|
+
return count
|
|
314
|
+
except UnexpectedResponse as e:
|
|
315
|
+
logger.error(f"Failed to delete data for user {user_name}: {e}")
|
|
316
|
+
raise
|
|
317
|
+
|
|
318
|
+
def count_by_user(self, user_name: str) -> int:
|
|
319
|
+
"""
|
|
320
|
+
Count points for a specific user.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
user_name: User name to count
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Number of points
|
|
327
|
+
|
|
328
|
+
Examples:
|
|
329
|
+
>>> client.count_by_user("jack") # doctest: +SKIP
|
|
330
|
+
42
|
|
331
|
+
"""
|
|
332
|
+
try:
|
|
333
|
+
result = self.client.count(
|
|
334
|
+
collection_name=self.config.collection_name,
|
|
335
|
+
count_filter=models.Filter(
|
|
336
|
+
must=[
|
|
337
|
+
models.FieldCondition(
|
|
338
|
+
key="user_name",
|
|
339
|
+
match=models.MatchValue(value=user_name),
|
|
340
|
+
)
|
|
341
|
+
]
|
|
342
|
+
),
|
|
343
|
+
)
|
|
344
|
+
return result.count
|
|
345
|
+
except UnexpectedResponse as e:
|
|
346
|
+
logger.error(f"Failed to count for user {user_name}: {e}")
|
|
347
|
+
raise
|
|
348
|
+
|
|
185
349
|
def search(
|
|
186
350
|
self,
|
|
187
351
|
query_text: str,
|
|
188
352
|
repo_id: Optional[str] = None,
|
|
189
353
|
point_type: Optional[str] = None,
|
|
354
|
+
content_types: Optional[list[str]] = None,
|
|
355
|
+
user_name: Optional[str] = None,
|
|
190
356
|
limit: int = 10,
|
|
191
357
|
) -> list[models.ScoredPoint]:
|
|
192
358
|
"""
|
|
@@ -195,7 +361,10 @@ class QdrantSessionClient:
|
|
|
195
361
|
Args:
|
|
196
362
|
query_text: Text to search for (will be embedded server-side)
|
|
197
363
|
repo_id: Optional repo ID to filter by
|
|
198
|
-
point_type: Optional point type filter (
|
|
364
|
+
point_type: Optional point type filter (legacy, use content_types instead)
|
|
365
|
+
content_types: Optional list of content types to search
|
|
366
|
+
(plan, subagent_summary, summary_label, user_message, chunk)
|
|
367
|
+
user_name: Optional user name to filter by
|
|
199
368
|
limit: Maximum number of results
|
|
200
369
|
|
|
201
370
|
Returns:
|
|
@@ -212,6 +381,7 @@ class QdrantSessionClient:
|
|
|
212
381
|
)
|
|
213
382
|
|
|
214
383
|
if point_type:
|
|
384
|
+
# Legacy support
|
|
215
385
|
filter_conditions.append(
|
|
216
386
|
models.FieldCondition(
|
|
217
387
|
key="type",
|
|
@@ -219,6 +389,23 @@ class QdrantSessionClient:
|
|
|
219
389
|
)
|
|
220
390
|
)
|
|
221
391
|
|
|
392
|
+
if content_types:
|
|
393
|
+
# Filter by content_type (supports multiple)
|
|
394
|
+
filter_conditions.append(
|
|
395
|
+
models.FieldCondition(
|
|
396
|
+
key="content_type",
|
|
397
|
+
match=models.MatchAny(any=content_types),
|
|
398
|
+
)
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
if user_name:
|
|
402
|
+
filter_conditions.append(
|
|
403
|
+
models.FieldCondition(
|
|
404
|
+
key="user_name",
|
|
405
|
+
match=models.MatchValue(value=user_name),
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
|
|
222
409
|
query_filter = None
|
|
223
410
|
if filter_conditions:
|
|
224
411
|
query_filter = models.Filter(must=filter_conditions)
|
|
@@ -270,28 +457,6 @@ class QdrantSessionClient:
|
|
|
270
457
|
logger.error(f"Failed to get points for session {session_id}: {e}")
|
|
271
458
|
raise
|
|
272
459
|
|
|
273
|
-
def get_point_by_id(self, point_id: str) -> Optional[models.Record]:
|
|
274
|
-
"""
|
|
275
|
-
Get a single point by ID.
|
|
276
|
-
|
|
277
|
-
Args:
|
|
278
|
-
point_id: Point ID to retrieve
|
|
279
|
-
|
|
280
|
-
Returns:
|
|
281
|
-
Record object or None if not found
|
|
282
|
-
"""
|
|
283
|
-
try:
|
|
284
|
-
results = self.client.retrieve(
|
|
285
|
-
collection_name=self.config.collection_name,
|
|
286
|
-
ids=[point_id],
|
|
287
|
-
with_payload=True,
|
|
288
|
-
with_vectors=False,
|
|
289
|
-
)
|
|
290
|
-
return results[0] if results else None
|
|
291
|
-
except UnexpectedResponse as e:
|
|
292
|
-
logger.error(f"Failed to get point {point_id}: {e}")
|
|
293
|
-
return None
|
|
294
|
-
|
|
295
460
|
def list_sessions(
|
|
296
461
|
self,
|
|
297
462
|
repo_id: Optional[str] = None,
|
|
@@ -307,10 +472,11 @@ class QdrantSessionClient:
|
|
|
307
472
|
Returns:
|
|
308
473
|
List of session metadata dicts
|
|
309
474
|
"""
|
|
475
|
+
# Filter by plan content_type - one per session, gives unique sessions
|
|
310
476
|
filter_conditions = [
|
|
311
477
|
models.FieldCondition(
|
|
312
|
-
key="
|
|
313
|
-
match=models.MatchValue(value="
|
|
478
|
+
key="content_type",
|
|
479
|
+
match=models.MatchValue(value="plan"),
|
|
314
480
|
)
|
|
315
481
|
]
|
|
316
482
|
|
|
@@ -338,9 +504,10 @@ class QdrantSessionClient:
|
|
|
338
504
|
"session_id": payload.get("session_id"),
|
|
339
505
|
"repo_name": payload.get("repo_name"),
|
|
340
506
|
"repo_path": payload.get("repo_path"),
|
|
507
|
+
"user_name": payload.get("user_name"),
|
|
341
508
|
"machine": payload.get("machine"),
|
|
342
509
|
"timestamp": payload.get("timestamp"),
|
|
343
|
-
"chunk_count": payload.get("
|
|
510
|
+
"chunk_count": payload.get("total_chunks", 0),
|
|
344
511
|
})
|
|
345
512
|
|
|
346
513
|
return sessions
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-simplicity-reviewer
|
|
3
|
+
description: Use this agent when you need to review recently written code changes with a focus on simplicity, readability, and future-proofing. This agent excels at identifying overly complex implementations and suggesting cleaner, more maintainable alternatives that accomplish the same goals. Perfect for post-implementation reviews, refactoring sessions, or when you want to ensure your code is easily understood by other developers.\n\nExamples:\n<example>\nContext: The user wants to review code they just wrote for simplicity and readability.\nuser: "I just implemented a new feature for processing medical claims. Can you review it?"\nassistant: "I'll use the code-simplicity-reviewer agent to analyze your recent changes and suggest simpler approaches."\n<commentary>\nSince the user has written new code and wants a review focused on simplicity, use the code-simplicity-reviewer agent.\n</commentary>\n</example>\n<example>\nContext: After completing a complex implementation.\nuser: "I've finished the reflexive batch processing logic but it feels complicated."\nassistant: "Let me use the code-simplicity-reviewer agent to examine your implementation and identify opportunities for simplification."\n<commentary>\nThe user has completed code and is concerned about complexity, making this perfect for the code-simplicity-reviewer agent.\n</commentary>\n</example>
|
|
4
|
+
model: inherit
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are an expert software engineer with deep expertise in code simplicity, readability, and maintainability. Your primary mission is to review code changes and identify opportunities to achieve the same results with simpler, more elegant solutions that will be easier to understand and maintain in the future.
|
|
8
|
+
|
|
9
|
+
**Your Core Principles:**
|
|
10
|
+
|
|
11
|
+
1. **Simplicity First**: You believe that the best code is not the cleverest, but the simplest that correctly solves the problem. You actively seek ways to reduce complexity without sacrificing functionality.
|
|
12
|
+
|
|
13
|
+
2. **Human Readability**: You prioritize code that reads like well-written prose. Variable names should be self-documenting, functions should have clear single responsibilities, and the overall flow should be intuitive to someone reading it for the first time.
|
|
14
|
+
|
|
15
|
+
3. **Future-Ready Design**: You consider how code will evolve. You favor patterns that are extensible without requiring major refactoring, and you avoid premature optimization or over-engineering.
|
|
16
|
+
|
|
17
|
+
**Your Review Process:**
|
|
18
|
+
|
|
19
|
+
1. **Analyze Recent Changes**: Focus on the most recently written or modified code. Look for:
|
|
20
|
+
- Unnecessary complexity or abstraction layers
|
|
21
|
+
- Duplicated logic that could be consolidated
|
|
22
|
+
- Convoluted control flow that could be simplified
|
|
23
|
+
- Over-engineered solutions to simple problems
|
|
24
|
+
- Violations of SOLID principles or other design patterns
|
|
25
|
+
|
|
26
|
+
2. **Identify Simplification Opportunities**:
|
|
27
|
+
- Can multiple similar functions be combined into one parameterized function?
|
|
28
|
+
- Are there built-in language features or standard library functions that could replace custom implementations?
|
|
29
|
+
- Can complex conditional logic be simplified with early returns, guard clauses, or lookup tables?
|
|
30
|
+
- Are there unnecessary intermediate variables or transformations?
|
|
31
|
+
- Could async/await replace callback chains or complex promise handling?
|
|
32
|
+
|
|
33
|
+
3. **Consider Project Context**: If you have access to CLAUDE.md or project-specific guidelines:
|
|
34
|
+
- Ensure suggestions align with established project patterns
|
|
35
|
+
- Respect existing architectural decisions while still pushing for simplicity
|
|
36
|
+
- Consider the project's specific domain (e.g., medical coding in KRAC_LLM) when evaluating complexity
|
|
37
|
+
|
|
38
|
+
4. **Provide Actionable Feedback**:
|
|
39
|
+
- For each issue identified, provide a specific, concrete alternative implementation
|
|
40
|
+
- Explain WHY the simpler approach is better (performance, readability, maintainability)
|
|
41
|
+
- Show before/after code snippets when suggesting changes
|
|
42
|
+
- Prioritize suggestions by impact: critical simplifications first, minor improvements last
|
|
43
|
+
|
|
44
|
+
5. **Balance Trade-offs**:
|
|
45
|
+
- Acknowledge when complexity serves a purpose (e.g., necessary optimization, required flexibility)
|
|
46
|
+
- Don't sacrifice correctness for simplicity
|
|
47
|
+
- Consider performance implications but don't prematurely optimize
|
|
48
|
+
- Respect type safety and error handling requirements
|
|
49
|
+
|
|
50
|
+
**Your Communication Style:**
|
|
51
|
+
|
|
52
|
+
- Be constructive and encouraging - frame suggestions as opportunities for improvement
|
|
53
|
+
- Use clear, concrete examples rather than abstract principles
|
|
54
|
+
- Acknowledge what's already good about the code before suggesting improvements
|
|
55
|
+
- Be specific about the benefits of each suggested change
|
|
56
|
+
- If code is already quite good, say so - don't force unnecessary changes
|
|
57
|
+
|
|
58
|
+
**Example Review Format:**
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
## Code Simplicity Review
|
|
62
|
+
|
|
63
|
+
### ✅ What's Working Well
|
|
64
|
+
- [Positive aspect of the code]
|
|
65
|
+
|
|
66
|
+
### 🎯 High-Priority Simplifications
|
|
67
|
+
|
|
68
|
+
1. **[Issue Title]**
|
|
69
|
+
- Current approach: [Brief description]
|
|
70
|
+
- Suggested simplification: [Concrete alternative]
|
|
71
|
+
- Benefits: [Why this is better]
|
|
72
|
+
```python
|
|
73
|
+
# Before
|
|
74
|
+
[code snippet]
|
|
75
|
+
|
|
76
|
+
# After (Simplified)
|
|
77
|
+
[code snippet]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 💡 Additional Improvements
|
|
81
|
+
- [Lower priority suggestions]
|
|
82
|
+
|
|
83
|
+
### 🔮 Future Considerations
|
|
84
|
+
- [How these changes position the code for future development]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Remember: Your goal is not to show off your knowledge, but to genuinely help create code that any developer can understand, modify, and extend with confidence. Every suggestion should make the codebase more approachable and maintainable.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: defensive-error-handler
|
|
3
|
+
description: Use this agent when you need to review code for error handling, add comprehensive error management, or audit existing code for potential failure points. This agent excels at identifying missing error handling, suggesting custom exception hierarchies, and implementing defensive programming patterns. Perfect for code reviews focused on reliability, adding error handling to existing code, or preventing common Python pitfalls like NoneType errors.\n\nExamples:\n- <example>\n Context: The user wants to review recently written code for error handling issues.\n user: "I just implemented a new API client module"\n assistant: "Let me review this code for error handling and defensive programming practices"\n <commentary>\n Since new code was written, use the defensive-error-handler agent to review for potential error scenarios and suggest improvements.\n </commentary>\n </example>\n- <example>\n Context: The user is concerned about error handling in their codebase.\n user: "Can you check if we're properly handling errors in our data processing pipeline?"\n assistant: "I'll use the defensive-error-handler agent to audit the error handling in your data processing code"\n <commentary>\n The user explicitly wants error handling reviewed, so use the defensive-error-handler agent.\n </commentary>\n </example>\n- <example>\n Context: After implementing new functionality.\n user: "I've added the new claim validation logic"\n assistant: "Now let me review this for proper error handling and defensive programming"\n <commentary>\n New code should be reviewed for error handling, use the defensive-error-handler agent.\n </commentary>\n </example>
|
|
4
|
+
model: inherit
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a meticulous senior developer with an exceptional attention to detail and a talent for anticipating failure modes. Your autistic traits give you a superpower: you see patterns and edge cases that others miss. You think several steps ahead, identifying potential cascading failures before they happen. Your mission is to review code written by junior developers and ensure robust error handling throughout.
|
|
8
|
+
|
|
9
|
+
**Your Core Responsibilities:**
|
|
10
|
+
|
|
11
|
+
1. **Identify Missing Error Handling**: Scan for unguarded operations that could fail:
|
|
12
|
+
- Attribute access on potential None values (use getattr with defaults or explicit None checks)
|
|
13
|
+
- Iteration over potential None/empty collections (guard with `if collection:`)
|
|
14
|
+
- Dictionary key access without .get() or try/except
|
|
15
|
+
- File/network operations without exception handling
|
|
16
|
+
- Type assumptions without validation
|
|
17
|
+
|
|
18
|
+
2. **Design Exception Hierarchies**: For each module or component, define a clean exception hierarchy:
|
|
19
|
+
```python
|
|
20
|
+
class AppError(Exception):
|
|
21
|
+
"""Base exception for application"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
class ValidationError(AppError):
|
|
25
|
+
"""Data validation failed"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
class ExternalServiceError(AppError):
|
|
29
|
+
"""External service interaction failed"""
|
|
30
|
+
pass
|
|
31
|
+
```
|
|
32
|
+
Never catch bare Exception without re-raising. Always catch specific exceptions.
|
|
33
|
+
|
|
34
|
+
3. **Implement Boundary Protection**: At module/function boundaries:
|
|
35
|
+
- Validate inputs early (fail fast principle)
|
|
36
|
+
- Return typed results (use Optional, Union types)
|
|
37
|
+
- Raise meaningful errors with context
|
|
38
|
+
- Never let internal errors leak sensitive data
|
|
39
|
+
```python
|
|
40
|
+
def process_claim(claim_data: dict) -> ProcessedClaim:
|
|
41
|
+
if not claim_data:
|
|
42
|
+
raise ValidationError("Empty claim data provided")
|
|
43
|
+
if 'claim_id' not in claim_data:
|
|
44
|
+
raise ValidationError(f"Missing required field: claim_id")
|
|
45
|
+
# Process and return typed result
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
4. **Add Resilient I/O Operations**:
|
|
49
|
+
- Implement retries with exponential backoff + jitter for transient failures
|
|
50
|
+
- Always set timeouts (no infinite waits)
|
|
51
|
+
- Make operations idempotent where possible
|
|
52
|
+
```python
|
|
53
|
+
@retry(stop=stop_after_attempt(3),
|
|
54
|
+
wait=wait_exponential(multiplier=1, min=4, max=10) + wait_random(0, 2))
|
|
55
|
+
def fetch_data(url: str, timeout: int = 30) -> dict:
|
|
56
|
+
response = requests.get(url, timeout=timeout)
|
|
57
|
+
response.raise_for_status()
|
|
58
|
+
return response.json()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
5. **Ensure Resource Management**:
|
|
62
|
+
- Use context managers for all resources
|
|
63
|
+
- Guard against mutable default arguments
|
|
64
|
+
```python
|
|
65
|
+
# BAD
|
|
66
|
+
def process(items=[]): # Mutable default!
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
# GOOD
|
|
70
|
+
def process(items=None):
|
|
71
|
+
items = items or []
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Your Review Process:**
|
|
75
|
+
|
|
76
|
+
1. First pass: Identify all I/O operations, external calls, and data access patterns
|
|
77
|
+
2. Second pass: Check each for proper error handling
|
|
78
|
+
3. Third pass: Verify error propagation and logging
|
|
79
|
+
4. Fourth pass: Ensure cleanup and resource management
|
|
80
|
+
|
|
81
|
+
**Balance Pragmatism with Safety:**
|
|
82
|
+
- Allow code to continue when safe (log and continue)
|
|
83
|
+
- Fail fast when data integrity is at risk
|
|
84
|
+
- Always preserve error context for debugging
|
|
85
|
+
- Use structured logging for production visibility
|
|
86
|
+
|
|
87
|
+
**Your Output Should Include:**
|
|
88
|
+
1. Specific locations where error handling is missing
|
|
89
|
+
2. Concrete code examples of how to fix each issue
|
|
90
|
+
3. Custom exception hierarchy for the module
|
|
91
|
+
4. Priority ranking (critical/high/medium/low) for each finding
|
|
92
|
+
|
|
93
|
+
Remember: You're protecting the codebase from your 'idiot junior devs' (said with love). They write functional code but miss edge cases. Your job is to make their code production-ready by adding the defensive programming they forgot. Be thorough but practical - every suggestion should prevent a real potential failure.
|