ref-agents 1.0.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 (175) hide show
  1. ref_agents/__init__.py +9 -0
  2. ref_agents/api_keys.json.example +8 -0
  3. ref_agents/auth.py +129 -0
  4. ref_agents/codemap/..md +62 -0
  5. ref_agents/codemap/CODE_MAP.md +37 -0
  6. ref_agents/codemap/core.md +43 -0
  7. ref_agents/codemap/models.md +43 -0
  8. ref_agents/codemap/prompts.md +40 -0
  9. ref_agents/codemap/security.md +45 -0
  10. ref_agents/codemap/tools.md +94 -0
  11. ref_agents/codemap/tools_browser.md +44 -0
  12. ref_agents/codemap/utils.md +42 -0
  13. ref_agents/codemap/workflow.md +42 -0
  14. ref_agents/config/ai_patterns.yaml +101 -0
  15. ref_agents/config/frameworks/angular.yaml +104 -0
  16. ref_agents/config/frameworks/aspnet.yaml +84 -0
  17. ref_agents/config/frameworks/ef_core.yaml +81 -0
  18. ref_agents/config/frameworks/react.yaml +111 -0
  19. ref_agents/config/frameworks/spring_boot.yaml +117 -0
  20. ref_agents/config/languages/csharp.yaml +153 -0
  21. ref_agents/config/languages/java.yaml +188 -0
  22. ref_agents/config/languages/javascript.yaml +172 -0
  23. ref_agents/config/languages/python.yaml +153 -0
  24. ref_agents/config/languages/typescript.yaml +193 -0
  25. ref_agents/constants.py +553 -0
  26. ref_agents/core/__init__.py +15 -0
  27. ref_agents/core/config_loader.py +160 -0
  28. ref_agents/core/config_models.py +167 -0
  29. ref_agents/core/config_parsing.py +84 -0
  30. ref_agents/core/language_detector.py +388 -0
  31. ref_agents/core/validation_models.py +66 -0
  32. ref_agents/core/validation_primitives.py +176 -0
  33. ref_agents/errors.py +34 -0
  34. ref_agents/license_client.py +247 -0
  35. ref_agents/models/__init__.py +22 -0
  36. ref_agents/models/gherkin.py +45 -0
  37. ref_agents/models/hierarchy.py +80 -0
  38. ref_agents/models/invest.py +59 -0
  39. ref_agents/models/version.py +49 -0
  40. ref_agents/prompts/__init__.py +9 -0
  41. ref_agents/prompts/start_agent.py +772 -0
  42. ref_agents/rules/architecture/backend_patterns.md +43 -0
  43. ref_agents/rules/architecture/diagramming.md +100 -0
  44. ref_agents/rules/architecture/frontend_patterns.md +40 -0
  45. ref_agents/rules/architecture/impact_analysis.md +129 -0
  46. ref_agents/rules/architecture/migration_strategy.md +208 -0
  47. ref_agents/rules/architecture/regression_protocol.md +77 -0
  48. ref_agents/rules/architecture/system_design.md +97 -0
  49. ref_agents/rules/common/codemap_standard.md +97 -0
  50. ref_agents/rules/common/core_protocol.md +59 -0
  51. ref_agents/rules/common/prompt_engineering.md +294 -0
  52. ref_agents/rules/development/debugging.md +32 -0
  53. ref_agents/rules/development/implementation.md +205 -0
  54. ref_agents/rules/operations/completion.md +119 -0
  55. ref_agents/rules/operations/cutover_protocol.md +218 -0
  56. ref_agents/rules/operations/discovery.md +179 -0
  57. ref_agents/rules/operations/fix_workflow.md +87 -0
  58. ref_agents/rules/operations/forensics.md +278 -0
  59. ref_agents/rules/operations/platform.md +263 -0
  60. ref_agents/rules/operations/synchronous_flow.md +25 -0
  61. ref_agents/rules/product/ac_validation.md +25 -0
  62. ref_agents/rules/product/brainstorming.md +27 -0
  63. ref_agents/rules/product/ref_flow.md +101 -0
  64. ref_agents/rules/product/requirements_std.md +114 -0
  65. ref_agents/rules/product/spec_writing.md +235 -0
  66. ref_agents/rules/product/strategy.md +96 -0
  67. ref_agents/rules/quality/documentation_standards.md +46 -0
  68. ref_agents/rules/quality/parity_testing.md +234 -0
  69. ref_agents/rules/quality/project_documentation.md +56 -0
  70. ref_agents/rules/quality/qa_lead.md +111 -0
  71. ref_agents/rules/quality/test_design.md +146 -0
  72. ref_agents/rules/quality/testing_standards.md +293 -0
  73. ref_agents/rules/review/pr_review.md +116 -0
  74. ref_agents/rules/security/security_audit.md +83 -0
  75. ref_agents/security/__init__.py +22 -0
  76. ref_agents/security/dependency_audit.py +188 -0
  77. ref_agents/security/file_audit.py +208 -0
  78. ref_agents/security/network_scan.py +179 -0
  79. ref_agents/security/report_generator.py +313 -0
  80. ref_agents/security/secret_scan.py +252 -0
  81. ref_agents/security/url_scan.py +240 -0
  82. ref_agents/security_scan.py +236 -0
  83. ref_agents/server.py +1586 -0
  84. ref_agents/session.py +100 -0
  85. ref_agents/tool_names.py +55 -0
  86. ref_agents/tools/__init__.py +8 -0
  87. ref_agents/tools/agents_generator.py +315 -0
  88. ref_agents/tools/ai_pattern_detector.py +815 -0
  89. ref_agents/tools/brownfield_populator.py +529 -0
  90. ref_agents/tools/browser/__init__.py +50 -0
  91. ref_agents/tools/browser/evidence_verifier.py +302 -0
  92. ref_agents/tools/browser/execution_logger.py +249 -0
  93. ref_agents/tools/browser/playwright_mcp_client.py +259 -0
  94. ref_agents/tools/browser/screenshot_utils.py +184 -0
  95. ref_agents/tools/browser/test_executor.py +537 -0
  96. ref_agents/tools/code_quality_scanner.py +629 -0
  97. ref_agents/tools/codemap/..md +93 -0
  98. ref_agents/tools/codemap/CODE_MAP.md +30 -0
  99. ref_agents/tools/codemap/browser.md +44 -0
  100. ref_agents/tools/codemap.py +403 -0
  101. ref_agents/tools/codemap_freshness.py +234 -0
  102. ref_agents/tools/comment_smell_scanner.py +346 -0
  103. ref_agents/tools/complexity.py +436 -0
  104. ref_agents/tools/complexity_ast.py +333 -0
  105. ref_agents/tools/compliance.py +246 -0
  106. ref_agents/tools/compliance_remediation.py +846 -0
  107. ref_agents/tools/context_graph.py +839 -0
  108. ref_agents/tools/context_manager.py +550 -0
  109. ref_agents/tools/context_tools.py +121 -0
  110. ref_agents/tools/cross_repo_linker.py +393 -0
  111. ref_agents/tools/dead_code_scanner.py +637 -0
  112. ref_agents/tools/debt_scanner.py +1092 -0
  113. ref_agents/tools/dependency_graph.py +272 -0
  114. ref_agents/tools/discovery_audit.py +372 -0
  115. ref_agents/tools/docs_scanner.py +600 -0
  116. ref_agents/tools/evaluate_gate.py +119 -0
  117. ref_agents/tools/external_detector.py +524 -0
  118. ref_agents/tools/features_generator.py +282 -0
  119. ref_agents/tools/flow_gap_detector.py +373 -0
  120. ref_agents/tools/flow_mapper.py +327 -0
  121. ref_agents/tools/full_suite_runner.py +740 -0
  122. ref_agents/tools/gherkin_parser.py +227 -0
  123. ref_agents/tools/guard_tools.py +139 -0
  124. ref_agents/tools/handoff_tools.py +282 -0
  125. ref_agents/tools/health_scanner.py +1211 -0
  126. ref_agents/tools/hierarchy_manager.py +289 -0
  127. ref_agents/tools/invest_scorer.py +249 -0
  128. ref_agents/tools/jira_confluence_export.py +306 -0
  129. ref_agents/tools/json_output.py +76 -0
  130. ref_agents/tools/migration_mapper.py +946 -0
  131. ref_agents/tools/migration_readiness_scanner.py +209 -0
  132. ref_agents/tools/pattern_learner.py +522 -0
  133. ref_agents/tools/report_utils.py +155 -0
  134. ref_agents/tools/requirements_serializer.py +225 -0
  135. ref_agents/tools/security_audit_tool.py +106 -0
  136. ref_agents/tools/sequencing_engine.py +288 -0
  137. ref_agents/tools/summary_generator.py +275 -0
  138. ref_agents/tools/symbol_resolver.py +306 -0
  139. ref_agents/tools/symbol_smoke_runner.py +336 -0
  140. ref_agents/tools/test_plan_validator.py +189 -0
  141. ref_agents/tools/test_smell_walker.py +902 -0
  142. ref_agents/tools/tier1_fixer.py +502 -0
  143. ref_agents/tools/validators/__init__.py +419 -0
  144. ref_agents/tools/validators/architect.py +268 -0
  145. ref_agents/tools/validators/cutover_engineer.py +167 -0
  146. ref_agents/tools/validators/developer.py +180 -0
  147. ref_agents/tools/validators/discovery.py +150 -0
  148. ref_agents/tools/validators/forensic_engineer.py +191 -0
  149. ref_agents/tools/validators/impact_architect.py +181 -0
  150. ref_agents/tools/validators/migration_planner.py +166 -0
  151. ref_agents/tools/validators/parity_tester.py +155 -0
  152. ref_agents/tools/validators/platform_engineer.py +134 -0
  153. ref_agents/tools/validators/pr_reviewer.py +129 -0
  154. ref_agents/tools/validators/product_manager.py +291 -0
  155. ref_agents/tools/validators/qa_lead.py +172 -0
  156. ref_agents/tools/validators/scrum_master.py +212 -0
  157. ref_agents/tools/validators/security_owner.py +162 -0
  158. ref_agents/tools/validators/specifier.py +134 -0
  159. ref_agents/tools/validators/strategist.py +149 -0
  160. ref_agents/tools/validators/tester.py +121 -0
  161. ref_agents/tools/version_manager.py +202 -0
  162. ref_agents/tools/workflow_tools.py +1549 -0
  163. ref_agents/utils/__init__.py +21 -0
  164. ref_agents/utils/git_utils.py +351 -0
  165. ref_agents/utils/handoff_logger.py +368 -0
  166. ref_agents/utils/ignore_matcher.py +270 -0
  167. ref_agents/workflow/__init__.py +19 -0
  168. ref_agents/workflow/capabilities.py +328 -0
  169. ref_agents/workflow/state_machine.py +708 -0
  170. ref_agents/workflow/transitions.py +658 -0
  171. ref_agents-1.0.0.dist-info/METADATA +365 -0
  172. ref_agents-1.0.0.dist-info/RECORD +175 -0
  173. ref_agents-1.0.0.dist-info/WHEEL +4 -0
  174. ref_agents-1.0.0.dist-info/entry_points.txt +2 -0
  175. ref_agents-1.0.0.dist-info/licenses/LICENSE +115 -0
