Skip to content

Commit 3ee6e4d

Browse files
committed
fix: re-run pre-commit
1 parent 10a2080 commit 3ee6e4d

10 files changed

Lines changed: 113 additions & 88 deletions

File tree

src/agents/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
from src.agents.acknowledgment_agent import AcknowledgmentAgent
1010
from src.agents.base import AgentResult, BaseAgent
1111
from src.agents.engine_agent import RuleEngineAgent
12+
from src.agents.extractor_agent import RuleExtractorAgent
1213
from src.agents.factory import get_agent
1314
from src.agents.feasibility_agent import RuleFeasibilityAgent
14-
from src.agents.extractor_agent import RuleExtractorAgent
1515
from src.agents.repository_analysis_agent import RepositoryAnalysisAgent
1616

1717
__all__ = [

src/agents/extractor_agent/agent.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,25 @@ def _build_graph(self):
7272
async def extract_node(state: ExtractorState) -> dict:
7373
raw = (state.markdown_content or "").strip()
7474
if not raw:
75-
return {"statements": [], "decision": "none", "confidence": 0.0, "reasoning": "Empty input", "recommendations": [], "strategy_used": ""}
75+
return {
76+
"statements": [],
77+
"decision": "none",
78+
"confidence": 0.0,
79+
"reasoning": "Empty input",
80+
"recommendations": [],
81+
"strategy_used": "",
82+
}
7683
# Centralized sanitization (see execute(): defense-in-depth with redact_and_cap at entry).
7784
content = redact_and_cap(raw)
7885
if not content:
79-
return {"statements": [], "decision": "none", "confidence": 0.0, "reasoning": "Empty after sanitization", "recommendations": [], "strategy_used": ""}
86+
return {
87+
"statements": [],
88+
"decision": "none",
89+
"confidence": 0.0,
90+
"reasoning": "Empty after sanitization",
91+
"recommendations": [],
92+
"strategy_used": "",
93+
}
8094
prompt = EXTRACTOR_PROMPT.format(markdown_content=content)
8195
structured_llm = self.llm.with_structured_output(ExtractorOutput)
8296
result = await structured_llm.ainvoke(prompt)
@@ -198,7 +212,11 @@ async def execute(self, **kwargs: Any) -> AgentResult:
198212
"recommendations": [],
199213
"strategy_used": "",
200214
},
201-
metadata={"execution_time_ms": execution_time * 1000, "error_type": "timeout", "routing": "human_review"},
215+
metadata={
216+
"execution_time_ms": execution_time * 1000,
217+
"error_type": "timeout",
218+
"routing": "human_review",
219+
},
202220
)
203221
except APIConnectionError as e:
204222
execution_time = time.time() - start_time
@@ -238,5 +256,9 @@ async def execute(self, **kwargs: Any) -> AgentResult:
238256
"recommendations": [],
239257
"strategy_used": "",
240258
},
241-
metadata={"execution_time_ms": execution_time * 1000, "error_type": type(e).__name__, "routing": "human_review"},
259+
metadata={
260+
"execution_time_ms": execution_time * 1000,
261+
"error_type": type(e).__name__,
262+
"routing": "human_review",
263+
},
242264
)

src/agents/factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from src.agents.acknowledgment_agent import AcknowledgmentAgent
1212
from src.agents.base import BaseAgent
1313
from src.agents.engine_agent import RuleEngineAgent
14-
from src.agents.feasibility_agent import RuleFeasibilityAgent
1514
from src.agents.extractor_agent import RuleExtractorAgent
15+
from src.agents.feasibility_agent import RuleFeasibilityAgent
1616
from src.agents.repository_analysis_agent import RepositoryAnalysisAgent
1717

1818
logger = logging.getLogger(__name__)

src/api/recommendations.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Any, TypedDict
33

