sourcecode 1.33.5__py3-none-any.whl → 1.33.7__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.
sourcecode/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.33.5"
3
+ __version__ = "1.33.7"
@@ -0,0 +1,708 @@
1
+ """Agent Runtime Layer — session state machine, intent detection, workflow orchestrators.
2
+
3
+ Converts the MCP from a flat tool collection into a guided agent operating system:
4
+ - start_session: single entry point that determines state and tool sequence
5
+ - analyze_task: intent detection and targeted tool sequence recommendation
6
+ - run_pr_review_flow: auto-chains delta + review_pr + blast radius
7
+ - run_bug_investigation_flow: auto-chains fix_bug + impact + IR context
8
+ - run_feature_flow: auto-chains context + endpoints + delta + structural awareness
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ import time
14
+ from pathlib import Path
15
+ from typing import Any, Optional
16
+
17
+ from sourcecode.mcp.runner import run_command
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # Session state constants
21
+ # ---------------------------------------------------------------------------
22
+
23
+ SESSION_INIT = "INIT" # no RIS, no context
24
+ SESSION_CONTEXT_LOADED = "CONTEXT_LOADED" # RIS fresh + complete
25
+ SESSION_STALE_CONTEXT = "STALE_CONTEXT" # RIS exists but HEAD changed
26
+ SESSION_INCOMPLETE_CONTEXT = "INCOMPLETE_CONTEXT" # RIS missing critical sections
27
+ SESSION_TASK_INTENT_DETECTED = "TASK_INTENT_DETECTED"
28
+ SESSION_READY_FOR_REVIEW = "READY_FOR_REVIEW" # flow complete
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # Intent constants
32
+ # ---------------------------------------------------------------------------
33
+
34
+ INTENT_PR_REVIEW = "pr_review"
35
+ INTENT_BUG_INVESTIGATION = "bug_investigation"
36
+ INTENT_FEATURE_IMPLEMENTATION = "feature_implementation"
37
+ INTENT_REFACTOR = "refactor"
38
+ INTENT_TEST_GENERATION = "test_generation"
39
+ INTENT_ORIENTATION = "orientation"
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Workflow sequences: intent → ordered tool names the agent should call
43
+ # ---------------------------------------------------------------------------
44
+
45
+ WORKFLOW_SEQUENCES: dict[str, list[str]] = {
46
+ INTENT_PR_REVIEW: ["get_delta", "review_pr_context", "get_impact_context"],
47
+ INTENT_BUG_INVESTIGATION: ["fix_bug_context", "get_impact_context"],
48
+ INTENT_FEATURE_IMPLEMENTATION: ["get_compact_context", "get_endpoints", "get_delta"],
49
+ INTENT_REFACTOR: ["get_agent_context", "modernize_context", "get_ir_summary"],
50
+ INTENT_TEST_GENERATION: ["generate_tests_context"],
51
+ INTENT_ORIENTATION: ["get_compact_context"],
52
+ }
53
+
54
+ WORKFLOW_DESCRIPTIONS: dict[str, str] = {
55
+ INTENT_PR_REVIEW: "PR review: delta → execution paths → blast radius of changed classes",
56
+ INTENT_BUG_INVESTIGATION: "Bug investigation: risk-ranked files → impact of suspect class",
57
+ INTENT_FEATURE_IMPLEMENTATION: "Feature implementation: context → API surface → recent changes",
58
+ INTENT_REFACTOR: "Refactor: deep context → modernization opportunities → IR coupling",
59
+ INTENT_TEST_GENERATION: "Test generation: untested files ranked by risk",
60
+ INTENT_ORIENTATION: "Orientation: compact context overview",
61
+ }
62
+
63
+ FLOW_RUNNERS: dict[str, str] = {
64
+ INTENT_PR_REVIEW: "run_pr_review_flow",
65
+ INTENT_BUG_INVESTIGATION: "run_bug_investigation_flow",
66
+ INTENT_FEATURE_IMPLEMENTATION: "run_feature_flow",
67
+ INTENT_REFACTOR: "run_feature_flow",
68
+ INTENT_TEST_GENERATION: "generate_tests_context",
69
+ INTENT_ORIENTATION: "get_compact_context",
70
+ }
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Intent detection
74
+ # ---------------------------------------------------------------------------
75
+
76
+ _INTENT_PATTERNS: list[tuple[str, list[str]]] = [
77
+ (INTENT_PR_REVIEW, [
78
+ r"\bpr\b", r"pull request", r"review pr", r"\bdiff\b", r"merge request",
79
+ r"code review", r"changes in branch", r"review.*branch", r"branch.*review",
80
+ ]),
81
+ (INTENT_BUG_INVESTIGATION, [
82
+ r"\bbug\b", r"\berror\b", r"\bexception\b", r"\bcrash\b", r"\bnpe\b",
83
+ r"\bfix\b", r"\bbroken\b", r"\bfail(s|ing)?\b", r"stack.?trace",
84
+ r"null.?pointer", r"wrong.?behav", r"\bincident\b", r"not.?work",
85
+ ]),
86
+ (INTENT_FEATURE_IMPLEMENTATION, [
87
+ r"\bfeature\b", r"\bimplement\b", r"\bdevelop\b", r"new endpoint",
88
+ r"new service", r"add.*(endpoint|service|api|feature)", r"create.*class",
89
+ r"\bbuild\b.*new",
90
+ ]),
91
+ (INTENT_REFACTOR, [
92
+ r"\brefactor\b", r"\bmodernize\b", r"clean.?up", r"technical.?debt",
93
+ r"\bdebt\b", r"\brewrite\b", r"\brestructure\b", r"\bclean.*code\b",
94
+ ]),
95
+ (INTENT_TEST_GENERATION, [
96
+ r"\btest(s|ing)?\b", r"\bcoverage\b", r"unit.?test", r"\bspec\b",
97
+ r"write.?test", r"add.?test",
98
+ ]),
99
+ ]
100
+
101
+
102
+ def detect_intent(task_description: str) -> tuple[str, float]:
103
+ """Return (intent, confidence). Confidence 1.0 = explicit match, 0.5 = fallback."""
104
+ t = task_description.lower()
105
+ for intent, patterns in _INTENT_PATTERNS:
106
+ for pat in patterns:
107
+ if re.search(pat, t):
108
+ return intent, 1.0
109
+ return INTENT_ORIENTATION, 0.5
110
+
111
+
112
+ def _extract_symptom(task_description: str) -> str:
113
+ """Heuristic: extract error class or quoted string from task description."""
114
+ # Quoted string
115
+ m = re.search(r'"([^"]+)"', task_description)
116
+ if m:
117
+ return m.group(1)
118
+ # Exception/Error class name
119
+ m = re.search(r'\b(\w+(?:Exception|Error|Fault))\b', task_description)
120
+ if m:
121
+ return m.group(1)
122
+ # "in ClassName"
123
+ m = re.search(r'\bin\s+(\w+(?:Service|Controller|Repository|Handler|Manager|Rest))\b', task_description)
124
+ if m:
125
+ return m.group(1)
126
+ return ""
127
+
128
+
129
+ # ---------------------------------------------------------------------------
130
+ # Orchestration rules (executable, not docs)
131
+ # ---------------------------------------------------------------------------
132
+
133
+ def apply_orchestration_rules(
134
+ freshness: str,
135
+ is_java: bool,
136
+ api_surface_complete: bool,
137
+ repo_class_count: int,
138
+ intent: str,
139
+ sequence: list[str],
140
+ ) -> tuple[list[str], list[str]]:
141
+ """Return (adjusted_sequence, rules_applied).
142
+
143
+ Rules applied in priority order:
144
+ R1 stale_cache → prepend get_delta (always sync before deep analysis)
145
+ R2 java_no_endpoints → prepend get_endpoints (api_surface must exist first)
146
+ R3 large_repo (>1000) → note RIS path preferred (informational, no seq change)
147
+ R4 no_symptom_bug_flow → quality warning issued by caller (not a seq change)
148
+ """
149
+ seq = list(sequence)
150
+ rules: list[str] = []
151
+
152
+ # R1: stale cache + any flow → prepend delta refresh
153
+ if freshness == "stale" and "get_delta" not in seq:
154
+ seq.insert(0, "get_delta")
155
+ rules.append("R1:stale_cache→prepend_delta")
156
+
157
+ # R2: Java + no endpoint index → prepend get_endpoints
158
+ if is_java and not api_surface_complete and "get_endpoints" not in seq:
159
+ seq.insert(0, "get_endpoints")
160
+ rules.append("R2:java_no_endpoints→prepend_get_endpoints")
161
+
162
+ # R3: large repo informational flag
163
+ if repo_class_count > 1000:
164
+ rules.append("R3:large_repo→RIS_path_preferred")
165
+
166
+ return seq, rules
167
+
168
+
169
+ # ---------------------------------------------------------------------------
170
+ # Internal execute helper
171
+ # ---------------------------------------------------------------------------
172
+
173
+ def _exec(args: list[str]) -> dict[str, Any]:
174
+ """Call run_command, return result dict. On error returns dict with 'error' key."""
175
+ try:
176
+ result = run_command(args)
177
+ if isinstance(result, dict):
178
+ return result
179
+ return {"raw": result}
180
+ except Exception as exc:
181
+ return {"_exec_error": f"{type(exc).__name__}: {exc}"}
182
+
183
+
184
+ # ---------------------------------------------------------------------------
185
+ # RIS helpers
186
+ # ---------------------------------------------------------------------------
187
+
188
+ def _cold_start(repo_path: str) -> dict[str, Any]:
189
+ from sourcecode.ris import get_cold_start_context as _gcs
190
+ return _gcs(Path(repo_path))
191
+
192
+
193
+ def _freshness(ris_status: str) -> str:
194
+ if ris_status == "cold_start_ready":
195
+ return "fresh"
196
+ if ris_status in ("cold_start_stale", "cold_start_incomplete"):
197
+ return "stale"
198
+ return "missing"
199
+
200
+
201
+ def _status_to_session_state(ris_status: str) -> str:
202
+ return {
203
+ "cold_start_ready": SESSION_CONTEXT_LOADED,
204
+ "cold_start_stale": SESSION_STALE_CONTEXT,
205
+ "cold_start_incomplete": SESSION_INCOMPLETE_CONTEXT,
206
+ "no_ris": SESSION_INIT,
207
+ }.get(ris_status, SESSION_INIT)
208
+
209
+
210
+ def _is_java_repo(repo_path: str) -> bool:
211
+ p = Path(repo_path)
212
+ return (p / "pom.xml").exists() or (p / "build.gradle").exists() or (p / "build.gradle.kts").exists()
213
+
214
+
215
+ def _default_sequence_for_state(
216
+ session_state: str, is_java: bool, api_complete: bool,
217
+ ) -> list[str]:
218
+ if session_state == SESSION_INIT:
219
+ return ["get_compact_context"]
220
+ if session_state == SESSION_STALE_CONTEXT:
221
+ return ["get_delta", "analyze_task"]
222
+ if session_state == SESSION_INCOMPLETE_CONTEXT and is_java:
223
+ return ["get_endpoints", "analyze_task"]
224
+ if session_state == SESSION_CONTEXT_LOADED:
225
+ return ["analyze_task"]
226
+ return ["get_compact_context"]
227
+
228
+
229
+ def _risk_level(freshness: str, stale: bool, class_count: int) -> str:
230
+ if freshness == "missing":
231
+ return "unknown"
232
+ if stale or freshness == "stale":
233
+ return "medium"
234
+ if class_count > 2000:
235
+ return "high"
236
+ if class_count > 500:
237
+ return "medium"
238
+ return "low"
239
+
240
+
241
+ # ---------------------------------------------------------------------------
242
+ # start_session
243
+ # ---------------------------------------------------------------------------
244
+
245
+ def start_session_impl(repo_path: str, task_description: str = "") -> dict[str, Any]:
246
+ """Core logic for start_session MCP tool."""
247
+ t0 = time.monotonic()
248
+
249
+ cold = _cold_start(repo_path)
250
+ ris_status = cold.get("status", "no_ris")
251
+ freshness = _freshness(ris_status)
252
+ session_state = _status_to_session_state(ris_status)
253
+
254
+ is_java = _is_java_repo(repo_path)
255
+ api_surface_complete = cold.get("api_surface_complete", True)
256
+ validation = cold.get("validation", {})
257
+ spring_detected = validation.get("spring_detected", False)
258
+ endpoints_count = validation.get("endpoints_found", len(cold.get("endpoints", [])))
259
+
260
+ summary = cold.get("summary", {})
261
+ repo_class_count: int = (
262
+ summary.get("class_count")
263
+ or summary.get("total_classes")
264
+ or 0
265
+ )
266
+
267
+ # Intent detection
268
+ intent: Optional[str] = None
269
+ intent_confidence = 0.5
270
+ flow_runner: Optional[str] = None
271
+ if task_description.strip():
272
+ intent, intent_confidence = detect_intent(task_description)
273
+ flow_runner = FLOW_RUNNERS.get(intent)
274
+ base_seq = list(WORKFLOW_SEQUENCES.get(intent, ["get_compact_context"]))
275
+ else:
276
+ base_seq = _default_sequence_for_state(session_state, is_java, api_surface_complete)
277
+
278
+ # Apply orchestration rules
279
+ seq, rules_applied = apply_orchestration_rules(
280
+ freshness=freshness,
281
+ is_java=is_java,
282
+ api_surface_complete=api_surface_complete,
283
+ repo_class_count=repo_class_count,
284
+ intent=intent or INTENT_ORIENTATION,
285
+ sequence=base_seq,
286
+ )
287
+
288
+ # Recommended next action
289
+ next_tool = seq[0] if seq else "get_compact_context"
290
+ if task_description.strip() and intent and flow_runner:
291
+ next_tool = flow_runner
292
+ next_reason = WORKFLOW_DESCRIPTIONS.get(intent, "")
293
+ elif freshness == "missing":
294
+ next_reason = "No RIS found — build context first (~8s for large repos, instant after)"
295
+ elif freshness == "stale":
296
+ next_reason = "RIS outdated — refresh delta before deeper analysis"
297
+ elif session_state == SESSION_INCOMPLETE_CONTEXT and is_java:
298
+ next_reason = "Java repo with no endpoint index — populate API surface first"
299
+ else:
300
+ next_reason = "RIS fresh — describe your task to get a targeted tool sequence"
301
+
302
+ recommended_args: dict[str, Any] = {"repo_path": repo_path}
303
+ if task_description.strip() and intent == INTENT_BUG_INVESTIGATION:
304
+ symptom = _extract_symptom(task_description)
305
+ if symptom:
306
+ recommended_args["symptom"] = symptom
307
+
308
+ ttfca_ms = int((time.monotonic() - t0) * 1000)
309
+
310
+ effective_state = SESSION_TASK_INTENT_DETECTED if (task_description.strip() and intent) else session_state
311
+
312
+ result: dict[str, Any] = {
313
+ "session_state": effective_state,
314
+ "repo_type": "java_spring" if spring_detected else ("java" if is_java else "unknown"),
315
+ "cache_freshness": freshness,
316
+ "recommended_next_action": {
317
+ "tool": next_tool,
318
+ "reason": next_reason,
319
+ "args": recommended_args,
320
+ },
321
+ "required_tools_sequence": seq,
322
+ "risk_level": _risk_level(freshness, cold.get("stale", False), repo_class_count),
323
+ "entrypoint_candidates": cold.get("entrypoints", []),
324
+ "endpoints_count": endpoints_count,
325
+ "affected_modules": [],
326
+ "session_meta": {
327
+ "ttfca_ms": ttfca_ms,
328
+ "tools_suggested": len(seq),
329
+ "agent_decision_reduction": f"{len(seq)}/18 tools exposed",
330
+ "orchestration_rules_applied": rules_applied,
331
+ },
332
+ }
333
+
334
+ if intent:
335
+ result["intent"] = intent
336
+ result["intent_confidence"] = intent_confidence
337
+ result["workflow_description"] = WORKFLOW_DESCRIPTIONS.get(intent, "")
338
+
339
+ # Include lightweight context when available
340
+ if session_state in (SESSION_CONTEXT_LOADED, SESSION_STALE_CONTEXT, SESSION_INCOMPLETE_CONTEXT):
341
+ result["ris_summary"] = {
342
+ "git_head": cold.get("git_head", ""),
343
+ "last_updated_at": cold.get("last_updated_at", ""),
344
+ "has_uncommitted_changes": cold.get("has_uncommitted_changes", False),
345
+ "hotspots": cold.get("hotspots", [])[:5],
346
+ }
347
+
348
+ if is_java and not api_surface_complete:
349
+ result["missing_data_hint"] = (
350
+ "Java repo detected but endpoint index is empty. "
351
+ "Call get_endpoints to populate API surface."
352
+ )
353
+
354
+ if freshness == "missing":
355
+ result["bootstrap_hint"] = (
356
+ "No RIS found. Call get_compact_context to bootstrap "
357
+ "(~8s for large repos; subsequent calls instant via RIS)."
358
+ )
359
+
360
+ return result
361
+
362
+
363
+ # ---------------------------------------------------------------------------
364
+ # analyze_task
365
+ # ---------------------------------------------------------------------------
366
+
367
+ def analyze_task_impl(repo_path: str, task_description: str) -> dict[str, Any]:
368
+ """Core logic for analyze_task MCP tool."""
369
+ t0 = time.monotonic()
370
+
371
+ intent, confidence = detect_intent(task_description)
372
+ symptom = _extract_symptom(task_description) if intent == INTENT_BUG_INVESTIGATION else ""
373
+
374
+ cold = _cold_start(repo_path)
375
+ freshness = _freshness(cold.get("status", "no_ris"))
376
+ is_java = _is_java_repo(repo_path)
377
+ api_surface_complete = cold.get("api_surface_complete", True)
378
+
379
+ base_seq = list(WORKFLOW_SEQUENCES.get(intent, ["get_compact_context"]))
380
+ flow_runner = FLOW_RUNNERS.get(intent)
381
+
382
+ seq, rules = apply_orchestration_rules(
383
+ freshness=freshness,
384
+ is_java=is_java,
385
+ api_surface_complete=api_surface_complete,
386
+ repo_class_count=0,
387
+ intent=intent,
388
+ sequence=base_seq,
389
+ )
390
+
391
+ extracted_params: dict[str, Any] = {}
392
+ if symptom:
393
+ extracted_params["symptom"] = symptom
394
+
395
+ recommended_args: dict[str, Any] = {"repo_path": repo_path}
396
+ if symptom and intent == INTENT_BUG_INVESTIGATION:
397
+ recommended_args["symptom"] = symptom
398
+
399
+ ttfca_ms = int((time.monotonic() - t0) * 1000)
400
+
401
+ result: dict[str, Any] = {
402
+ "session_state": SESSION_TASK_INTENT_DETECTED,
403
+ "intent": intent,
404
+ "intent_confidence": confidence,
405
+ "workflow_description": WORKFLOW_DESCRIPTIONS.get(intent, ""),
406
+ "required_tools_sequence": seq,
407
+ "recommended_next_action": {
408
+ "tool": flow_runner or (seq[0] if seq else "get_compact_context"),
409
+ "reason": WORKFLOW_DESCRIPTIONS.get(intent, ""),
410
+ "args": recommended_args,
411
+ },
412
+ "extracted_params": extracted_params,
413
+ "session_meta": {
414
+ "ttfca_ms": ttfca_ms,
415
+ "orchestration_rules_applied": rules,
416
+ },
417
+ }
418
+
419
+ if intent == INTENT_BUG_INVESTIGATION and not symptom:
420
+ result["quality_warning"] = (
421
+ "No error class or message extracted from task description. "
422
+ "Pass symptom= to fix_bug_context for focused file ranking."
423
+ )
424
+
425
+ return result
426
+
427
+
428
+ # ---------------------------------------------------------------------------
429
+ # Flow: PR Review
430
+ # ---------------------------------------------------------------------------
431
+
432
+ def run_pr_review_flow_impl(repo_path: str, since: str = "") -> dict[str, Any]:
433
+ """PR Review Flow: delta → execution paths → blast radius of top changed classes.
434
+
435
+ Auto-detects merge-base with origin/main or origin/master when since is omitted.
436
+ Runs get_impact_context for up to 3 changed Java classes automatically.
437
+ Returns consolidated output — agent makes zero sequencing decisions.
438
+ """
439
+ t0 = time.monotonic()
440
+ steps: list[str] = []
441
+ quality_warnings: list[str] = []
442
+ output: dict[str, Any] = {}
443
+
444
+ # Check freshness
445
+ cold = _cold_start(repo_path)
446
+ if _freshness(cold.get("status", "no_ris")) == "stale":
447
+ quality_warnings.append("RIS_stale—snapshot_may_not_reflect_current_HEAD")
448
+
449
+ # Auto-detect since
450
+ if not since:
451
+ import subprocess as _sp
452
+ for base in ("origin/main", "origin/master"):
453
+ try:
454
+ r = _sp.run(
455
+ ["git", "-C", repo_path, "merge-base", "HEAD", base],
456
+ capture_output=True, text=True, timeout=5,
457
+ )
458
+ if r.returncode == 0 and r.stdout.strip():
459
+ since = r.stdout.strip()
460
+ break
461
+ except Exception:
462
+ pass
463
+ if not since:
464
+ since = "HEAD~1"
465
+
466
+ # Step 1: delta
467
+ delta = _exec(["prepare-context", "delta", repo_path, "--since", since])
468
+ steps.append(f"get_delta(since={since[:12]})")
469
+ if "_exec_error" not in delta:
470
+ output["delta_context"] = delta
471
+ else:
472
+ quality_warnings.append(f"delta_failed: {delta['_exec_error']}")
473
+
474
+ # Step 2: PR context
475
+ pr_args = ["prepare-context", "review-pr", repo_path, "--since", since]
476
+ pr = _exec(pr_args)
477
+ steps.append("review_pr_context")
478
+ if "_exec_error" not in pr:
479
+ output["pr_context"] = pr
480
+ else:
481
+ quality_warnings.append(f"review_pr_failed: {pr['_exec_error']}")
482
+
483
+ # Step 3: impact for top changed classes (up to 3)
484
+ changed_classes = _extract_changed_classes_from_delta(delta)
485
+ impact_results: list[dict[str, Any]] = []
486
+ for cls in changed_classes[:3]:
487
+ imp = _exec(["impact", cls, repo_path, "--depth", "3"])
488
+ steps.append(f"get_impact_context({cls})")
489
+ if "_exec_error" not in imp:
490
+ impact_results.append({"target": cls, "result": imp})
491
+ else:
492
+ quality_warnings.append(f"impact_failed({cls}): {imp['_exec_error']}")
493
+ if impact_results:
494
+ output["impact_analysis"] = impact_results
495
+
496
+ ttfca_ms = int((time.monotonic() - t0) * 1000)
497
+
498
+ return {
499
+ "session_state": SESSION_READY_FOR_REVIEW,
500
+ "flow": "pr_review",
501
+ "since": since,
502
+ "steps_executed": steps,
503
+ "quality_warnings": quality_warnings,
504
+ "consolidated_output": output,
505
+ "session_meta": {
506
+ "ttfca_ms": ttfca_ms,
507
+ "steps_auto_executed": len(steps),
508
+ "tools_suggested_to_agent": 0,
509
+ },
510
+ }
511
+
512
+
513
+ def _extract_changed_classes_from_delta(delta: dict[str, Any]) -> list[str]:
514
+ if "_exec_error" in delta:
515
+ return []
516
+ changed_files: list[Any] = (
517
+ delta.get("changed_files")
518
+ or delta.get("files")
519
+ or delta.get("data", {}).get("changed_files", [])
520
+ or []
521
+ )
522
+ classes: list[str] = []
523
+ for f in changed_files:
524
+ if isinstance(f, dict):
525
+ path = f.get("path") or f.get("file") or ""
526
+ elif isinstance(f, str):
527
+ path = f
528
+ else:
529
+ continue
530
+ if isinstance(path, str) and path.endswith(".java"):
531
+ name = path.split("/")[-1].replace(".java", "")
532
+ if name and name not in classes:
533
+ classes.append(name)
534
+ return classes
535
+
536
+
537
+ # ---------------------------------------------------------------------------
538
+ # Flow: Bug Investigation
539
+ # ---------------------------------------------------------------------------
540
+
541
+ def run_bug_investigation_flow_impl(repo_path: str, symptom: str = "") -> dict[str, Any]:
542
+ """Bug Investigation Flow: risk-ranked files → impact of top suspect → IR context.
543
+
544
+ symptom should be an error message, exception class, or affected class name.
545
+ Without symptom, ranking is generic (not focused) — quality_warnings will note this.
546
+ """
547
+ t0 = time.monotonic()
548
+ steps: list[str] = []
549
+ quality_warnings: list[str] = []
550
+ output: dict[str, Any] = {}
551
+
552
+ if not symptom:
553
+ quality_warnings.append(
554
+ "no_symptom_provided: file ranking is generic, not focused. "
555
+ "Pass symptom= with error message or class name for targeted analysis."
556
+ )
557
+
558
+ # Step 1: fix-bug context
559
+ fix_args = ["prepare-context", "fix-bug", repo_path]
560
+ if symptom:
561
+ fix_args.extend(["--symptom", symptom])
562
+ fix = _exec(fix_args)
563
+ steps.append(f"fix_bug_context(symptom={symptom!r})" if symptom else "fix_bug_context(no_symptom)")
564
+ if "_exec_error" not in fix:
565
+ output["risk_ranked_files"] = fix
566
+ else:
567
+ quality_warnings.append(f"fix_bug_failed: {fix['_exec_error']}")
568
+
569
+ # Step 2: impact for top suspect class
570
+ suspect = _extract_class_from_symptom(symptom) or _top_class_from_fix(fix)
571
+ if suspect:
572
+ imp = _exec(["impact", suspect, repo_path, "--depth", "4"])
573
+ steps.append(f"get_impact_context({suspect})")
574
+ if "_exec_error" not in imp:
575
+ output["impact_analysis"] = {"target": suspect, "result": imp}
576
+ else:
577
+ quality_warnings.append(f"impact_failed({suspect}): {imp['_exec_error']}")
578
+
579
+ # Step 3: IR summary for Java (dependency context)
580
+ if _is_java_repo(repo_path):
581
+ ir = _exec(["repo-ir", repo_path, "--summary-only"])
582
+ steps.append("get_ir_summary")
583
+ if "_exec_error" not in ir:
584
+ output["ir_summary"] = ir
585
+ else:
586
+ quality_warnings.append(f"ir_summary_failed: {ir['_exec_error']}")
587
+
588
+ ttfca_ms = int((time.monotonic() - t0) * 1000)
589
+
590
+ return {
591
+ "session_state": SESSION_READY_FOR_REVIEW,
592
+ "flow": "bug_investigation",
593
+ "symptom": symptom,
594
+ "suspect_class": suspect,
595
+ "steps_executed": steps,
596
+ "quality_warnings": quality_warnings,
597
+ "consolidated_output": output,
598
+ "session_meta": {
599
+ "ttfca_ms": ttfca_ms,
600
+ "steps_auto_executed": len(steps),
601
+ "tools_suggested_to_agent": 0,
602
+ },
603
+ }
604
+
605
+
606
+ def _extract_class_from_symptom(symptom: str) -> str:
607
+ if not symptom:
608
+ return ""
609
+ m = re.search(
610
+ r'\b([A-Z][a-zA-Z0-9]+(?:Service|Controller|Repository|Handler|Manager|Util|Helper|DAO|Rest|Api)?)\b',
611
+ symptom,
612
+ )
613
+ return m.group(1) if m else ""
614
+
615
+
616
+ def _top_class_from_fix(fix: dict[str, Any]) -> str:
617
+ if "_exec_error" in fix:
618
+ return ""
619
+ files: list[Any] = (
620
+ fix.get("files")
621
+ or fix.get("ranked_files")
622
+ or fix.get("top_files")
623
+ or fix.get("data", {}).get("files", [])
624
+ or []
625
+ )
626
+ for f in files:
627
+ if isinstance(f, dict):
628
+ path = str(f.get("path") or f.get("file") or "")
629
+ elif isinstance(f, str):
630
+ path = f
631
+ else:
632
+ continue
633
+ if path.endswith(".java"):
634
+ return path.split("/")[-1].replace(".java", "")
635
+ return ""
636
+
637
+
638
+ # ---------------------------------------------------------------------------
639
+ # Flow: Feature Implementation
640
+ # ---------------------------------------------------------------------------
641
+
642
+ def run_feature_flow_impl(repo_path: str, feature_description: str = "") -> dict[str, Any]:
643
+ """Feature Implementation Flow: context → API surface → recent changes → structural awareness.
644
+
645
+ Provides everything an agent needs to implement a new feature:
646
+ structural context, existing API surface, what changed recently, and coupling hotspots.
647
+ """
648
+ t0 = time.monotonic()
649
+ steps: list[str] = []
650
+ quality_warnings: list[str] = []
651
+ output: dict[str, Any] = {}
652
+
653
+ is_java = _is_java_repo(repo_path)
654
+
655
+ # Step 1: compact context (use RIS fast path if available)
656
+ cold = _cold_start(repo_path)
657
+ freshness = _freshness(cold.get("status", "no_ris"))
658
+ if freshness == "fresh" and cold.get("summary"):
659
+ output["context_summary"] = cold["summary"]
660
+ steps.append("get_context(RIS_fast_path)")
661
+ else:
662
+ ctx = _exec([repo_path, "--compact"])
663
+ steps.append("get_compact_context")
664
+ if "_exec_error" not in ctx:
665
+ output["context_summary"] = ctx
666
+ else:
667
+ quality_warnings.append(f"compact_context_failed: {ctx['_exec_error']}")
668
+
669
+ # Step 2: API surface (Java only)
670
+ if is_java:
671
+ ep = _exec(["endpoints", repo_path])
672
+ steps.append("get_endpoints")
673
+ if "_exec_error" not in ep:
674
+ output["api_surface"] = ep
675
+ else:
676
+ quality_warnings.append(f"endpoints_failed: {ep['_exec_error']}")
677
+
678
+ # Step 3: recent delta (last 3 commits for context on active areas)
679
+ delta = _exec(["prepare-context", "delta", repo_path, "--since", "HEAD~3"])
680
+ steps.append("get_delta(HEAD~3)")
681
+ if "_exec_error" not in delta:
682
+ output["recent_changes"] = delta
683
+ else:
684
+ quality_warnings.append(f"delta_failed: {delta['_exec_error']}")
685
+
686
+ # Step 4: structural context (coupling, hotspots, refactor opportunities)
687
+ refactor = _exec(["prepare-context", "refactor", repo_path])
688
+ steps.append("refactor_context")
689
+ if "_exec_error" not in refactor:
690
+ output["structural_context"] = refactor
691
+ else:
692
+ quality_warnings.append(f"refactor_context_failed: {refactor['_exec_error']}")
693
+
694
+ ttfca_ms = int((time.monotonic() - t0) * 1000)
695
+
696
+ return {
697
+ "session_state": SESSION_READY_FOR_REVIEW,
698
+ "flow": "feature_implementation",
699
+ "feature_description": feature_description,
700
+ "steps_executed": steps,
701
+ "quality_warnings": quality_warnings,
702
+ "consolidated_output": output,
703
+ "session_meta": {
704
+ "ttfca_ms": ttfca_ms,
705
+ "steps_auto_executed": len(steps),
706
+ "tools_suggested_to_agent": 0,
707
+ },
708
+ }
sourcecode/mcp/server.py CHANGED
@@ -109,12 +109,242 @@ def _check_repo_path(path: str) -> "CallToolResult | None":
109
109
  return None
110
110
 
111
111
 
112
+ @mcp.tool()
113
+ def start_session(repo_path: str = ".", task_description: str = "") -> dict:
114
+ """PRIMARY ENTRY POINT — call this first on every new MCP session.
115
+
116
+ Single entry point that replaces manual tool selection. Determines session state,
117
+ checks RIS freshness, detects repo type, and returns a ready-to-execute tool
118
+ sequence. Agent never has to guess which tool to call next.
119
+
120
+ With task_description: detects intent (pr_review, bug_investigation,
121
+ feature_implementation, refactor, test_generation) and returns the exact
122
+ flow runner to call with pre-filled args. Zero sequencing decisions for agent.
123
+
124
+ Without task_description: returns session state + recommended_next_action
125
+ based on RIS freshness and repo type (java_spring, java, etc.).
126
+
127
+ Returns:
128
+ session_state — INIT | CONTEXT_LOADED | STALE_CONTEXT |
129
+ INCOMPLETE_CONTEXT | TASK_INTENT_DETECTED
130
+ repo_type — java_spring | java | nodejs | python | unknown
131
+ cache_freshness — fresh | stale | missing
132
+ recommended_next_action — {tool, reason, args} — call this next, no guessing
133
+ required_tools_sequence — ordered list of tools the agent should call
134
+ risk_level — low | medium | high | unknown
135
+ entrypoint_candidates — top entry points from RIS
136
+ endpoints_count — Java endpoint count from RIS api_surface
137
+ session_meta — {ttfca_ms, tools_suggested, agent_decision_reduction,
138
+ orchestration_rules_applied}
139
+ intent — detected intent when task_description provided
140
+ intent_confidence — detection confidence (1.0 = explicit match)
141
+ ris_summary — lightweight RIS snapshot when context loaded
142
+ missing_data_hint — what to build next when critical data absent
143
+ bootstrap_hint — how to build RIS when missing
144
+
145
+ Orchestration rules applied automatically:
146
+ R1: stale cache → prepend get_delta to any tool sequence
147
+ R2: Java + no endpoint index → prepend get_endpoints
148
+ R3: repo > 1000 classes → RIS path flagged as preferred
149
+
150
+ KPIs tracked in session_meta:
151
+ ttfca_ms — time_to_first_correct_action in milliseconds
152
+ tools_suggested — agent decision reduction (1-3 vs 18 free tools)
153
+ orchestration_rules_applied — which rules fired
154
+
155
+ repo_path: absolute path to the repository (default: current working directory).
156
+ task_description: optional natural language task description for intent detection.
157
+ Examples: "review the PR on the auth module",
158
+ "NullPointerException in UserService.findById",
159
+ "implement a new endpoint for password reset"
160
+ """
161
+ _raw = repo_path
162
+ try:
163
+ if not isinstance(repo_path, str):
164
+ return _err("repo_path must be a string", "INVALID_ARGUMENT")
165
+ repo_path = _normalize_repo_path(repo_path)
166
+ _path_err = _check_repo_path(repo_path)
167
+ if _path_err is not None:
168
+ return _path_err
169
+ from sourcecode.mcp.orchestrator import start_session_impl
170
+ return _ok(start_session_impl(repo_path, task_description or ""))
171
+ except Exception as exc:
172
+ return _err(
173
+ f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
174
+ "INTERNAL_ERROR",
175
+ )
176
+
177
+
178
+ @mcp.tool()
179
+ def analyze_task(repo_path: str = ".", task_description: str = "") -> dict:
180
+ """Detect task intent and return targeted tool sequence. No tool guessing needed.
181
+
182
+ Given a natural language description of what you need to do, returns:
183
+ - Detected intent (pr_review, bug_investigation, feature_implementation, etc.)
184
+ - Ordered tool sequence to execute
185
+ - Recommended flow runner to call with pre-filled args
186
+ - Extracted parameters (symptom for bugs, etc.)
187
+ - Orchestration rules that were applied
188
+
189
+ Prefer start_session(task_description=...) for combined bootstrap + intent detection.
190
+ Use analyze_task standalone when session is already loaded and you have a new task.
191
+
192
+ Quality warnings included when:
193
+ - Bug intent detected but no error class/message found in description
194
+ (ranking will be generic, not focused)
195
+
196
+ repo_path: absolute path to the repository (default: current working directory).
197
+ task_description: natural language task description (required for meaningful output).
198
+ Examples: "fix the NPE in PaymentService",
199
+ "review PR changes in the billing module",
200
+ "add new REST endpoint for user preferences"
201
+ """
202
+ _raw = repo_path
203
+ try:
204
+ if not isinstance(repo_path, str):
205
+ return _err("repo_path must be a string", "INVALID_ARGUMENT")
206
+ if not isinstance(task_description, str) or not task_description.strip():
207
+ return _err("task_description must be a non-empty string", "INVALID_ARGUMENT")
208
+ repo_path = _normalize_repo_path(repo_path)
209
+ _path_err = _check_repo_path(repo_path)
210
+ if _path_err is not None:
211
+ return _path_err
212
+ from sourcecode.mcp.orchestrator import analyze_task_impl
213
+ return _ok(analyze_task_impl(repo_path, task_description))
214
+ except Exception as exc:
215
+ return _err(
216
+ f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
217
+ "INTERNAL_ERROR",
218
+ )
219
+
220
+
221
+ @mcp.tool()
222
+ def run_pr_review_flow(repo_path: str = ".", since: str = "") -> dict:
223
+ """PR Review Flow — auto-chains 3 tools: delta → execution paths → blast radius.
224
+
225
+ Auto-executes the complete PR review pipeline in one call:
226
+ 1. get_delta(since) — changed files and context since merge base
227
+ 2. review_pr_context — execution paths and hotspots for changed files
228
+ 3. get_impact_context — blast radius for up to 3 changed Java classes
229
+
230
+ Auto-detects merge base with origin/main or origin/master when since is omitted.
231
+ Falls back to HEAD~1 when no remote branch found.
232
+
233
+ Returns consolidated_output with all three results merged.
234
+ session_meta.ttfca_ms shows total wall-clock time.
235
+ quality_warnings list any partial failures (steps still return partial output).
236
+
237
+ Use this instead of calling get_delta + review_pr_context + get_impact_context
238
+ separately — agent makes zero sequencing decisions.
239
+
240
+ JAVA ONLY for blast-radius step. Delta and execution paths work on all repos.
241
+
242
+ repo_path: absolute path to the repository (default: current working directory).
243
+ since: git ref to diff against. Auto-detected from origin/main if omitted.
244
+ """
245
+ _raw = repo_path
246
+ try:
247
+ if not isinstance(repo_path, str):
248
+ return _err("repo_path must be a string", "INVALID_ARGUMENT")
249
+ repo_path = _normalize_repo_path(repo_path)
250
+ _path_err = _check_repo_path(repo_path)
251
+ if _path_err is not None:
252
+ return _path_err
253
+ from sourcecode.mcp.orchestrator import run_pr_review_flow_impl
254
+ return _ok(run_pr_review_flow_impl(repo_path, since or ""))
255
+ except Exception as exc:
256
+ return _err(
257
+ f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
258
+ "INTERNAL_ERROR",
259
+ )
260
+
261
+
262
+ @mcp.tool()
263
+ def run_bug_investigation_flow(repo_path: str = ".", symptom: str = "") -> dict:
264
+ """Bug Investigation Flow — auto-chains 3 tools: risk-rank → impact → IR context.
265
+
266
+ Auto-executes the complete bug investigation pipeline in one call:
267
+ 1. fix_bug_context(symptom) — files ranked by risk, focused on symptom
268
+ 2. get_impact_context — blast radius of the top suspect class
269
+ 3. get_ir_summary — Java IR dependency context (Java repos only)
270
+
271
+ symptom should be: error message, exception class name, or affected class.
272
+ Without symptom: ranking is generic (not focused). quality_warnings will note this.
273
+
274
+ Returns consolidated_output with all results plus suspect_class (auto-extracted
275
+ from symptom or top ranked file). session_meta.ttfca_ms shows total wall time.
276
+
277
+ Use this instead of calling fix_bug_context + get_impact_context + get_ir_summary
278
+ separately — agent makes zero sequencing decisions.
279
+
280
+ repo_path: absolute path to the repository (default: current working directory).
281
+ symptom: error message, exception class, or class name.
282
+ Examples: "NullPointerException in UserService.findById",
283
+ "PaymentController", "AuthenticationException"
284
+ """
285
+ _raw = repo_path
286
+ try:
287
+ if not isinstance(repo_path, str):
288
+ return _err("repo_path must be a string", "INVALID_ARGUMENT")
289
+ repo_path = _normalize_repo_path(repo_path)
290
+ _path_err = _check_repo_path(repo_path)
291
+ if _path_err is not None:
292
+ return _path_err
293
+ from sourcecode.mcp.orchestrator import run_bug_investigation_flow_impl
294
+ return _ok(run_bug_investigation_flow_impl(repo_path, symptom or ""))
295
+ except Exception as exc:
296
+ return _err(
297
+ f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
298
+ "INTERNAL_ERROR",
299
+ )
300
+
301
+
302
+ @mcp.tool()
303
+ def run_feature_flow(repo_path: str = ".", feature_description: str = "") -> dict:
304
+ """Feature Implementation Flow — auto-chains 4 tools: context → API → delta → structure.
305
+
306
+ Auto-executes the complete feature planning pipeline in one call:
307
+ 1. get_compact_context (or RIS fast-path if fresh) — project orientation
308
+ 2. get_endpoints — existing API surface (Java repos only)
309
+ 3. get_delta(HEAD~3) — what changed recently (active development areas)
310
+ 4. refactor_context — structural coupling and hotspot awareness
311
+
312
+ Returns consolidated_output with all four results. Use feature_description to
313
+ help the agent understand what you are building (stored in output for traceability).
314
+
315
+ Use this instead of calling the four tools separately — agent makes zero
316
+ sequencing decisions. Typical wall time: 2-10s depending on repo size and cache.
317
+
318
+ repo_path: absolute path to the repository (default: current working directory).
319
+ feature_description: optional description of the feature to implement.
320
+ Example: "add password reset flow with email verification"
321
+ """
322
+ _raw = repo_path
323
+ try:
324
+ if not isinstance(repo_path, str):
325
+ return _err("repo_path must be a string", "INVALID_ARGUMENT")
326
+ repo_path = _normalize_repo_path(repo_path)
327
+ _path_err = _check_repo_path(repo_path)
328
+ if _path_err is not None:
329
+ return _path_err
330
+ from sourcecode.mcp.orchestrator import run_feature_flow_impl
331
+ return _ok(run_feature_flow_impl(repo_path, feature_description or ""))
332
+ except Exception as exc:
333
+ return _err(
334
+ f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
335
+ "INTERNAL_ERROR",
336
+ )
337
+
338
+
112
339
  @mcp.tool()
113
340
  def get_cold_start_context(repo_path: str = ".") -> dict:
114
341
  """Instant session bootstrap from persisted Repository Intelligence Snapshot (RIS).
