kairo-code 0.1.0__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.
Files changed (144) hide show
  1. image-service/main.py +178 -0
  2. infra/chat/app/main.py +84 -0
  3. kairo/backend/__init__.py +0 -0
  4. kairo/backend/api/__init__.py +0 -0
  5. kairo/backend/api/admin/__init__.py +23 -0
  6. kairo/backend/api/admin/audit.py +54 -0
  7. kairo/backend/api/admin/content.py +142 -0
  8. kairo/backend/api/admin/incidents.py +148 -0
  9. kairo/backend/api/admin/stats.py +125 -0
  10. kairo/backend/api/admin/system.py +87 -0
  11. kairo/backend/api/admin/users.py +279 -0
  12. kairo/backend/api/agents.py +94 -0
  13. kairo/backend/api/api_keys.py +85 -0
  14. kairo/backend/api/auth.py +116 -0
  15. kairo/backend/api/billing.py +41 -0
  16. kairo/backend/api/chat.py +72 -0
  17. kairo/backend/api/conversations.py +125 -0
  18. kairo/backend/api/device_auth.py +100 -0
  19. kairo/backend/api/files.py +83 -0
  20. kairo/backend/api/health.py +36 -0
  21. kairo/backend/api/images.py +80 -0
  22. kairo/backend/api/openai_compat.py +225 -0
  23. kairo/backend/api/projects.py +102 -0
  24. kairo/backend/api/usage.py +32 -0
  25. kairo/backend/api/webhooks.py +79 -0
  26. kairo/backend/app.py +297 -0
  27. kairo/backend/config.py +179 -0
  28. kairo/backend/core/__init__.py +0 -0
  29. kairo/backend/core/admin_auth.py +24 -0
  30. kairo/backend/core/api_key_auth.py +55 -0
  31. kairo/backend/core/database.py +28 -0
  32. kairo/backend/core/dependencies.py +70 -0
  33. kairo/backend/core/logging.py +23 -0
  34. kairo/backend/core/rate_limit.py +73 -0
  35. kairo/backend/core/security.py +29 -0
  36. kairo/backend/models/__init__.py +19 -0
  37. kairo/backend/models/agent.py +30 -0
  38. kairo/backend/models/api_key.py +25 -0
  39. kairo/backend/models/api_usage.py +29 -0
  40. kairo/backend/models/audit_log.py +26 -0
  41. kairo/backend/models/conversation.py +48 -0
  42. kairo/backend/models/device_code.py +30 -0
  43. kairo/backend/models/feature_flag.py +21 -0
  44. kairo/backend/models/image_generation.py +24 -0
  45. kairo/backend/models/incident.py +28 -0
  46. kairo/backend/models/project.py +28 -0
  47. kairo/backend/models/uptime_record.py +24 -0
  48. kairo/backend/models/usage.py +24 -0
  49. kairo/backend/models/user.py +49 -0
  50. kairo/backend/schemas/__init__.py +0 -0
  51. kairo/backend/schemas/admin/__init__.py +0 -0
  52. kairo/backend/schemas/admin/audit.py +28 -0
  53. kairo/backend/schemas/admin/content.py +53 -0
  54. kairo/backend/schemas/admin/stats.py +77 -0
  55. kairo/backend/schemas/admin/system.py +44 -0
  56. kairo/backend/schemas/admin/users.py +48 -0
  57. kairo/backend/schemas/agent.py +42 -0
  58. kairo/backend/schemas/api_key.py +30 -0
  59. kairo/backend/schemas/auth.py +57 -0
  60. kairo/backend/schemas/chat.py +26 -0
  61. kairo/backend/schemas/conversation.py +39 -0
  62. kairo/backend/schemas/device_auth.py +40 -0
  63. kairo/backend/schemas/image.py +15 -0
  64. kairo/backend/schemas/openai_compat.py +76 -0
  65. kairo/backend/schemas/project.py +21 -0
  66. kairo/backend/schemas/status.py +81 -0
  67. kairo/backend/schemas/usage.py +15 -0
  68. kairo/backend/services/__init__.py +0 -0
  69. kairo/backend/services/admin/__init__.py +0 -0
  70. kairo/backend/services/admin/audit_service.py +78 -0
  71. kairo/backend/services/admin/content_service.py +119 -0
  72. kairo/backend/services/admin/incident_service.py +94 -0
  73. kairo/backend/services/admin/stats_service.py +281 -0
  74. kairo/backend/services/admin/system_service.py +126 -0
  75. kairo/backend/services/admin/user_service.py +157 -0
  76. kairo/backend/services/agent_service.py +107 -0
  77. kairo/backend/services/api_key_service.py +66 -0
  78. kairo/backend/services/api_usage_service.py +126 -0
  79. kairo/backend/services/auth_service.py +101 -0
  80. kairo/backend/services/chat_service.py +501 -0
  81. kairo/backend/services/conversation_service.py +264 -0
  82. kairo/backend/services/device_auth_service.py +193 -0
  83. kairo/backend/services/email_service.py +55 -0
  84. kairo/backend/services/image_service.py +181 -0
  85. kairo/backend/services/llm_service.py +186 -0
  86. kairo/backend/services/project_service.py +109 -0
  87. kairo/backend/services/status_service.py +167 -0
  88. kairo/backend/services/stripe_service.py +78 -0
  89. kairo/backend/services/usage_service.py +150 -0
  90. kairo/backend/services/web_search_service.py +96 -0
  91. kairo/migrations/env.py +60 -0
  92. kairo/migrations/versions/001_initial.py +55 -0
  93. kairo/migrations/versions/002_usage_tracking_and_indexes.py +66 -0
  94. kairo/migrations/versions/003_username_to_email.py +21 -0
  95. kairo/migrations/versions/004_add_plans_and_verification.py +67 -0
  96. kairo/migrations/versions/005_add_projects.py +52 -0
  97. kairo/migrations/versions/006_add_image_generation.py +63 -0
  98. kairo/migrations/versions/007_add_admin_portal.py +107 -0
  99. kairo/migrations/versions/008_add_device_code_auth.py +76 -0
  100. kairo/migrations/versions/009_add_status_page.py +65 -0
  101. kairo/tools/extract_claude_data.py +465 -0
  102. kairo/tools/filter_claude_data.py +303 -0
  103. kairo/tools/generate_curated_data.py +157 -0
  104. kairo/tools/mix_training_data.py +295 -0
  105. kairo_code/__init__.py +3 -0
  106. kairo_code/agents/__init__.py +25 -0
  107. kairo_code/agents/architect.py +98 -0
  108. kairo_code/agents/audit.py +100 -0
  109. kairo_code/agents/base.py +463 -0
  110. kairo_code/agents/coder.py +155 -0
  111. kairo_code/agents/database.py +77 -0
  112. kairo_code/agents/docs.py +88 -0
  113. kairo_code/agents/explorer.py +62 -0
  114. kairo_code/agents/guardian.py +80 -0
  115. kairo_code/agents/planner.py +66 -0
  116. kairo_code/agents/reviewer.py +91 -0
  117. kairo_code/agents/security.py +94 -0
  118. kairo_code/agents/terraform.py +88 -0
  119. kairo_code/agents/testing.py +97 -0
  120. kairo_code/agents/uiux.py +88 -0
  121. kairo_code/auth.py +232 -0
  122. kairo_code/config.py +172 -0
  123. kairo_code/conversation.py +173 -0
  124. kairo_code/heartbeat.py +63 -0
  125. kairo_code/llm.py +291 -0
  126. kairo_code/logging_config.py +156 -0
  127. kairo_code/main.py +818 -0
  128. kairo_code/router.py +217 -0
  129. kairo_code/sandbox.py +248 -0
  130. kairo_code/settings.py +183 -0
  131. kairo_code/tools/__init__.py +51 -0
  132. kairo_code/tools/analysis.py +509 -0
  133. kairo_code/tools/base.py +417 -0
  134. kairo_code/tools/code.py +58 -0
  135. kairo_code/tools/definitions.py +617 -0
  136. kairo_code/tools/files.py +315 -0
  137. kairo_code/tools/review.py +390 -0
  138. kairo_code/tools/search.py +185 -0
  139. kairo_code/ui.py +418 -0
  140. kairo_code-0.1.0.dist-info/METADATA +13 -0
  141. kairo_code-0.1.0.dist-info/RECORD +144 -0
  142. kairo_code-0.1.0.dist-info/WHEEL +5 -0
  143. kairo_code-0.1.0.dist-info/entry_points.txt +2 -0
  144. kairo_code-0.1.0.dist-info/top_level.txt +4 -0
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env python3
2
+ """Combine filtered Claude data with curated best-practices data into final training set.
3
+
4
+ Steps:
5
+ 1. Load both datasets
6
+ 2. Deduplicate by Jaccard similarity of user messages (threshold 0.85)
7
+ 3. Shuffle with seed for reproducibility
8
+ 4. Split 90/10 train/val
9
+ 5. Write output
10
+
11
+ Usage:
12
+ python kairo/tools/mix_training_data.py \
13
+ --curated kairo/data/curated_best_practices.jsonl \
14
+ --filtered kairo/data/claude_filtered.jsonl \
15
+ --output-dir kairo/data \
16
+ --val-ratio 0.1 \
17
+ --seed 42
18
+ """
19
+
20
+ import argparse
21
+ import json
22
+ import math
23
+ import os
24
+ import random
25
+ import re
26
+ import sys
27
+
28
+
29
+ def load_jsonl(path: str) -> list[dict]:
30
+ """Load a JSONL file into a list of dicts."""
31
+ examples = []
32
+ with open(path, "r", encoding="utf-8") as f:
33
+ for line in f:
34
+ line = line.strip()
35
+ if not line:
36
+ continue
37
+ try:
38
+ examples.append(json.loads(line))
39
+ except json.JSONDecodeError:
40
+ continue
41
+ return examples
42
+
43
+
44
+ def extract_user_messages(text: str) -> str:
45
+ """Extract concatenated user messages from a Llama 3.1 formatted example."""
46
+ parts = re.split(
47
+ r"<\|start_header_id\|>(user|assistant|system)<\|end_header_id\|>\n\n", text
48
+ )
49
+ user_texts = []
50
+ i = 1
51
+ while i + 1 < len(parts):
52
+ role = parts[i]
53
+ content = parts[i + 1].replace("<|eot_id|>", "").strip()
54
+ if role == "user" and content:
55
+ user_texts.append(content)
56
+ i += 2
57
+ return " ".join(user_texts)
58
+
59
+
60
+ def tokenize(text: str) -> set[str]:
61
+ """Simple word-level tokenization for Jaccard similarity."""
62
+ return set(re.findall(r"\w+", text.lower()))
63
+
64
+
65
+ def jaccard_similarity(a: set[str], b: set[str]) -> float:
66
+ """Compute Jaccard similarity between two token sets."""
67
+ if not a and not b:
68
+ return 1.0
69
+ if not a or not b:
70
+ return 0.0
71
+ return len(a & b) / len(a | b)
72
+
73
+
74
+ def deduplicate(examples: list[dict], threshold: float = 0.85) -> list[dict]:
75
+ """Remove near-duplicate examples based on Jaccard similarity of user messages.
76
+
77
+ Uses a simple O(n^2) approach which is fine for <1000 examples.
78
+ """
79
+ if not examples:
80
+ return []
81
+
82
+ # Pre-compute user message tokens for each example
83
+ tokenized = []
84
+ for ex in examples:
85
+ text = ex.get("text", "")
86
+ user_msg = extract_user_messages(text)
87
+ tokens = tokenize(user_msg)
88
+ tokenized.append(tokens)
89
+
90
+ kept_indices = []
91
+ for i in range(len(examples)):
92
+ is_dup = False
93
+ for j in kept_indices:
94
+ sim = jaccard_similarity(tokenized[i], tokenized[j])
95
+ if sim >= threshold:
96
+ is_dup = True
97
+ break
98
+ if not is_dup:
99
+ kept_indices.append(i)
100
+
101
+ return [examples[i] for i in kept_indices]
102
+
103
+
104
+ def write_jsonl(path: str, examples: list[dict]) -> None:
105
+ """Write examples to a JSONL file."""
106
+ os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
107
+ with open(path, "w", encoding="utf-8") as f:
108
+ for ex in examples:
109
+ f.write(json.dumps(ex) + "\n")
110
+
111
+
112
+ def detect_category(text: str) -> str:
113
+ """Try to detect the category of a curated example from content signals."""
114
+ text_lower = text.lower()
115
+
116
+ category_signals = [
117
+ ("security", ["sql injection", "xss", "csrf", "auth", "password", "hash",
118
+ "sanitiz", "owasp", "vulnerab", "token", "jwt", "cors"]),
119
+ ("error_handling", ["try", "except", "exception", "error handling",
120
+ "error boundar", "graceful", "retry"]),
121
+ ("api_design", ["rest", "endpoint", "status code", "pagination",
122
+ "pydantic", "fastapi", "router", "http method"]),
123
+ ("database", ["sqlalchemy", "migration", "alembic", "query", "index",
124
+ "n+1", "transaction", "connection pool"]),
125
+ ("testing", ["pytest", "test_", "mock", "fixture", "assert",
126
+ "coverage", "test client"]),
127
+ ("architecture", ["solid", "separation of concerns", "dependency injection",
128
+ "repository pattern", "service layer", "clean arch"]),
129
+ ("performance", ["cache", "async", "batch", "optimi", "profil",
130
+ "lazy load", "background task"]),
131
+ ("react_typescript", ["react", "component", "usestate", "useeffect",
132
+ "typescript", "interface", "props", "hook"]),
133
+ ("python_fastapi", ["type hint", "pydantic", "fastapi", "middleware",
134
+ "lifespan", "depends"]),
135
+ ("git_deployment", ["commit", "branch", "docker", "ci/cd", "deploy",
136
+ "dockerfile", "health check"]),
137
+ ("clean_code", ["naming", "dry", "readab", "refactor", "function length",
138
+ "single responsib", "magic number"]),
139
+ ]
140
+
141
+ for category, signals in category_signals:
142
+ matches = sum(1 for s in signals if s in text_lower)
143
+ if matches >= 2:
144
+ return category
145
+
146
+ return "other"
147
+
148
+
149
+ def main():
150
+ parser = argparse.ArgumentParser(
151
+ description="Mix filtered Claude data with curated best-practices data"
152
+ )
153
+ parser.add_argument(
154
+ "--curated",
155
+ default="kairo/data/curated_best_practices.jsonl",
156
+ help="Curated best-practices JSONL file",
157
+ )
158
+ parser.add_argument(
159
+ "--filtered",
160
+ default="kairo/data/claude_filtered.jsonl",
161
+ help="Filtered Claude training JSONL file",
162
+ )
163
+ parser.add_argument(
164
+ "--output-dir",
165
+ default="kairo/data",
166
+ help="Output directory (default: kairo/data)",
167
+ )
168
+ parser.add_argument(
169
+ "--val-ratio",
170
+ type=float,
171
+ default=0.1,
172
+ help="Validation set ratio (default: 0.1)",
173
+ )
174
+ parser.add_argument(
175
+ "--seed",
176
+ type=int,
177
+ default=42,
178
+ help="Random seed for shuffling (default: 42)",
179
+ )
180
+ parser.add_argument(
181
+ "--dedup-threshold",
182
+ type=float,
183
+ default=0.85,
184
+ help="Jaccard similarity threshold for deduplication (default: 0.85)",
185
+ )
186
+ args = parser.parse_args()
187
+
188
+ # Load datasets
189
+ curated = load_jsonl(args.curated)
190
+ filtered = load_jsonl(args.filtered)
191
+ print(f"Loaded {len(curated)} curated examples from {args.curated}")
192
+ print(f"Loaded {len(filtered)} filtered examples from {args.filtered}")
193
+
194
+ # Tag source for stats
195
+ for ex in curated:
196
+ ex["_source"] = "curated"
197
+ for ex in filtered:
198
+ ex["_source"] = "filtered"
199
+
200
+ # Combine
201
+ combined = curated + filtered
202
+ print(f"Combined: {len(combined)} examples")
203
+
204
+ # Deduplicate
205
+ deduped = deduplicate(combined, threshold=args.dedup_threshold)
206
+ removed = len(combined) - len(deduped)
207
+ print(f"After dedup (threshold={args.dedup_threshold}): {len(deduped)} ({removed} removed)")
208
+
209
+ # Shuffle
210
+ rng = random.Random(args.seed)
211
+ rng.shuffle(deduped)
212
+
213
+ # Split
214
+ val_count = max(1, math.floor(len(deduped) * args.val_ratio))
215
+ val_set = deduped[:val_count]
216
+ train_set = deduped[val_count:]
217
+
218
+ # Collect stats before stripping metadata
219
+ source_counts = {"curated": 0, "filtered": 0}
220
+ category_counts = {}
221
+ for ex in deduped:
222
+ src = ex.get("_source", "unknown")
223
+ source_counts[src] = source_counts.get(src, 0) + 1
224
+ cat = detect_category(ex.get("text", ""))
225
+ category_counts[cat] = category_counts.get(cat, 0) + 1
226
+
227
+ train_sources = {"curated": 0, "filtered": 0}
228
+ for ex in train_set:
229
+ src = ex.get("_source", "unknown")
230
+ train_sources[src] = train_sources.get(src, 0) + 1
231
+
232
+ val_sources = {"curated": 0, "filtered": 0}
233
+ for ex in val_set:
234
+ src = ex.get("_source", "unknown")
235
+ val_sources[src] = val_sources.get(src, 0) + 1
236
+
237
+ # Strip internal metadata before writing
238
+ for ex in deduped:
239
+ ex.pop("_source", None)
240
+
241
+ # Write output
242
+ train_path = os.path.join(args.output_dir, "final_train.jsonl")
243
+ val_path = os.path.join(args.output_dir, "final_val.jsonl")
244
+ stats_path = os.path.join(args.output_dir, "mixing_stats.json")
245
+
246
+ write_jsonl(train_path, train_set)
247
+ write_jsonl(val_path, val_set)
248
+
249
+ # Write stats
250
+ stats = {
251
+ "input": {
252
+ "curated": len(curated),
253
+ "filtered": len(filtered),
254
+ "combined": len(combined),
255
+ },
256
+ "deduplication": {
257
+ "threshold": args.dedup_threshold,
258
+ "removed": removed,
259
+ "after_dedup": len(deduped),
260
+ },
261
+ "split": {
262
+ "seed": args.seed,
263
+ "val_ratio": args.val_ratio,
264
+ "train_count": len(train_set),
265
+ "val_count": len(val_set),
266
+ },
267
+ "source_distribution": {
268
+ "total": source_counts,
269
+ "train": train_sources,
270
+ "val": val_sources,
271
+ },
272
+ "category_distribution": {
273
+ k: v for k, v in sorted(category_counts.items(), key=lambda x: -x[1])
274
+ },
275
+ }
276
+
277
+ os.makedirs(os.path.dirname(stats_path) or ".", exist_ok=True)
278
+ with open(stats_path, "w", encoding="utf-8") as f:
279
+ json.dump(stats, f, indent=2)
280
+
281
+ # Print summary
282
+ print(f"\nMixing complete!")
283
+ print(f" Train: {len(train_set)} examples → {train_path}")
284
+ print(f" Val: {len(val_set)} examples → {val_path}")
285
+ print(f" Stats: {stats_path}")
286
+ print(f"\nSource distribution:")
287
+ print(f" Curated: {source_counts.get('curated', 0)}")
288
+ print(f" Filtered: {source_counts.get('filtered', 0)}")
289
+ print(f"\nCategory distribution:")
290
+ for cat, count in sorted(category_counts.items(), key=lambda x: -x[1]):
291
+ print(f" {cat}: {count}")
292
+
293
+
294
+ if __name__ == "__main__":
295
+ main()
kairo_code/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Kairo Code - AI Coding Assistant by Kairon Labs"""
2
+
3
+ __version__ = "0.3.0"
@@ -0,0 +1,25 @@
1
+ """Kairo Code Agents - Specialized AI agents for different tasks"""
2
+
3
+ from .base import Agent, AgentEvent
4
+ from .explorer import ExplorerAgent
5
+ from .coder import CoderAgent
6
+ from .planner import PlannerAgent
7
+ from .security import SecurityAgent
8
+ from .reviewer import ReviewerAgent
9
+ from .audit import AuditAgent
10
+ from .testing import TestingAgent
11
+ from .uiux import UiUxAgent
12
+ from .guardian import GuardianAgent
13
+ from .docs import DocsAgent
14
+ from .architect import ArchitectAgent
15
+ from .database import DatabaseAgent
16
+ from .terraform import TerraformAgent
17
+
18
+ __all__ = [
19
+ "Agent", "AgentEvent",
20
+ "ExplorerAgent", "CoderAgent", "PlannerAgent",
21
+ "SecurityAgent", "ReviewerAgent", "AuditAgent",
22
+ "TestingAgent", "UiUxAgent", "GuardianAgent",
23
+ "DocsAgent", "ArchitectAgent", "DatabaseAgent",
24
+ "TerraformAgent",
25
+ ]
@@ -0,0 +1,98 @@
1
+ """Modular code architect agent — designs and refactors for modularity.
2
+
3
+ Mirrors the modular-code-architect Claude Code agent.
4
+ """
5
+
6
+ from .base import Agent
7
+
8
+
9
+ class ArchitectAgent(Agent):
10
+ name = "architect"
11
+ description = "Designs, builds, and refactors code with focus on modularity and scalability"
12
+ max_iterations = 15
13
+ require_confirmation = ["write_file"]
14
+
15
+ def get_system_prompt(self) -> str:
16
+ tools_desc = self.tools.format_for_prompt()
17
+
18
+ return f"""You are the Modular Code Architect, specializing in designing highly modular, maintainable, and scalable code structures. You have deep expertise in design patterns, separation of concerns, and clean architecture.
19
+
20
+ {tools_desc}
21
+
22
+ ## Absolute Rules
23
+ 1. **File Size Limits**: Never output a single file longer than 200-300 lines unless explicitly instructed
24
+ 2. **Function Size**: Keep functions under 30 lines; decompose if longer
25
+ 3. **Composition Over Inheritance**: Default to composition unless domain clearly requires inheritance
26
+ 4. **Pure Functions**: Keep functions pure unless side effects are necessary
27
+ 5. **No Monoliths**: Always decompose into smaller, reusable pieces
28
+
29
+ ## Required Workflow
30
+
31
+ ### Building New Features:
32
+ 1. Propose a modular architecture before writing code
33
+ 2. Present design philosophy
34
+ 3. Show complete file tree structure
35
+ 4. Implement each module separately
36
+ 5. Document module interactions
37
+
38
+ ### Modifying Existing Code:
39
+ 1. Analyze current structure for modularity violations
40
+ 2. Suggest modular refactors before writing new code
41
+ 3. If monolithic, propose refactoring plan
42
+ 4. Implement changes that improve or maintain modularity
43
+
44
+ ## TOOL FORMAT
45
+ <tool>tool_name</tool>
46
+ <params>{{"key": "value"}}</params>
47
+
48
+ For write_file:
49
+ <write_file path="filename.py">
50
+ code here
51
+ </write_file>
52
+
53
+ ## Output Format
54
+
55
+ ### 1. Architecture Overview
56
+ - Design philosophy chosen
57
+ - Key modules and responsibilities
58
+ - Separation of concerns rationale
59
+
60
+ ### 2. File Tree
61
+ project/
62
+ ├── module-a/
63
+ │ ├── __init__.py
64
+ │ └── helpers.py
65
+ ├── module-b/
66
+ │ └── __init__.py
67
+ └── shared/
68
+ └── types.py
69
+
70
+ ### 3. Module Implementations
71
+ Each file in separate, clearly labeled blocks.
72
+
73
+ ### 4. Module Interaction Notes
74
+ - How modules communicate
75
+ - Dependencies between modules
76
+ - Entry points and exports
77
+
78
+ ## Design Principles Priority
79
+ 1. **Maintainability**: Easy to understand and modify
80
+ 2. **Readability**: Clear naming and structure
81
+ 3. **Extensibility**: Easy to add features without modifying existing code
82
+ 4. **Testability**: Independently testable modules
83
+ 5. **Small, Focused Files**: Each file has a clear, single purpose
84
+ 6. **Clean Boundaries**: Clear interfaces, minimal coupling
85
+
86
+ ## Folder Structure
87
+ Organize by feature/domain, not by type:
88
+ features/auth/login/ over components/services/utils/
89
+
90
+ ## Self-Verification
91
+ - No file exceeds 200-300 lines
92
+ - No function exceeds 30 lines (unless justified)
93
+ - Each module has single, clear responsibility
94
+ - Dependencies minimal and explicit
95
+ - Code testable in isolation"""
96
+
97
+ def get_tool_examples(self) -> str:
98
+ return ""
@@ -0,0 +1,100 @@
1
+ """Code audit agent — comprehensive codebase health assessment.
2
+
3
+ Mirrors the code-audit Claude Code agent.
4
+ """
5
+
6
+ from .base import Agent
7
+
8
+
9
+ class AuditAgent(Agent):
10
+ name = "audit"
11
+ description = "Comprehensive codebase health, quality, and viability assessment"
12
+ max_iterations = 20
13
+ require_confirmation = []
14
+
15
+ def get_system_prompt(self) -> str:
16
+ tools_desc = self.tools.format_for_prompt()
17
+
18
+ return f"""You are a senior technical architect specializing in codebase assessment and technical debt evaluation. You have 20+ years of experience auditing codebases from startups to enterprise systems. Your assessments are thorough, objective, and actionable.
19
+
20
+ {tools_desc}
21
+
22
+ ## Your Mission
23
+
24
+ Perform comprehensive audits to provide clear, evidence-based recommendations:
25
+ - **KEEP**: Healthy enough to continue development
26
+ - **REFACTOR**: Core value exists but structure needs work
27
+ - **REWRITE**: Fundamental issues make incremental improvement impractical
28
+ - **ABANDON**: Effort to fix exceeds value delivered
29
+
30
+ ## Analysis Framework
31
+
32
+ ### 1. Code Quality (40% weight)
33
+ - Architecture & design patterns, SOLID principles, scalability
34
+ - Code complexity, cyclomatic complexity, god classes, code smells
35
+ - Naming consistency, style, organizational patterns
36
+ - Error handling robustness
37
+ - Security practices
38
+
39
+ ### 2. Maintainability (30% weight)
40
+ - Documentation: README, inline comments, API docs
41
+ - Test coverage: unit, integration, test quality
42
+ - Dependencies: outdated, vulnerable, abandoned packages
43
+ - Tech stack currency
44
+ - Build & deploy complexity
45
+
46
+ ### 3. Technical Debt (20% weight)
47
+ - Legacy code volume, deprecated patterns
48
+ - Coupling & cohesion
49
+ - Duplication (DRY violations)
50
+ - Performance concerns (N+1, memory leaks)
51
+ - Scalability limitations
52
+
53
+ ### 4. Business Value (10% weight)
54
+ - Functionality completeness
55
+ - Active development signs
56
+ - Configuration & flexibility
57
+ - Operational concerns (logging, monitoring)
58
+
59
+ ## TOOL FORMAT
60
+ <tool>tool_name</tool>
61
+ <params>{{"key": "value"}}</params>
62
+
63
+ ## Output Format
64
+
65
+ ### Executive Summary
66
+ One paragraph with final recommendation (KEEP/REFACTOR/REWRITE/ABANDON).
67
+ Overall quality score: X/100.
68
+
69
+ ### Detailed Findings
70
+ #### Strengths
71
+ 3-5 things done well with specific examples.
72
+
73
+ #### Critical Issues
74
+ Major problems with severity (HIGH/MEDIUM/LOW), specific file paths and line numbers.
75
+
76
+ #### Code Quality Metrics
77
+ - Architecture: X/10
78
+ - Maintainability: X/10
79
+ - Test Coverage: X/10
80
+ - Documentation: X/10
81
+ - Tech Stack Health: X/10
82
+
83
+ ### Recommendation
84
+ **Verdict: [KEEP/REFACTOR/REWRITE/ABANDON]**
85
+ **Reasoning**: 2-3 sentences based on evidence.
86
+ **Confidence Level**: HIGH/MEDIUM/LOW
87
+
88
+ ### Action Plan
89
+ If KEEP or REFACTOR:
90
+ - Phase 1 (Critical): ...
91
+ - Phase 2 (Important): ...
92
+ - Phase 3 (Nice to Have): ...
93
+
94
+ If REWRITE or ABANDON:
95
+ - What to salvage
96
+ - Recommended replacement approach
97
+ - Risks of rewriting vs. maintaining"""
98
+
99
+ def get_tool_examples(self) -> str:
100
+ return ""