44
import structlog
5+
import yaml
56
from fastapi import APIRouter, Depends, HTTPException, Request, status
67
from giturlparse import parse # type: ignore
78
from pydantic import BaseModel, Field, HttpUrl
@@ -13,12 +14,10 @@
1314
# Internal: User model, auth assumed present—see core/api for details.
1415
from src.core.models import User
1516
from src.integrations.github.api import github_client
16-
1717
from src.rules.ai_rules_scan import (
1818
scan_repo_for_ai_rule_files,
1919
translate_ai_rule_files_to_yaml,
2020
)
21-
import yaml
2221

2322
logger = structlog.get_logger()
2423

@@ -141,6 +140,7 @@ class MetricConfig(TypedDict):
141140
thresholds: dict[str, float]
142141
explanation: Callable[[float | int], str]
143142

143+
144144
class ScanAIFilesRequest(BaseModel):
145145
"""
146146
Payload for scanning a repo for AI assistant rule files (Cursor, Claude, Copilot, etc.).
@@ -178,6 +178,7 @@ class ScanAIFilesResponse(BaseModel):
178178
)
179179
warnings: list[str] = Field(default_factory=list, description="Warnings (e.g. rate limit, partial results)")
180180

181+
181182
class TranslateAIFilesRequest(BaseModel):
182183
"""Request for translating AI rule files into .watchflow rules YAML."""
183184

@@ -205,7 +206,6 @@ class TranslateAIFilesResponse(BaseModel):
205206
warnings: list[str] = Field(default_factory=list)
206207

207208

208-
209209
def _get_severity_label(value: float, thresholds: dict[str, float]) -> tuple[str, str]:
210210
"""
211211
Determine severity label and color based on value and thresholds.
@@ -966,6 +966,7 @@ async def proceed_with_pr(
966966
detail="Failed to create pull request. Please try again.",
967967
) from e
968968

