subagent-fleet 0.0.1__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.
@@ -0,0 +1,3 @@
1
+ """subagent-fleet package."""
2
+
3
+ __version__ = "0.0.1"
subagent_fleet/cli.py ADDED
@@ -0,0 +1,331 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Annotated
6
+
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+
11
+ from subagent_fleet.config import ConfigError, FleetConfig, config_to_plain_dict, load_config
12
+ from subagent_fleet.defaults import STARTER_FLEET_YAML
13
+ from subagent_fleet.discovery import discovery_to_json, discover_fleet
14
+ from subagent_fleet.generators import generate_claude_agents, generate_env_file, generate_litellm_config
15
+ from subagent_fleet.plugins import PluginInstallError, install_plugin_marketplaces
16
+ from subagent_fleet.skills import (
17
+ ASSISTANT_TARGETS,
18
+ BUNDLED_SKILLS,
19
+ SkillInstallError,
20
+ install_skills,
21
+ )
22
+ from subagent_fleet.status import get_status, routes_to_json
23
+ from subagent_fleet.warmup import warmup_models
24
+
25
+ app = typer.Typer(help="Run Claude Code-style subagents across your local model fleet.", no_args_is_help=True)
26
+ skills_app = typer.Typer(help="Install assistant skills for using subagent-fleet.", no_args_is_help=True)
27
+ plugins_app = typer.Typer(help="Install assistant plugin marketplace bundles.", no_args_is_help=True)
28
+ app.add_typer(skills_app, name="skills")
29
+ app.add_typer(plugins_app, name="plugins")
30
+ console = Console()
31
+
32
+
33
+ def _load_or_exit(path: Path) -> FleetConfig:
34
+ try:
35
+ return load_config(path)
36
+ except ConfigError as exc:
37
+ console.print(f"[red]Invalid {path}:[/red]\n\n{exc}")
38
+ raise typer.Exit(1) from exc
39
+
40
+
41
+ @app.command()
42
+ def init(
43
+ output: Annotated[Path, typer.Option("--output", help="Path to write the starter fleet config.")] = Path("fleet.yaml"),
44
+ force: Annotated[bool, typer.Option("--force", help="Overwrite an existing config file.")] = False,
45
+ ) -> None:
46
+ """Create a starter fleet.yaml."""
47
+ if output.exists() and not force:
48
+ console.print(f"[red]{output} already exists. Use --force to overwrite.[/red]")
49
+ raise typer.Exit(1)
50
+ output.write_text(STARTER_FLEET_YAML)
51
+ console.print(f"Created {output}")
52
+ console.print("\nEdit it with your Ollama node endpoints, then run:\n")
53
+ console.print(" subagent-fleet discover")
54
+ console.print(" subagent-fleet generate")
55
+
56
+
57
+ @app.command()
58
+ def validate(config: Annotated[Path, typer.Option("--config", help="Path to fleet.yaml.")] = Path("fleet.yaml")) -> None:
59
+ """Validate fleet.yaml."""
60
+ fleet = _load_or_exit(config)
61
+ console.print(f"{config} is valid.")
62
+ for warning in fleet.alias_warnings():
63
+ console.print(f"[yellow]Warning:[/yellow] {warning}")
64
+
65
+
66
+ @app.command()
67
+ def discover(
68
+ config: Annotated[Path, typer.Option("--config", help="Path to fleet.yaml.")] = Path("fleet.yaml"),
69
+ as_json: Annotated[bool, typer.Option("--json", help="Print machine-readable JSON.")] = False,
70
+ write: Annotated[bool, typer.Option("--write", help="Write .subagent-fleet/discovery.json.")] = False,
71
+ verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Show connection errors for offline nodes.")] = False,
72
+ ) -> None:
73
+ """Discover models available on configured Ollama nodes."""
74
+ fleet = _load_or_exit(config)
75
+ results = discover_fleet(fleet)
76
+ payload = {"fleet": fleet.project.name, "nodes": discovery_to_json(results)}
77
+
78
+ if write:
79
+ discovery_path = Path(".subagent-fleet/discovery.json")
80
+ discovery_path.parent.mkdir(parents=True, exist_ok=True)
81
+ discovery_path.write_text(json.dumps(payload, indent=2) + "\n")
82
+
83
+ if as_json:
84
+ console.print(json.dumps(payload, indent=2))
85
+ return
86
+
87
+ console.print(f"Fleet: {fleet.project.name}\n")
88
+ table = Table(show_header=True, header_style="bold")
89
+ table.add_column("Node")
90
+ table.add_column("Status")
91
+ table.add_column("Models")
92
+ if verbose:
93
+ table.add_column("Error")
94
+ for result in results:
95
+ status = "[green]online[/green]" if result.online else "[red]offline[/red]"
96
+ row = [result.name, status, ", ".join(result.models) if result.models else "-"]
97
+ if verbose:
98
+ row.append(result.error or "")
99
+ table.add_row(*row)
100
+ console.print(table)
101
+ if write:
102
+ console.print(f"\nWrote {discovery_path}")
103
+
104
+
105
+ @app.command()
106
+ def generate(
107
+ config: Annotated[Path, typer.Option("--config", help="Path to fleet.yaml.")] = Path("fleet.yaml"),
108
+ out: Annotated[Path, typer.Option("--out", help="Output root.")] = Path("."),
109
+ litellm_only: Annotated[bool, typer.Option("--litellm-only", help="Only generate LiteLLM config.")] = False,
110
+ claude_only: Annotated[bool, typer.Option("--claude-only", help="Only generate Claude agent files.")] = False,
111
+ force: Annotated[bool, typer.Option("--force", help="Overwrite generated files.")] = False,
112
+ ) -> None:
113
+ """Generate LiteLLM, Claude Code agent, and environment files."""
114
+ if litellm_only and claude_only:
115
+ console.print("[red]Use at most one of --litellm-only or --claude-only.[/red]")
116
+ raise typer.Exit(1)
117
+
118
+ fleet = _load_or_exit(config)
119
+ source = str(config)
120
+ generated: list[Path] = []
121
+ try:
122
+ if not claude_only:
123
+ generated.append(generate_litellm_config(fleet, out / "litellm_config.yaml", source=source, force=force))
124
+ if not litellm_only:
125
+ generated.extend(generate_claude_agents(fleet, out / ".claude" / "agents", source=source, force=force))
126
+ if not claude_only:
127
+ generated.append(generate_env_file(fleet, out / ".env.subagent-fleet", source=source, force=force))
128
+ except FileExistsError as exc:
129
+ console.print(f"[red]{exc}[/red]")
130
+ raise typer.Exit(1) from exc
131
+
132
+ console.print("Generated:")
133
+ for path in generated:
134
+ console.print(f" {path}")
135
+ if not claude_only:
136
+ console.print("\nStart LiteLLM with:")
137
+ console.print(f" litellm --config {out / 'litellm_config.yaml'} --host {fleet.project.gateway.host} --port {fleet.project.gateway.port}")
138
+ if not litellm_only and not claude_only:
139
+ console.print("\nThen run:")
140
+ console.print(" source .env.subagent-fleet")
141
+ console.print(" claude")
142
+ for warning in fleet.alias_warnings():
143
+ console.print(f"[yellow]Warning:[/yellow] {warning}")
144
+
145
+
146
+ @app.command()
147
+ def warmup(
148
+ config: Annotated[Path, typer.Option("--config", help="Path to fleet.yaml.")] = Path("fleet.yaml"),
149
+ model: Annotated[str | None, typer.Option("--model", help="Only warm this configured model name.")] = None,
150
+ agent: Annotated[str | None, typer.Option("--agent", help="Only warm the model used by this agent.")] = None,
151
+ ) -> None:
152
+ """Preload configured Ollama models."""
153
+ fleet = _load_or_exit(config)
154
+ try:
155
+ results = warmup_models(fleet, model_name=model, agent_name=agent)
156
+ except ValueError as exc:
157
+ console.print(f"[red]{exc}[/red]")
158
+ raise typer.Exit(1) from exc
159
+
160
+ console.print("Warming models:\n")
161
+ table = Table(show_header=True, header_style="bold")
162
+ table.add_column("Model")
163
+ table.add_column("Node")
164
+ table.add_column("Ollama Model")
165
+ table.add_column("Status")
166
+ for result in results:
167
+ table.add_row(
168
+ result.model_name,
169
+ result.node_name,
170
+ result.ollama_model,
171
+ "[green]ok[/green]" if result.ok else f"[red]failed[/red] {result.error}",
172
+ )
173
+ console.print(table)
174
+ if any(not result.ok for result in results):
175
+ raise typer.Exit(1)
176
+
177
+
178
+ @app.command()
179
+ def status(
180
+ config: Annotated[Path, typer.Option("--config", help="Path to fleet.yaml.")] = Path("fleet.yaml"),
181
+ as_json: Annotated[bool, typer.Option("--json", help="Print machine-readable JSON.")] = False,
182
+ ) -> None:
183
+ """Show node status and agent routing."""
184
+ fleet = _load_or_exit(config)
185
+ nodes, routes = get_status(fleet)
186
+ if as_json:
187
+ console.print(
188
+ json.dumps(
189
+ {
190
+ "fleet": fleet.project.name,
191
+ "nodes": discovery_to_json(nodes),
192
+ "routes": routes_to_json(routes),
193
+ },
194
+ indent=2,
195
+ )
196
+ )
197
+ return
198
+
199
+ console.print(f"Fleet: {fleet.project.name}\n")
200
+ table = Table(show_header=True, header_style="bold")
201
+ table.add_column("Node")
202
+ table.add_column("Status")
203
+ table.add_column("Endpoint")
204
+ table.add_column("Models")
205
+ for node in nodes:
206
+ table.add_row(
207
+ node.name,
208
+ "[green]online[/green]" if node.online else "[red]offline[/red]",
209
+ node.endpoint,
210
+ ", ".join(node.models) if node.models else "-",
211
+ )
212
+ console.print(table)
213
+ _print_routes(routes)
214
+
215
+
216
+ @app.command()
217
+ def doctor(config: Annotated[Path, typer.Option("--config", help="Path to fleet.yaml.")] = Path("fleet.yaml")) -> None:
218
+ """Validate config and print local-network security guidance."""
219
+ fleet = _load_or_exit(config)
220
+ console.print(f"{config} is valid.")
221
+ console.print("\nSecurity checks:")
222
+ console.print("- Do not expose Ollama directly to the public internet.")
223
+ console.print("- Do not expose LiteLLM without authentication.")
224
+ console.print("- Prefer LAN, firewall rules, Tailscale, or WireGuard.")
225
+ console.print(f"- Use a non-default {fleet.project.gateway.master_key_env} beyond local dev.")
226
+ console.print("\nOllama worker setup hint:")
227
+ console.print(' launchctl setenv OLLAMA_HOST "0.0.0.0:11434"')
228
+ console.print(' launchctl setenv OLLAMA_KEEP_ALIVE "-1"')
229
+ console.print(' launchctl setenv OLLAMA_NUM_PARALLEL "1"')
230
+ console.print(' launchctl setenv OLLAMA_MAX_LOADED_MODELS "1"')
231
+
232
+
233
+ @app.command()
234
+ def clean(out: Annotated[Path, typer.Option("--out", help="Output root to clean.")] = Path("."), force: bool = False) -> None:
235
+ """Remove generated files from an output root."""
236
+ targets = [out / "litellm_config.yaml", out / ".env.subagent-fleet"]
237
+ targets.extend((out / ".claude" / "agents").glob("*.md") if (out / ".claude" / "agents").exists() else [])
238
+ if not force:
239
+ console.print("Files that would be removed:")
240
+ for target in targets:
241
+ console.print(f" {target}")
242
+ console.print("\nRun with --force to remove them.")
243
+ return
244
+ for target in targets:
245
+ target.unlink(missing_ok=True)
246
+ console.print("Removed generated files.")
247
+
248
+
249
+ @skills_app.command("list")
250
+ def list_skills() -> None:
251
+ """List bundled skills and supported assistant targets."""
252
+ skill_table = Table(title="Bundled skills", show_header=True, header_style="bold")
253
+ skill_table.add_column("Skill")
254
+ skill_table.add_column("Description")
255
+ for skill in BUNDLED_SKILLS.values():
256
+ skill_table.add_row(skill.name, skill.description)
257
+ console.print(skill_table)
258
+
259
+ target_table = Table(title="Targets", show_header=True, header_style="bold")
260
+ target_table.add_column("Target")
261
+ target_table.add_column("Install Directory")
262
+ target_table.add_column("Description")
263
+ for target in ASSISTANT_TARGETS.values():
264
+ target_table.add_row(target.name, str(target.directory), target.description)
265
+ console.print(target_table)
266
+
267
+
268
+ @skills_app.command("install")
269
+ def install_assistant_skills(
270
+ out: Annotated[Path, typer.Option("--out", help="Project root or output directory.")] = Path("."),
271
+ target: Annotated[
272
+ list[str] | None,
273
+ typer.Option("--target", "-t", help="Assistant target: all, claude-code, codex, opencode. Can be repeated or comma-separated."),
274
+ ] = None,
275
+ skill: Annotated[
276
+ list[str] | None,
277
+ typer.Option("--skill", "-s", help="Bundled skill: all, subagent-fleet-setup, subagent-fleet-operations. Can be repeated or comma-separated."),
278
+ ] = None,
279
+ force: Annotated[bool, typer.Option("--force", help="Overwrite existing installed skill files.")] = False,
280
+ ) -> None:
281
+ """Install bundled assistant skills for Claude Code, Codex, OpenCode, or all targets."""
282
+ try:
283
+ results = install_skills(output_root=out, targets=target or ["all"], skills=skill or ["all"], force=force)
284
+ except (FileExistsError, SkillInstallError) as exc:
285
+ console.print(f"[red]{exc}[/red]")
286
+ raise typer.Exit(1) from exc
287
+
288
+ console.print("Installed skills:")
289
+ for result in results:
290
+ console.print(f" {result.target}: {result.skill} -> {result.path}")
291
+
292
+
293
+ @plugins_app.command("install")
294
+ def install_assistant_plugins(
295
+ out: Annotated[Path, typer.Option("--out", help="Marketplace root or output directory.")] = Path("."),
296
+ target: Annotated[
297
+ list[str] | None,
298
+ typer.Option("--target", "-t", help="Plugin target: all, claude-code, codex. Can be repeated or comma-separated."),
299
+ ] = None,
300
+ force: Annotated[bool, typer.Option("--force", help="Overwrite existing plugin marketplace files.")] = False,
301
+ ) -> None:
302
+ """Install Claude Code and Codex plugin marketplace bundles."""
303
+ try:
304
+ results = install_plugin_marketplaces(output_root=out, targets=target or ["all"], force=force)
305
+ except (FileExistsError, PluginInstallError) as exc:
306
+ console.print(f"[red]{exc}[/red]")
307
+ raise typer.Exit(1) from exc
308
+
309
+ console.print("Installed plugin marketplace files:")
310
+ for result in results:
311
+ console.print(f" {result.target}: {result.path}")
312
+ console.print("\nPlugin skills included:")
313
+ console.print(" subagent-fleet-bootstrap")
314
+ console.print(" subagent-fleet-setup")
315
+ console.print(" subagent-fleet-operations")
316
+
317
+
318
+ def _print_routes(routes: list[object]) -> None:
319
+ console.print("\nAgent routing:\n")
320
+ table = Table(show_header=True, header_style="bold")
321
+ table.add_column("Agent")
322
+ table.add_column("Node")
323
+ table.add_column("Ollama Model")
324
+ table.add_column("LiteLLM Alias")
325
+ for route in routes:
326
+ table.add_row(route.agent, route.node, route.ollama_model, route.litellm_alias)
327
+ console.print(table)
328
+
329
+
330
+ if __name__ == "__main__":
331
+ app()
@@ -0,0 +1,163 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import yaml
8
+ from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, ValidationError, field_validator, model_validator
9
+
10
+ AGENT_NAME_RE = re.compile(r"^[a-z0-9_-]+$")
11
+ DEFAULT_AGENT_PROMPT = "You are a local subagent. Follow the agent description and return a concise, useful response."
12
+
13
+
14
+ class ConfigError(ValueError):
15
+ """Raised when fleet.yaml cannot be loaded or validated."""
16
+
17
+
18
+ class UniqueKeyLoader(yaml.SafeLoader):
19
+ """YAML loader that rejects duplicate mapping keys."""
20
+
21
+
22
+ def _construct_mapping(loader: UniqueKeyLoader, node: yaml.MappingNode, deep: bool = False) -> dict[Any, Any]:
23
+ mapping: dict[Any, Any] = {}
24
+ for key_node, value_node in node.value:
25
+ key = loader.construct_object(key_node, deep=deep)
26
+ if key in mapping:
27
+ raise yaml.constructor.ConstructorError(
28
+ "while constructing a mapping",
29
+ node.start_mark,
30
+ f"found duplicate key: {key}",
31
+ key_node.start_mark,
32
+ )
33
+ mapping[key] = loader.construct_object(value_node, deep=deep)
34
+ return mapping
35
+
36
+
37
+ UniqueKeyLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _construct_mapping)
38
+
39
+
40
+ class GatewayConfig(BaseModel):
41
+ model_config = ConfigDict(extra="forbid")
42
+
43
+ provider: str = "litellm"
44
+ host: str = "127.0.0.1"
45
+ port: int = Field(default=4000, ge=1, le=65535)
46
+ master_key_env: str = "LITELLM_MASTER_KEY"
47
+
48
+
49
+ class ProjectConfig(BaseModel):
50
+ model_config = ConfigDict(extra="forbid")
51
+
52
+ name: str
53
+ gateway: GatewayConfig = Field(default_factory=GatewayConfig)
54
+
55
+
56
+ class NodeConfig(BaseModel):
57
+ model_config = ConfigDict(extra="forbid")
58
+
59
+ endpoint: AnyHttpUrl
60
+ tags: list[str] = Field(default_factory=list)
61
+
62
+ @property
63
+ def endpoint_str(self) -> str:
64
+ return str(self.endpoint).rstrip("/")
65
+
66
+
67
+ class ModelConfig(BaseModel):
68
+ model_config = ConfigDict(extra="forbid")
69
+
70
+ node: str
71
+ ollama_model: str
72
+ litellm_alias: str
73
+ context: int = Field(default=8192, gt=0)
74
+ timeout: int = Field(default=300, gt=0)
75
+ max_parallel: int = Field(default=1, gt=0)
76
+
77
+
78
+ class AgentConfig(BaseModel):
79
+ model_config = ConfigDict(extra="forbid")
80
+
81
+ model: str
82
+ description: str
83
+ tools: list[str] = Field(default_factory=list)
84
+ prompt: str | None = None
85
+
86
+ @field_validator("prompt")
87
+ @classmethod
88
+ def default_prompt(cls, value: str | None) -> str:
89
+ if value is None or not value.strip():
90
+ return DEFAULT_AGENT_PROMPT
91
+ return value
92
+
93
+
94
+ class FleetConfig(BaseModel):
95
+ model_config = ConfigDict(extra="forbid")
96
+
97
+ project: ProjectConfig
98
+ nodes: dict[str, NodeConfig]
99
+ models: dict[str, ModelConfig]
100
+ agents: dict[str, AgentConfig]
101
+
102
+ @model_validator(mode="after")
103
+ def validate_references(self) -> "FleetConfig":
104
+ for node_name in self.nodes:
105
+ if not node_name:
106
+ raise ValueError("node names must not be empty")
107
+
108
+ for model_name, model in self.models.items():
109
+ if model.node not in self.nodes:
110
+ raise ValueError(f"models.{model_name}.node references unknown node: {model.node}")
111
+
112
+ for agent_name, agent in self.agents.items():
113
+ if not AGENT_NAME_RE.fullmatch(agent_name):
114
+ raise ValueError(
115
+ f"agents.{agent_name} must be filesystem-safe: lowercase letters, numbers, hyphens, underscores"
116
+ )
117
+ if agent.model not in self.models:
118
+ raise ValueError(f"agents.{agent_name}.model references unknown model: {agent.model}")
119
+
120
+ return self
121
+
122
+ def alias_warnings(self) -> list[str]:
123
+ aliases: dict[str, set[str]] = {}
124
+ for model in self.models.values():
125
+ aliases.setdefault(model.litellm_alias, set()).add(model.ollama_model)
126
+ return [
127
+ f"alias {alias!r} is used for multiple Ollama models: {', '.join(sorted(models))}"
128
+ for alias, models in sorted(aliases.items())
129
+ if len(models) > 1
130
+ ]
131
+
132
+
133
+ def load_config(path: Path | str) -> FleetConfig:
134
+ config_path = Path(path)
135
+ try:
136
+ raw = yaml.load(config_path.read_text(), Loader=UniqueKeyLoader) or {}
137
+ except FileNotFoundError as exc:
138
+ raise ConfigError(f"{config_path} does not exist") from exc
139
+ except yaml.YAMLError as exc:
140
+ raise ConfigError(f"{config_path} is not valid YAML: {exc}") from exc
141
+
142
+ if not isinstance(raw, dict):
143
+ raise ConfigError(f"{config_path} must contain a YAML mapping")
144
+
145
+ try:
146
+ return FleetConfig.model_validate(raw)
147
+ except ValidationError as exc:
148
+ raise ConfigError(format_validation_error(exc)) from exc
149
+ except ValueError as exc:
150
+ raise ConfigError(str(exc)) from exc
151
+
152
+
153
+ def format_validation_error(exc: ValidationError) -> str:
154
+ errors: list[str] = []
155
+ for error in exc.errors():
156
+ loc = ".".join(str(part) for part in error["loc"])
157
+ msg = error["msg"]
158
+ errors.append(f"{loc}: {msg}" if loc else msg)
159
+ return "\n".join(errors)
160
+
161
+
162
+ def config_to_plain_dict(config: FleetConfig) -> dict[str, Any]:
163
+ return config.model_dump(mode="json")
@@ -0,0 +1,82 @@
1
+ STARTER_FLEET_YAML = """project:
2
+ name: local-dev
3
+ gateway:
4
+ provider: litellm
5
+ host: 127.0.0.1
6
+ port: 4000
7
+ master_key_env: LITELLM_MASTER_KEY
8
+
9
+ nodes:
10
+ local-ollama:
11
+ endpoint: http://localhost:11434
12
+ tags:
13
+ - controller
14
+ - local
15
+ - fast
16
+
17
+ models:
18
+ heavy-coder:
19
+ node: local-ollama
20
+ ollama_model: qwen2.5-coder:32b
21
+ litellm_alias: claude-sonnet-local
22
+ context: 32768
23
+ timeout: 600
24
+ max_parallel: 1
25
+
26
+ small-coder:
27
+ node: local-ollama
28
+ ollama_model: qwen2.5-coder:7b
29
+ litellm_alias: claude-haiku-local
30
+ context: 8192
31
+ timeout: 300
32
+ max_parallel: 1
33
+
34
+ agents:
35
+ planner:
36
+ model: small-coder
37
+ description: Use for planning, file discovery, task decomposition, and summarization.
38
+ tools:
39
+ - Read
40
+ - Grep
41
+ - Glob
42
+ prompt: |
43
+ You are a fast local planning agent.
44
+ Do not edit files.
45
+ Return a concise response with:
46
+ - plan
47
+ - relevant files
48
+ - risks
49
+ - next recommended agent
50
+
51
+ implementer:
52
+ model: heavy-coder
53
+ description: Use for implementation, bug fixes, refactors, and patch creation.
54
+ tools:
55
+ - Read
56
+ - Grep
57
+ - Glob
58
+ - Edit
59
+ - MultiEdit
60
+ - Bash
61
+ prompt: |
62
+ You are a senior implementation agent.
63
+ Make minimal, correct changes.
64
+ Prefer small patches.
65
+ Run relevant checks when possible.
66
+ Explain what changed and why.
67
+
68
+ reviewer:
69
+ model: heavy-coder
70
+ description: Use after implementation to review diffs, tests, regressions, and maintainability.
71
+ tools:
72
+ - Read
73
+ - Grep
74
+ - Glob
75
+ - Bash
76
+ prompt: |
77
+ You are a strict code reviewer.
78
+ Focus on correctness, regressions, missing tests, security issues,
79
+ over-engineering, and maintainability.
80
+ Review the diff and test output.
81
+ Return only actionable issues.
82
+ """