@@ -0,0 +1,328 @@
1
+ """Capability Detection for REF Workflow.
2
+
3
+ Detects available tools (Playwright, etc.) and story type classification
4
+ for appropriate testing strategy selection.
5
+ """
6
+
7
+ import re
8
+ from pathlib import Path
9
+
10
+ import structlog
11
+
12
+ from ref_agents.constants import (
13
+ BACKEND_KEYWORDS,
14
+ FRONTEND_KEYWORDS,
15
+ StoryType,
16
+ )
17
+
18
+ logger = structlog.get_logger(__name__)
19
+
20
+
21
+ class CapabilityDetector:
22
+ """Detects tool capabilities and story type.
23
+
24
+ Attributes:
25
+ project_root: Root directory for file scanning.
26
+ """
27
+
28
+ def __init__(self, project_root: Path | None = None) -> None:
29
+ """Initialize capability detector.
30
+
31
+ Args:
32
+ project_root: Project root for file scanning.
33
+ """
34
+ self.project_root = project_root or Path(".")
35
+
36
+ def detect_browser_tools(self) -> bool:
37
+ """Detect if browser/Playwright MCP tools are available.
38
+
39
+ Checks for MCP browser tools availability via:
40
+ 1. Check ref-agents browser tools module
41
+ 2. Fall back to file-based indicators
42
+
43
+ Returns:
44
+ True if browser tools are available.
45
+ """
46
+ # Primary: Check ref-agents browser tools module
47
+ try:
48
+ from ref_agents.tools.browser.playwright_mcp_client import (
49
+ check_browser_tools_available,
50
+ )
51
+
52
+ if check_browser_tools_available():
53
+ logger.info("browser_tools_detected", source="playwright_mcp_client")
54
+ return True
55
+ except ImportError:
56
+ logger.debug("playwright_mcp_client not available, using fallback")
57
+
58
+ # Fallback: Check for file-based indicators
59
+ indicators = [
60
+ self.project_root / "playwright.config.ts",
61
+ self.project_root / "playwright.config.js",
62
+ self.project_root / "e2e",
63
+ self.project_root / "tests" / "e2e",
64
+ ]
65
+
66
+ for indicator in indicators:
67
+ if indicator.exists():
68
+ logger.info("browser_tools_detected", indicator=str(indicator))
69
+ return True
70
+
71
+ # Check package.json for playwright
72
+ package_json = self.project_root / "package.json"
73
+ if package_json.exists():
74
+ try:
75
+ content = package_json.read_text()
76
+ if "playwright" in content.lower():
77
+ logger.info("browser_tools_detected", source="package.json")
78
+ return True
79
+ except OSError:
80
+ pass
81
+
82
+ # Check pyproject.toml for playwright
83
+ pyproject = self.project_root / "pyproject.toml"
84
+ if pyproject.exists():
85
+ try:
86
+ content = pyproject.read_text()
87
+ if "playwright" in content.lower():
88
+ logger.info("browser_tools_detected", source="pyproject.toml")
89
+ return True
90
+ except OSError:
91
+ pass
92
+
93
+ logger.info("browser_tools_not_detected")
94
+ return False
95
+
96
+ def classify_story_type(self, requirements_content: str) -> str:
97
+ """Classify story type from requirements content.
98
+
99
+ Args:
100
+ requirements_content: Full text of requirements file.
101
+
102
+ Returns:
103
+ StoryType value (backend, frontend, fullstack, unknown).
104
+ """
105
+ content_lower = requirements_content.lower()
106
+
107
+ frontend_score = sum(1 for kw in FRONTEND_KEYWORDS if kw in content_lower)
108
+ backend_score = sum(1 for kw in BACKEND_KEYWORDS if kw in content_lower)
109
+
110
+ logger.info(
111
+ "story_type_classification",
112
+ frontend_score=frontend_score,
113
+ backend_score=backend_score,
114
+ )
115
+
116
+ if frontend_score > 0 and backend_score > 0:
117
+ return StoryType.FULLSTACK.value
118
+ elif frontend_score > 0:
119
+ return StoryType.FRONTEND.value
120
+ elif backend_score > 0:
121
+ return StoryType.BACKEND.value
122
+ else:
123
+ return StoryType.UNKNOWN.value
124
+
125
+ def classify_from_file(self, requirements_path: Path) -> str:
126
+ """Classify story type from requirements file.
127
+
128
+ Args:
129
+ requirements_path: Path to requirements markdown file.
130
+
131
+ Returns:
132
+ StoryType value.
133
+ """
134
+ if not requirements_path.exists():
135
+ logger.warning("requirements_file_not_found", path=str(requirements_path))
136
+ return StoryType.UNKNOWN.value
137
+
138
+ try:
139
+ content = requirements_path.read_text()
140
+ return self.classify_story_type(content)
141
+ except OSError as e:
142
+ logger.error("requirements_read_failed", error=str(e))
143
+ return StoryType.UNKNOWN.value
144
+
145
+ def find_requirements_file(self, story_id: str) -> Path | None:
146
+ """Find requirements file for a story.
147
+
148
+ Args:
149
+ story_id: Story identifier.
150
+
151
+ Returns:
152
+ Path to requirements file if found.
153
+ """
154
+ # Check common locations
155
+ patterns = [
156
+ f"docs/requirements/REQ-{story_id}.md",
157
+ f"docs/requirements/{story_id}.md",
158
+ f"requirements/{story_id}.md",
159
+ "docs/requirements/REQ-*.md", # Glob pattern
160
+ ]
161
+
162
+ for pattern in patterns:
163
+ if "*" in pattern:
164
+ matches = list(self.project_root.glob(pattern))
165
+ if matches:
166
+ for match in matches:
167
+ if story_id.lower() in match.name.lower():
168
+ return match
169
+ return matches[0]
170
+ else:
171
+ path = self.project_root / pattern
172
+ if path.exists():
173
+ return path
174
+
175
+ # Fallback: requirements.md anywhere within depth 3
176
+ for match in sorted(self.project_root.rglob("requirements.md")):
177
+ parts = match.relative_to(self.project_root).parts
178
+ if (
179
+ len(parts) <= 4 # noqa: PLR2004 # TECH_DEBT: extract to named constant — G25
180
+ ): # max 3 dirs deep + filename # noqa: PLR2004 # TECH_DEBT: extract to named constant — G25
181
+ logger.info("requirements_file_found_via_rglob", path=str(match))
182
+ return match
183
+
184
+ return None
185
+
186
+ def check_capabilities(
187
+ self,
188
+ story_id: str,
189
+ story_type: str | None = None,
190
+ ) -> dict:
191
+ """Check all capabilities for a story.
192
+
193
+ Args:
194
+ story_id: Story identifier.
195
+ story_type: Optional pre-classified story type.
196
+
197
+ Returns:
198
+ Dict with capability check results.
199
+ """
200
+ # Detect story type if not provided
201
+ if not story_type or story_type == StoryType.UNKNOWN.value:
202
+ req_file = self.find_requirements_file(story_id)
203
+ if req_file:
204
+ story_type = self.classify_from_file(req_file)
205
+ else:
206
+ story_type = StoryType.UNKNOWN.value
207
+
208
+ # Detect available tools
209
+ browser_available = self.detect_browser_tools()
210
+
211
+ # Determine required capabilities
212
+ requires_browser = story_type in (
213
+ StoryType.FRONTEND.value,
214
+ StoryType.FULLSTACK.value,
215
+ )
216
+
217
+ # Build capability status
218
+ capabilities = {
219
+ "browser_tools": browser_available,
220
+ "git_tools": True, # Always assume available
221
+ "file_tools": True, # Always available in MCP
222
+ }
223
+
224
+ # Check for capability gaps
225
+ gaps = []
226
+ if requires_browser and not browser_available:
227
+ gaps.append(
228
+ {
229
+ "capability": "browser_tools",
230
+ "required": True,
231
+ "available": False,
232
+ "reason": "Frontend story requires Playwright for E2E tests",
233
+ }
234
+ )
235
+
236
+ return {
237
+ "story_id": story_id,
238
+ "story_type": story_type,
239
+ "requires_browser": requires_browser,
240
+ "browser_available": browser_available,
241
+ "capabilities": capabilities,
242
+ "gaps": gaps,
243
+ "all_met": len(gaps) == 0,
244
+ }
245
+
246
+ def format_capability_report(self, check_result: dict) -> str:
247
+ """Format capability check as user-friendly report.
248
+
249
+ Args:
250
+ check_result: Result from check_capabilities().
251
+
252
+ Returns:
253
+ Formatted string report.
254
+ """
255
+ lines = [
256
+ f"📋 Capability Check: {check_result['story_id']}",
257
+ "",
258
+ f"Story Type: {check_result['story_type']}",
259
+ "",
260
+ "| Capability | Required | Available |",
261
+ "|------------|----------|-----------|",
262
+ ]
263
+
264
+ caps = check_result["capabilities"]
265
+ requires_browser = check_result["requires_browser"]
266
+
267
+ lines.append(
268
+ f"| Browser/Playwright | "
269
+ f"{'✅ Yes' if requires_browser else '➖ No'} | "
270
+ f"{'✅ Yes' if caps['browser_tools'] else '❌ No'} |"
271
+ )
272
+ lines.append("| Git Tools | ✅ Yes | ✅ Yes |")
273
+ lines.append("| File Tools | ✅ Yes | ✅ Yes |")
274
+
275
+ if check_result["gaps"]:
276
+ lines.append("")
277
+ lines.append("⚠️ WARNING: Capability gaps detected.")
278
+ lines.append("")
279
+ lines.append("Options:")
280
+ lines.append("1. Enable Playwright MCP in Cursor settings (recommended)")
281
+ lines.append("2. Run manual E2E tests, document in ui_test_report.md")
282
+ lines.append("3. Log to TECH_DEBT.md as 'E2E_PENDING' and continue")
283
+ lines.append("")
284
+ lines.append("Select option (1/2/3):")
285
+ else:
286
+ lines.append("")
287
+ lines.append("✅ All required capabilities available.")
288
+
289
+ return "\n".join(lines)
290
+
291
+
292
+ def detect_story_type_from_diff(diff_content: str) -> str:
293
+ """Classify story type from git diff content.
294
+
295
+ Useful for developer to detect frontend work mid-implementation.
296
+
297
+ Args:
298
+ diff_content: Git diff output.
299
+
300
+ Returns:
301
+ StoryType value.
302
+ """
303
+ # Check file extensions in diff
304
+ frontend_extensions = {".tsx", ".jsx", ".vue", ".svelte", ".css", ".scss"}
305
+ backend_extensions = {".py", ".java", ".go", ".rs", ".sql"}
306
+
307
+ frontend_files = 0
308
+ backend_files = 0
309
+
310
+ # Parse diff for file paths
311
+ file_pattern = re.compile(r"^\+\+\+ b/(.+)$", re.MULTILINE)
312
+ matches = file_pattern.findall(diff_content)
313
+
314
+ for filepath in matches:
315
+ ext = Path(filepath).suffix.lower()
316
+ if ext in frontend_extensions:
317
+ frontend_files += 1
318
+ elif ext in backend_extensions:
319
+ backend_files += 1
320
+
321
+ if frontend_files > 0 and backend_files > 0:
322
+ return StoryType.FULLSTACK.value
323
+ elif frontend_files > 0:
324
+ return StoryType.FRONTEND.value
325
+ elif backend_files > 0:
326
+ return StoryType.BACKEND.value
327
+
328
+ return StoryType.UNKNOWN.value