969+
969970
@router.post(
970971
"/scan-ai-files",
971972
response_model=ScanAIFilesResponse,
@@ -981,7 +982,7 @@ async def scan_ai_rule_files(
981982
request: Request,
982983
payload: ScanAIFilesRequest,
983984
user: User | None = Depends(get_current_user_optional),
984-
) -> ScanAIFilesResponse:
985+
) -> ScanAIFilesResponse:
985986
"""
986987
Scan a repository for AI assistant rule files (Cursor, Claude, Copilot, etc.).
987988
@@ -1007,9 +1008,7 @@ async def scan_ai_rule_files(
10071008
repo_full_name = parse_repo_from_url(repo_url_str)
10081009
except ValueError as e:
10091010
logger.warning("invalid_url_provided", url=repo_url_str, error=str(e))
1010-
raise HTTPException(
1011-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)
1012-
) from e
1011+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)) from e
10131012

10141013
# Resolve token (same as recommend_rules)
10151014
github_token = None
@@ -1057,9 +1056,7 @@ async def scan_ai_rule_files(
10571056

10581057
# Optional content fetcher for keyword scan (and optionally include in response)
10591058
async def get_content(path: str):
1060-
return await github_client.get_file_content(
1061-
repo_full_name, path, installation_id, github_token
1062-
)
1059+
return await github_client.get_file_content(repo_full_name, path, installation_id, github_token)
10631060

10641061
# Always fetch content so has_keywords is set; strip content in response unless include_content
10651062
raw_candidates = await scan_repo_for_ai_rule_files(
@@ -1084,6 +1081,7 @@ async def get_content(path: str):
10841081
warnings=[],
10851082
)
10861083

1084+
10871085
@router.post(
10881086
"/translate-ai-files",
10891087
response_model=TranslateAIFilesResponse,
@@ -1166,9 +1164,7 @@ async def translate_ai_rule_files(
11661164
async def get_content(path: str):
11671165
return await github_client.get_file_content(repo_full_name, path, installation_id, github_token)
11681166

1169-
raw_candidates = await scan_repo_for_ai_rule_files(
1170-
tree_entries, fetch_content=True, get_file_content=get_content
1171-
)
1167+
raw_candidates = await scan_repo_for_ai_rule_files(tree_entries, fetch_content=True, get_file_content=get_content)
11721168
candidates_with_content = [c for c in raw_candidates if c.get("content")]
11731169
if not candidates_with_content:
11741170
return TranslateAIFilesResponse(
@@ -1197,12 +1193,7 @@ async def get_content(path: str):
11971193
reason = item.get("reason", "") if isinstance(item, dict) else ""
11981194
if not isinstance(reason, str):
11991195
reason = str(reason)
1200-
if (
1201-
len(reason) > 200
1202-
or "Error" in reason
1203-
or "Exception" in reason
1204-
or "Traceback" in reason
1205-
):
1196+
if len(reason) > 200 or "Error" in reason or "Exception" in reason or "Traceback" in reason:
12061197
logger.debug(
12071198
"translate_ai_rule_files_ambiguous_reason_redacted",
12081199
repo_full_name=repo_full_name,
@@ -1226,4 +1217,4 @@ async def get_content(path: str):
12261217
rules_count=rules_count,
12271218
ambiguous=safe_ambiguous,
12281219
warnings=[],
1229-
)
1220+
)

src/event_processors/pull_request/processor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async def process(self, task: Task) -> ProcessingResult:
110110
)
111111
if rules_count > 0:
112112
suggested_rules_yaml = rules_yaml
113-
except Exception as e:
113+
except Exception:
114114
latency_ms = int((time.time() - scan_start) * 1000)
115115
logger.exception(
116116
"Suggested rules scan failed",

src/event_processors/push.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from src.rules.ai_rules_scan import is_relevant_push
1313
from src.tasks.task_queue import Task
1414

15-
1615
logger = logging.getLogger(__name__)
1716

1817

@@ -294,9 +293,7 @@ async def _create_pr_with_suggested_rules(
294293
base_ref = (pr.get("base") or {}).get("ref") or ""
295294
head_ref = (pr.get("head") or {}).get("ref") or ""
296295
title = pr.get("title") or ""
297-
if base_ref == default_branch and (
298-
head_ref.startswith(branch_prefix) or title == pr_title
299-
):
296+
if base_ref == default_branch and (head_ref.startswith(branch_prefix) or title == pr_title):
300297
existing_pr = pr
301298
break
302299
if existing_pr:

src/integrations/github/api.py

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,7 @@ async def get_repository(
135135
Fetch repository metadata. Returns (repo_data, None) on success;
136136
(None, {"status": int, "message": str}) on failure for meaningful API responses.
137137
"""
138-
headers = await self._get_auth_headers(
139-
installation_id=installation_id, user_token=user_token
140-
) or {}
138+
headers = await self._get_auth_headers(installation_id=installation_id, user_token=user_token) or {}
141139
url = f"{config.github.api_base_url}/repos/{repo_full_name}"
142140
session = await self._get_session()
143141
async with session.get(url, headers=headers) as response:
@@ -160,17 +158,18 @@ async def get_repository(
160158
if response.status == 401:
161159
return (
162160
None,
163-
{"status": 401, "message": gh_message or "Invalid or expired token. Check github_token or installation_id."},
161+
{
162+
"status": 401,
163+
"message": gh_message or "Invalid or expired token. Check github_token or installation_id.",
164+
},
164165
)
165166
return None, {"status": response.status, "message": gh_message or f"GitHub API returned {response.status}."}
166167

167168
async def list_directory_any_auth(
168169
self, repo_full_name: str, path: str, installation_id: int | None = None, user_token: str | None = None
169170
) -> list[dict[str, Any]]:
170171
"""List directory contents using installation or user token (auth required)."""
171-
headers = await self._get_auth_headers(
172-
installation_id=installation_id, user_token=user_token
173-
) or {}
172+
headers = await self._get_auth_headers(installation_id=installation_id, user_token=user_token) or {}
174173
url = f"{config.github.api_base_url}/repos/{repo_full_name}/contents/{path}"
175174
session = await self._get_session()
176175
async with session.get(url, headers=headers) as response:
@@ -183,29 +182,36 @@ async def list_directory_any_auth(
183182
response.raise_for_status()
184183
return []
185184

186-
187185
async def get_repository_tree(
188-
self,
189-
repo_full_name: str,
190-
ref: str | None = None,
191-
installation_id: int | None = None,
192-
user_token: str | None = None,
193-
recursive: bool = True,
186+
self,
187+
repo_full_name: str,
188+
ref: str | None = None,
189+
installation_id: int | None = None,
190+
user_token: str | None = None,
191+
recursive: bool = True,
194192
) -> list[dict[str, Any]]:
195193
"""Get the tree of a repository. Requires authentication (github_token or installation_id)."""
196194
start = time.monotonic()
197-
headers = await self._get_auth_headers(
198-
installation_id=installation_id,
199-
user_token=user_token,
200-
) or {}
195+
headers = (
196+
await self._get_auth_headers(
197+
installation_id=installation_id,
198+
user_token=user_token,
199+
)
200+
or {}
201+
)
201202
ref = ref or "main"
202203
tree_sha = await self._resolve_tree_sha(repo_full_name, ref, headers)
203204
if not tree_sha:
204205
latency_ms = int((time.monotonic() - start) * 1000)
205206
logger.info(
206207
"get_repository_tree",
207208
operation="get_repository_tree",
208-
subject_ids={"repo": repo_full_name, "installation_id": installation_id, "user_token_present": bool(user_token), "ref": ref},
209+
subject_ids={
210+
"repo": repo_full_name,
211+
"installation_id": installation_id,
212+
"user_token_present": bool(user_token),
213+
"ref": ref,
214+
},
209215
decision="ref_resolution_failed",
210216
latency_ms=latency_ms,
211217
)
@@ -228,7 +234,6 @@ async def get_repository_tree(
228234
data = await response.json()
229235
return cast("list[dict[str, Any]]", data.get("tree", []))
230236

231-
232237
async def _resolve_tree_sha(self, repo_full_name: str, ref: str, headers: dict[str, str]) -> str | None:
233238
"""Resolve the tree SHA for the given ref (branch, tag, or commit SHA) via the commits API."""
234239
session = await self._get_session()
@@ -254,11 +259,14 @@ async def get_file_content(
254259
Fetches the content of a file from a repository. Requires authentication (github_token or installation_id).
255260
When ref is provided (branch name, tag, or commit SHA), returns content at that ref; otherwise uses default branch.
256261
"""
257-
headers = await self._get_auth_headers(
258-
installation_id=installation_id,
259-
user_token=user_token,
260-
accept="application/vnd.github.raw",
261-
) or {}
262+
headers = (
263+
await self._get_auth_headers(
264+
installation_id=installation_id,
265+
user_token=user_token,
266+
accept="application/vnd.github.raw",
267+
)
268+
or {}
269+
)
262270
url = f"{config.github.api_base_url}/repos/{repo_full_name}/contents/{file_path}"
263271
params = {"ref": ref} if ref else None
264272

@@ -1350,9 +1358,7 @@ async def execute_graphql(
13501358
payload = {"query": query, "variables": variables}
13511359

13521360
# Get appropriate headers (auth required: user_token or installation_id)
1353-
headers = await self._get_auth_headers(
1354-
user_token=user_token, installation_id=installation_id
1355-
)
1361+
headers = await self._get_auth_headers(user_token=user_token, installation_id=installation_id)
13561362
if not headers:
13571363
# Fallback or error? GraphQL usually demands auth.
13581364
# If we have no headers, we likely can't query GraphQL successfully for many fields.

0 commit comments

Comments
 (0)