115
342
 
116
- CALL THIS FIRST at the start of every MCP session. Returns cached structural
117
- context built from prior analysis runs zero re-analysis cost.
343
+ PREFER start_session over this tool it provides orchestration guidance on top
344
+ of the same RIS data. Use get_cold_start_context when you only need the raw
345
+ RIS bootstrap object without tool sequencing recommendations.
346
+
347
+ Returns cached structural context built from prior analysis runs — zero re-analysis cost.
118
348
 
119
349
  status values:
120
350
  "cold_start_ready" — RIS exists and matches the current git HEAD.
@@ -368,6 +598,11 @@ def get_ir_summary(repo_path: str = ".") -> dict:
368
598
  on large repos), use the CLI: sourcecode repo-ir <path> --output ir.json
369
599
  Use get_compact_context or get_agent_context for non-Java repos.
370
600
 
601
+ IR access paths (for full IR via CLI):
602
+ nodes → result["graph"]["nodes"] (list of {fqn, type, ...})
603
+ edges → result["graph"]["edges"] (list of {source, target, type})
604
+ Summary mode omits nodes/edges entirely (graph._omitted explains why).
605
+
371
606
  repo_path: absolute path to the Java repository (default: current working directory).
372
607
  """
373
608
  _raw = repo_path
@@ -378,7 +613,14 @@ def get_ir_summary(repo_path: str = ".") -> dict:
378
613
  _path_err = _check_repo_path(repo_path)
379
614
  if _path_err is not None:
380
615
  return _path_err
381
- return _execute(["repo-ir", repo_path, "--summary-only"])
616
+ result = _execute(["repo-ir", repo_path, "--summary-only"])
617
+ if isinstance(result, dict) and "error" not in result:
618
+ result["_ir_access"] = {
619
+ "nodes_path": "result['graph']['nodes']",
620
+ "edges_path": "result['graph']['edges']",
621
+ "note": "nodes/edges omitted in summary mode — use CLI repo-ir --output ir.json for full graph",
622
+ }
623
+ return result
382
624
  except Exception as exc:
383
625
  return _err(
384
626
  f"Internal error: {type(exc).__name__}: {exc} — repo_path recibido: {_raw}",
@@ -1237,6 +1237,10 @@ class TaskContextBuilder:
1237
1237
  limitations=[], confidence="low",
1238
1238
  error_code="no_diff_source",
1239
1239
  error_message=_no_diff_msg,
1240
+ error_hints=[
1241
+ "Add --since <ref> to specify a base commit.",
1242
+ "Examples: --since origin/main | --since HEAD~3 | --since main",
1243
+ ],
1240
1244
  gaps=[_no_diff_msg],
1241
1245
  ci_decision="no_diff_source",
1242
1246
  scope_source=_pr_scope_source,
@@ -2993,7 +2993,7 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
2993
2993
  # Use security_annotations already extracted by _build_route_surface
2994
2994
  # via the canonical _route_security_from_sym extractor.
2995
2995
  security_info = route.get("security_annotations")
2996
- entry["security"] = security_info # always present; None = no security signal
2996
+ entry["security"] = security_info if security_info is not None else {"policy": "none_detected"}
2997
2997
  if security_info:
2998
2998
  # Backward compat: keep required_permission for custom annotation
2999
2999
  if isinstance(security_info, dict) and security_info.get("policy") == "custom_permission":
@@ -3708,9 +3708,15 @@ def _resolve_target(
3708
3708
  """
3709
3709
  # Normalize: strip .java suffix if given a path
3710
3710
  t = target.strip()
3711
+ _is_path_like = "/" in t or "\\" in t or t.endswith(".java")
3711
3712
  if t.endswith(".java"):
3712
3713
  t = t[:-5]
3713
- # Convert file path separators to class name
3714
+ # When given a file path (e.g. src/main/java/org/foo/Bar), extract the class
3715
+ # name (basename) so suffix matching resolves it to the correct FQN.
3716
+ # Path-to-dot conversion (src.main.java.org.foo.Bar) never matches an IR FQN.
3717
+ if _is_path_like and ("/" in t or "\\" in t):
3718
+ import os.path as _osp
3719
+ t = _osp.basename(t.replace("\\", "/"))
3714
3720
  t_class = t.replace("/", ".").replace("\\", ".")
3715
3721
 
3716
3722
  # 1. Exact match
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.33.5
3
+ Version: 1.33.7
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -39,7 +39,7 @@ Description-Content-Type: text/markdown
39
39
 
40
40
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
41
41
 
42
- ![Version](https://img.shields.io/badge/version-1.33.4-blue)
42
+ ![Version](https://img.shields.io/badge/version-1.33.7-blue)
43
43
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
44
44
 
45
45
  ---
@@ -1,4 +1,4 @@
1
- sourcecode/__init__.py,sha256=1rtjkdHP6M265Dvjzw_Pe1-jjy13_C_oBKGBnxQCbUk,103
1
+ sourcecode/__init__.py,sha256=1FxY3ofkdwrV8K_gc9aQqAWEepqXfZm-yAQCuPW5nBo,103
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/architecture_analyzer.py,sha256=qh749a7ykPtGmQI1MR9y6j8TtL_jBdVYFx9YRsLqOMw,44121
4
4
  sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
@@ -29,13 +29,13 @@ sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7
29
29
  sourcecode/output_budget.py,sha256=43307mJEyUPU3MI-QEQoVxrcAvNyUzdzF_SAPgisBQE,6603
30
30
  sourcecode/path_filters.py,sha256=ROFRQ8eSLBEMiixK9f45-RO7um4VEEcjoD5AA4I427I,3739
31
31
  sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
32
- sourcecode/prepare_context.py,sha256=aL4WS62wozw9iG_v3UrU00Qc7lGgckUB5RY5ApPblo8,205618
32
+ sourcecode/prepare_context.py,sha256=5TgEEhH7wkwup9ibdGhDNvQRwBpL3BrpqFHaJxRG6NQ,205855
33
33
  sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
34
34
  sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
35
35
  sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
36
36
  sourcecode/relevance_scorer.py,sha256=MYF4FFkveAQps9SmTeTlh6ODiBz2F--_hWNeHMLtUHQ,8405
37
37
  sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
38
- sourcecode/repository_ir.py,sha256=WtUPRVrvJju6Lrvm_7iTDk57qJFCAh-LJalLtX8jzIk,155595
38
+ sourcecode/repository_ir.py,sha256=n3QYeCC4VjgVupjTxK3cJ6T6d0xcn-GOTR18tMhT0qg,155993
39
39
  sourcecode/ris.py,sha256=vkIe_v-jjceeb0Adhn-Kw5eFXE_nIkGHflOnQYPycTk,17411
40
40
  sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
41
41
  sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
@@ -67,8 +67,9 @@ sourcecode/detectors/systems.py,sha256=nYaKbGDFu0EOXFcd_1doWFT3tTUdkbxc2DjHUF5Tc
67
67
  sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG65hZ4,1693
68
68
  sourcecode/detectors/tooling.py,sha256=8CKbtxwQoABP-WyBRNmdAmHDOvAH57AR1cF4UKuWEdQ,2074
69
69
  sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,179
70
+ sourcecode/mcp/orchestrator.py,sha256=BMi1D6liJHI3DXiaC8yeBLLP0wXajpCP3-vnRGqrvnw,26850
70
71
  sourcecode/mcp/runner.py,sha256=YSw2DXEICau6mCBr3Gfia3D_tKxMbRvIIXEh4cHC1SY,1390
71
- sourcecode/mcp/server.py,sha256=dAmoUbnu4kPPWeRH2tcOn9WJPG8Zfzvlu0nRmyiok70,27925
72
+ sourcecode/mcp/server.py,sha256=m_C0LJVQzxlSe0e_gjN5wqzXH-j7TBSpNNQnWM8Ra4g,39648
72
73
  sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
73
74
  sourcecode/mcp/onboarding/applier.py,sha256=B9CneieWTpaDSDIyW3S5nrlRlBpvfqUcgi93-mm_ApQ,2135
74
75
  sourcecode/mcp/onboarding/backup.py,sha256=ihqGOR8QTX8HASRSEDyfFyXr5bkXrygPHamv4p9KTmk,1452
@@ -80,8 +81,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
80
81
  sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
81
82
  sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
82
83
  sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
83
- sourcecode-1.33.5.dist-info/METADATA,sha256=0CKSD6Q2_A9noOoPyeC4I0A-JuYA8bo3aIAu-kxtm1A,16440
84
- sourcecode-1.33.5.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
85
- sourcecode-1.33.5.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
86
- sourcecode-1.33.5.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
87
- sourcecode-1.33.5.dist-info/RECORD,,
84
+ sourcecode-1.33.7.dist-info/METADATA,sha256=AOgEqdDTfYhRdBee0gHEvfr_hicz4N8Ex5hH0NTMAKc,16440
85
+ sourcecode-1.33.7.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
86
+ sourcecode-1.33.7.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
87
+ sourcecode-1.33.7.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
88
+ sourcecode-1.33.7.dist-info/RECORD,,