patchi 0.7.5__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 (496) hide show
  1. patchi/__init__.py +1 -0
  2. patchi/__main__.py +13 -0
  3. patchi/cli/__init__.py +0 -0
  4. patchi/cli/command_families.py +235 -0
  5. patchi/cli/commands/__init__.py +0 -0
  6. patchi/cli/commands/access_cmd.py +65 -0
  7. patchi/cli/commands/agent_stats_cmd.py +273 -0
  8. patchi/cli/commands/agents_cmd.py +187 -0
  9. patchi/cli/commands/ai_cmd.py +308 -0
  10. patchi/cli/commands/assure_cmd.py +538 -0
  11. patchi/cli/commands/audit_cmd.py +286 -0
  12. patchi/cli/commands/auto_cmd.py +110 -0
  13. patchi/cli/commands/blame_cmd.py +53 -0
  14. patchi/cli/commands/chains_cmd.py +327 -0
  15. patchi/cli/commands/charter_cmd.py +173 -0
  16. patchi/cli/commands/chat_cmd.py +597 -0
  17. patchi/cli/commands/check_cmd.py +232 -0
  18. patchi/cli/commands/cleanup_cmd.py +328 -0
  19. patchi/cli/commands/cockpit_cmd.py +208 -0
  20. patchi/cli/commands/commands_cmd.py +160 -0
  21. patchi/cli/commands/cross_repo_cmd.py +113 -0
  22. patchi/cli/commands/deps_cmd.py +110 -0
  23. patchi/cli/commands/dev_check_cmd.py +342 -0
  24. patchi/cli/commands/dev_cmd.py +445 -0
  25. patchi/cli/commands/doctor_cmd.py +457 -0
  26. patchi/cli/commands/findings_cmd.py +187 -0
  27. patchi/cli/commands/fix_cmd.py +648 -0
  28. patchi/cli/commands/fix_review_cmd.py +273 -0
  29. patchi/cli/commands/heatmap_cmd.py +62 -0
  30. patchi/cli/commands/help_cmd.py +159 -0
  31. patchi/cli/commands/hosted_cmd.py +980 -0
  32. patchi/cli/commands/init.py +456 -0
  33. patchi/cli/commands/key_cmd.py +571 -0
  34. patchi/cli/commands/learn_cmd.py +140 -0
  35. patchi/cli/commands/link_cmd.py +104 -0
  36. patchi/cli/commands/log_cmd.py +57 -0
  37. patchi/cli/commands/memory_cmd.py +215 -0
  38. patchi/cli/commands/model_cmd.py +207 -0
  39. patchi/cli/commands/notify_cmd.py +213 -0
  40. patchi/cli/commands/notify_prompts.py +29 -0
  41. patchi/cli/commands/patch_cmd.py +231 -0
  42. patchi/cli/commands/plan_cmd.py +128 -0
  43. patchi/cli/commands/plugins_cmd.py +307 -0
  44. patchi/cli/commands/queue_cmd.py +152 -0
  45. patchi/cli/commands/quick_cmd.py +159 -0
  46. patchi/cli/commands/ready_cmd.py +292 -0
  47. patchi/cli/commands/report_cmd.py +416 -0
  48. patchi/cli/commands/restrict_cmd.py +127 -0
  49. patchi/cli/commands/review_cmd.py +225 -0
  50. patchi/cli/commands/rules_cmd.py +233 -0
  51. patchi/cli/commands/scan_cmd.py +1908 -0
  52. patchi/cli/commands/settings_cmd.py +45 -0
  53. patchi/cli/commands/status_cmd.py +679 -0
  54. patchi/cli/commands/test_cmd.py +1052 -0
  55. patchi/cli/commands/trend_cmd.py +296 -0
  56. patchi/cli/commands/undo_cmd.py +235 -0
  57. patchi/cli/commands/update_cmd.py +345 -0
  58. patchi/cli/commands/verify_cmd.py +56 -0
  59. patchi/cli/commands/vr_cmd.py +440 -0
  60. patchi/cli/commands/watch_cmd.py +304 -0
  61. patchi/cli/commands/web_cmd.py +114 -0
  62. patchi/cli/console.py +101 -0
  63. patchi/cli/display/__init__.py +1 -0
  64. patchi/cli/display/live_audit.py +269 -0
  65. patchi/cli/display/live_progress.py +169 -0
  66. patchi/cli/framework.py +241 -0
  67. patchi/cli/logo.py +128 -0
  68. patchi/cli/main.py +222 -0
  69. patchi/cli/registry.py +1171 -0
  70. patchi/cli/style.py +68 -0
  71. patchi/cli/themes.py +187 -0
  72. patchi/cli/ux.py +444 -0
  73. patchi/core/__init__.py +0 -0
  74. patchi/core/agents/__init__.py +0 -0
  75. patchi/core/agents/api_fuzzer_agent.py +141 -0
  76. patchi/core/agents/attack_agent.py +242 -0
  77. patchi/core/agents/base.py +1012 -0
  78. patchi/core/agents/bug_predictor_agent.py +58 -0
  79. patchi/core/agents/build_tool_validator.py +150 -0
  80. patchi/core/agents/cache.py +192 -0
  81. patchi/core/agents/chaos_agent.py +133 -0
  82. patchi/core/agents/cicd_generator.py +240 -0
  83. patchi/core/agents/comment_scanner.py +280 -0
  84. patchi/core/agents/console_logging_agent.py +156 -0
  85. patchi/core/agents/coordinator.py +858 -0
  86. patchi/core/agents/core_scanner.py +269 -0
  87. patchi/core/agents/coverage_prioritizer.py +181 -0
  88. patchi/core/agents/dead_code_hygiene.py +296 -0
  89. patchi/core/agents/dead_code_scanner.py +692 -0
  90. patchi/core/agents/dependency_scanner.py +725 -0
  91. patchi/core/agents/doc_claim_agent.py +573 -0
  92. patchi/core/agents/duplicate_scanner.py +568 -0
  93. patchi/core/agents/env_scanner.py +410 -0
  94. patchi/core/agents/feature_flag_agent.py +89 -0
  95. patchi/core/agents/frontend_framework_agent.py +133 -0
  96. patchi/core/agents/goroutine_agent.py +82 -0
  97. patchi/core/agents/governor.py +1972 -0
  98. patchi/core/agents/i18n_agent.py +95 -0
  99. patchi/core/agents/license_compliance.py +247 -0
  100. patchi/core/agents/linking_agent.py +251 -0
  101. patchi/core/agents/memory_profiler_agent.py +102 -0
  102. patchi/core/agents/modernization_agent.py +84 -0
  103. patchi/core/agents/mutation_agent.py +174 -0
  104. patchi/core/agents/nl_query_agent.py +74 -0
  105. patchi/core/agents/promise_rejection_agent.py +100 -0
  106. patchi/core/agents/refactoring_agent.py +286 -0
  107. patchi/core/agents/resource_leak_agent.py +91 -0
  108. patchi/core/agents/route_graph_scanner.py +951 -0
  109. patchi/core/agents/run_agent.py +106 -0
  110. patchi/core/agents/rust_unwrap_agent.py +100 -0
  111. patchi/core/agents/sbom_generator.py +284 -0
  112. patchi/core/agents/scanners.py +67 -0
  113. patchi/core/agents/side/__init__.py +1 -0
  114. patchi/core/agents/side/build_agent.py +65 -0
  115. patchi/core/agents/side/format_agent.py +50 -0
  116. patchi/core/agents/side/install_agent.py +63 -0
  117. patchi/core/agents/side_file_scanner.py +549 -0
  118. patchi/core/agents/snapshot_drift_detector.py +146 -0
  119. patchi/core/agents/spa_route_inventory.py +203 -0
  120. patchi/core/agents/test_scanner.py +442 -0
  121. patchi/core/agents/tool_health.py +109 -0
  122. patchi/core/agents/tool_runner.py +162 -0
  123. patchi/core/agents/type_scanner.py +168 -0
  124. patchi/core/agents/ui_scanner.py +558 -0
  125. patchi/core/ai/__init__.py +20 -0
  126. patchi/core/ai/agent_audit.py +153 -0
  127. patchi/core/ai/agent_profiler.py +277 -0
  128. patchi/core/ai/client.py +499 -0
  129. patchi/core/ai/cost_tracker.py +107 -0
  130. patchi/core/ai/model_router.py +318 -0
  131. patchi/core/ai/orchestrator.py +625 -0
  132. patchi/core/ai/prompts.py +681 -0
  133. patchi/core/ai/smart.py +302 -0
  134. patchi/core/ai/tool_executor.py +24 -0
  135. patchi/core/ai/tools/__init__.py +10 -0
  136. patchi/core/ai/tools/executor.py +621 -0
  137. patchi/core/ai/tools/realize.py +1160 -0
  138. patchi/core/ai/tools/registry.py +1577 -0
  139. patchi/core/assurance/__init__.py +33 -0
  140. patchi/core/assurance/builder.py +211 -0
  141. patchi/core/assurance/graph.py +342 -0
  142. patchi/core/assurance/invariants.py +168 -0
  143. patchi/core/atomic.py +61 -0
  144. patchi/core/attackers/__init__.py +17 -0
  145. patchi/core/attackers/api_abuse.py +89 -0
  146. patchi/core/attackers/auth_bypass.py +73 -0
  147. patchi/core/attackers/base.py +91 -0
  148. patchi/core/attackers/business_logic.py +88 -0
  149. patchi/core/attackers/planner.py +108 -0
  150. patchi/core/attackers/priv_escalation.py +88 -0
  151. patchi/core/attackers/recon.py +87 -0
  152. patchi/core/brain/__init__.py +0 -0
  153. patchi/core/brain/ast_utils/__init__.py +81 -0
  154. patchi/core/brain/ast_utils/assignments.py +219 -0
  155. patchi/core/brain/ast_utils/calls.py +139 -0
  156. patchi/core/brain/ast_utils/config.py +132 -0
  157. patchi/core/brain/ast_utils/control_flow.py +74 -0
  158. patchi/core/brain/ast_utils/dead_symbols.py +205 -0
  159. patchi/core/brain/ast_utils/decorators.py +108 -0
  160. patchi/core/brain/ast_utils/helpers.py +144 -0
  161. patchi/core/brain/ast_utils/imports.py +169 -0
  162. patchi/core/brain/ast_utils/scan.py +143 -0
  163. patchi/core/brain/ast_utils/taint.py +310 -0
  164. patchi/core/brain/audit.py +379 -0
  165. patchi/core/brain/baseline.py +195 -0
  166. patchi/core/brain/blast_radius.py +312 -0
  167. patchi/core/brain/body_tags.py +144 -0
  168. patchi/core/brain/brain.py +1161 -0
  169. patchi/core/brain/brain_context.py +393 -0
  170. patchi/core/brain/brain_watcher.py +340 -0
  171. patchi/core/brain/charter.py +473 -0
  172. patchi/core/brain/classifier.py +214 -0
  173. patchi/core/brain/cockpit.py +350 -0
  174. patchi/core/brain/code_query.py +710 -0
  175. patchi/core/brain/contract.py +889 -0
  176. patchi/core/brain/contract_diff.py +155 -0
  177. patchi/core/brain/council.py +800 -0
  178. patchi/core/brain/cross_repo.py +104 -0
  179. patchi/core/brain/dead_code_tools.py +157 -0
  180. patchi/core/brain/doc_validator.py +197 -0
  181. patchi/core/brain/domain_activator.py +2183 -0
  182. patchi/core/brain/enriched_context.py +220 -0
  183. patchi/core/brain/file_corpus.py +220 -0
  184. patchi/core/brain/framework.py +953 -0
  185. patchi/core/brain/freshness.py +273 -0
  186. patchi/core/brain/git_aware.py +206 -0
  187. patchi/core/brain/heatmap.py +104 -0
  188. patchi/core/brain/ignore_parser.py +153 -0
  189. patchi/core/brain/import_graph.py +486 -0
  190. patchi/core/brain/languages.py +397 -0
  191. patchi/core/brain/layered_brain.py +419 -0
  192. patchi/core/brain/learning.py +229 -0
  193. patchi/core/brain/orphaned_endpoints.py +313 -0
  194. patchi/core/brain/personas/__init__.py +648 -0
  195. patchi/core/brain/personas/bad_user.py +165 -0
  196. patchi/core/brain/personas/base.py +349 -0
  197. patchi/core/brain/proactive.py +807 -0
  198. patchi/core/brain/project_context.py +121 -0
  199. patchi/core/brain/project_reader.py +521 -0
  200. patchi/core/brain/reasoning.py +269 -0
  201. patchi/core/brain/route_detector/__init__.py +15 -0
  202. patchi/core/brain/route_detector/base.py +31 -0
  203. patchi/core/brain/route_detector/csharp.py +209 -0
  204. patchi/core/brain/route_detector/file_based.py +81 -0
  205. patchi/core/brain/route_detector/go.py +152 -0
  206. patchi/core/brain/route_detector/java.py +195 -0
  207. patchi/core/brain/route_detector/javascript.py +197 -0
  208. patchi/core/brain/route_detector/php.py +158 -0
  209. patchi/core/brain/route_detector/python.py +193 -0
  210. patchi/core/brain/route_detector/registry.py +25 -0
  211. patchi/core/brain/route_detector/ruby.py +134 -0
  212. patchi/core/brain/route_detector/rust.py +216 -0
  213. patchi/core/brain/route_detector/swift.py +118 -0
  214. patchi/core/brain/route_mapper.py +265 -0
  215. patchi/core/brain/scanner.py +2136 -0
  216. patchi/core/brain/secrets.py +280 -0
  217. patchi/core/brain/symbol_graph.py +1955 -0
  218. patchi/core/brain/team.py +28 -0
  219. patchi/core/brain/trace_log.py +132 -0
  220. patchi/core/brain/type_checker/__init__.py +65 -0
  221. patchi/core/brain/type_checker/base.py +60 -0
  222. patchi/core/brain/type_checker/csharp.py +59 -0
  223. patchi/core/brain/type_checker/dart.py +59 -0
  224. patchi/core/brain/type_checker/go.py +81 -0
  225. patchi/core/brain/type_checker/java.py +75 -0
  226. patchi/core/brain/type_checker/kotlin.py +75 -0
  227. patchi/core/brain/type_checker/php.py +88 -0
  228. patchi/core/brain/type_checker/python.py +87 -0
  229. patchi/core/brain/type_checker/rust.py +81 -0
  230. patchi/core/brain/type_checker/swift.py +59 -0
  231. patchi/core/brain/type_checker/typescript.py +298 -0
  232. patchi/core/brain/understander.py +130 -0
  233. patchi/core/brain/verify.py +379 -0
  234. patchi/core/campaigns/__init__.py +14 -0
  235. patchi/core/campaigns/base.py +62 -0
  236. patchi/core/campaigns/data_flow_abuse.py +153 -0
  237. patchi/core/campaigns/orchestrator.py +140 -0
  238. patchi/core/campaigns/state_transitions.py +178 -0
  239. patchi/core/ci_bundle.py +104 -0
  240. patchi/core/config.py +311 -0
  241. patchi/core/constants.py +245 -0
  242. patchi/core/debug/__init__.py +25 -0
  243. patchi/core/debug/adapters/__init__.py +11 -0
  244. patchi/core/debug/adapters/codelldb.py +379 -0
  245. patchi/core/debug/adapters/node.py +222 -0
  246. patchi/core/debug/adapters/powershell.py +210 -0
  247. patchi/core/debug/adapters/python.py +244 -0
  248. patchi/core/debug/capture.py +210 -0
  249. patchi/core/debug/dap_client.py +268 -0
  250. patchi/core/detector/__init__.py +0 -0
  251. patchi/core/detector/audit.py +194 -0
  252. patchi/core/detector/bus.py +226 -0
  253. patchi/core/detector/dispatcher.py +275 -0
  254. patchi/core/detector/event.py +254 -0
  255. patchi/core/detector/sigma_engine.py +378 -0
  256. patchi/core/detector/triage.py +289 -0
  257. patchi/core/export/sarif.py +113 -0
  258. patchi/core/fix/__init__.py +17 -0
  259. patchi/core/fix/applier.py +574 -0
  260. patchi/core/fix/base.py +510 -0
  261. patchi/core/fix/code_fixer.py +169 -0
  262. patchi/core/fix/dead_code_remover.py +131 -0
  263. patchi/core/fix/fix_agents.py +775 -0
  264. patchi/core/fix/patch.py +444 -0
  265. patchi/core/fix/risk_gate.py +383 -0
  266. patchi/core/fix/security_fixer.py +93 -0
  267. patchi/core/fix/verify_loop.py +204 -0
  268. patchi/core/fuzz/__init__.py +8 -0
  269. patchi/core/fuzz/corpus.py +137 -0
  270. patchi/core/fuzz/input_fuzzer.py +256 -0
  271. patchi/core/fuzz/sequence_fuzzer.py +162 -0
  272. patchi/core/fuzz/state_fuzzer.py +236 -0
  273. patchi/core/health.py +407 -0
  274. patchi/core/hosted/__init__.py +1 -0
  275. patchi/core/hosted/anomaly.py +298 -0
  276. patchi/core/hosted/audit_log.py +102 -0
  277. patchi/core/hosted/compliance_report.py +148 -0
  278. patchi/core/hosted/ip_reputation.py +169 -0
  279. patchi/core/hosted/log_parsers.py +251 -0
  280. patchi/core/hosted/tokens.py +127 -0
  281. patchi/core/hosted/watchlist.py +164 -0
  282. patchi/core/hosted/webhooks.py +156 -0
  283. patchi/core/memory.py +437 -0
  284. patchi/core/notifications/__init__.py +18 -0
  285. patchi/core/notifications/channels.py +192 -0
  286. patchi/core/notifications/digest.py +141 -0
  287. patchi/core/notifications/escalation.py +158 -0
  288. patchi/core/notifications/notifier.py +182 -0
  289. patchi/core/notifications/quiet_hours.py +69 -0
  290. patchi/core/plugins/__init__.py +48 -0
  291. patchi/core/plugins/analyzer.py +326 -0
  292. patchi/core/plugins/analyzers/__init__.py +6 -0
  293. patchi/core/plugins/analyzers/dead_code_analyzer.py +120 -0
  294. patchi/core/plugins/analyzers/secret_scanner.py +158 -0
  295. patchi/core/plugins/analyzers/todo_scanner.py +115 -0
  296. patchi/core/plugins/registry.py +335 -0
  297. patchi/core/queue.py +375 -0
  298. patchi/core/reliability/__init__.py +13 -0
  299. patchi/core/reliability/chaos.py +163 -0
  300. patchi/core/reliability/fault_injector.py +172 -0
  301. patchi/core/reliability/idempotency.py +128 -0
  302. patchi/core/reliability/recovery.py +147 -0
  303. patchi/core/runtime/__init__.py +5 -0
  304. patchi/core/runtime/tracer.py +230 -0
  305. patchi/core/scan_bus.py +156 -0
  306. patchi/core/security/__init__.py +1 -0
  307. patchi/core/security/ai_validator.py +462 -0
  308. patchi/core/security/app_mapper.py +216 -0
  309. patchi/core/security/app_profile.py +464 -0
  310. patchi/core/security/attack_feedback.py +141 -0
  311. patchi/core/security/attack_scenarios/__init__.py +1 -0
  312. patchi/core/security/attack_tree.py +176 -0
  313. patchi/core/security/auth_audit_agent.py +344 -0
  314. patchi/core/security/authz_agent.py +602 -0
  315. patchi/core/security/auto_fix_proactive.py +620 -0
  316. patchi/core/security/auto_fixer.py +660 -0
  317. patchi/core/security/auto_ticket.py +53 -0
  318. patchi/core/security/bandit_agent.py +152 -0
  319. patchi/core/security/blast_radius.py +226 -0
  320. patchi/core/security/browser_tester.py +333 -0
  321. patchi/core/security/business_logic_agent.py +263 -0
  322. patchi/core/security/catch_block_auditor.py +395 -0
  323. patchi/core/security/cdn_cache_agent.py +357 -0
  324. patchi/core/security/chain_analyzer.py +321 -0
  325. patchi/core/security/chain_to_assurance.py +168 -0
  326. patchi/core/security/charter.py +510 -0
  327. patchi/core/security/cloud_waf_detector.py +295 -0
  328. patchi/core/security/codeql_agent.py +233 -0
  329. patchi/core/security/compliance_agent.py +348 -0
  330. patchi/core/security/confidence_gate.py +423 -0
  331. patchi/core/security/container_scanner.py +282 -0
  332. patchi/core/security/crypto_agent.py +378 -0
  333. patchi/core/security/cve_monitor.py +262 -0
  334. patchi/core/security/dast_agent.py +865 -0
  335. patchi/core/security/defectdojo.py +238 -0
  336. patchi/core/security/defenders/__init__.py +75 -0
  337. patchi/core/security/defenders/auth_middleware.py +115 -0
  338. patchi/core/security/defenders/base.py +97 -0
  339. patchi/core/security/defenders/block_ip.py +71 -0
  340. patchi/core/security/defenders/block_ws_origin.py +36 -0
  341. patchi/core/security/defenders/crypto_fix.py +118 -0
  342. patchi/core/security/defenders/enforce_rate_limit.py +108 -0
  343. patchi/core/security/defenders/fix_code.py +93 -0
  344. patchi/core/security/defenders/invalidate_session.py +40 -0
  345. patchi/core/security/defenders/patch_config.py +70 -0
  346. patchi/core/security/defenders/rotate_secret.py +54 -0
  347. patchi/core/security/defenders/suspend_account.py +41 -0
  348. patchi/core/security/defenders/update_dependency.py +160 -0
  349. patchi/core/security/defense_layer.py +217 -0
  350. patchi/core/security/dependency_vulnerability_agent.py +616 -0
  351. patchi/core/security/detection_pipeline.py +289 -0
  352. patchi/core/security/dns_security_agent.py +497 -0
  353. patchi/core/security/domain_activator_v2.py +913 -0
  354. patchi/core/security/domain_loader.py +1004 -0
  355. patchi/core/security/domains/__init__.py +0 -0
  356. patchi/core/security/email_authentication_agent.py +417 -0
  357. patchi/core/security/env_var_validator.py +304 -0
  358. patchi/core/security/evidence.py +100 -0
  359. patchi/core/security/falco_runtime_agent.py +387 -0
  360. patchi/core/security/fix-playbooks/__init__.py +0 -0
  361. patchi/core/security/gated_finding.py +85 -0
  362. patchi/core/security/git_diff_activator.py +367 -0
  363. patchi/core/security/governance.py +208 -0
  364. patchi/core/security/gradual_gate.py +34 -0
  365. patchi/core/security/history.py +315 -0
  366. patchi/core/security/iac_scanner.py +250 -0
  367. patchi/core/security/ignore_expiry.py +111 -0
  368. patchi/core/security/ignore_learner.py +416 -0
  369. patchi/core/security/injection_agent.py +319 -0
  370. patchi/core/security/insecure_randomness_agent.py +288 -0
  371. patchi/core/security/intent_analyzer.py +326 -0
  372. patchi/core/security/jwt_agent.py +453 -0
  373. patchi/core/security/kubernetes_agent.py +638 -0
  374. patchi/core/security/layer2_orchestrator.py +341 -0
  375. patchi/core/security/llm_security_agent.py +252 -0
  376. patchi/core/security/misconfig_agent.py +600 -0
  377. patchi/core/security/mobile_security_agent.py +474 -0
  378. patchi/core/security/network_agent.py +376 -0
  379. patchi/core/security/noise_filter.py +274 -0
  380. patchi/core/security/orchestrator.py +313 -0
  381. patchi/core/security/pattern_context.py +125 -0
  382. patchi/core/security/pattern_loader.py +101 -0
  383. patchi/core/security/pentest/__init__.py +4 -0
  384. patchi/core/security/pentest/base.py +58 -0
  385. patchi/core/security/pentest/dalfox_adapter.py +40 -0
  386. patchi/core/security/pentest/ffuf_adapter.py +48 -0
  387. patchi/core/security/pentest/nuclei_adapter.py +50 -0
  388. patchi/core/security/pentest/registry.py +80 -0
  389. patchi/core/security/pentest/shannon_adapter.py +107 -0
  390. patchi/core/security/pentest/sqlmap_adapter.py +56 -0
  391. patchi/core/security/pentest/zap_adapter.py +52 -0
  392. patchi/core/security/plan_auditor.py +276 -0
  393. patchi/core/security/policy_engine.py +272 -0
  394. patchi/core/security/prechecks.py +172 -0
  395. patchi/core/security/precommit_hook.py +247 -0
  396. patchi/core/security/privacy_agent.py +361 -0
  397. patchi/core/security/push_notification_agent.py +417 -0
  398. patchi/core/security/pysa_agent.py +143 -0
  399. patchi/core/security/reasoning.py +442 -0
  400. patchi/core/security/red_team_agent.py +184 -0
  401. patchi/core/security/red_team_engine.py +1142 -0
  402. patchi/core/security/remediation.py +360 -0
  403. patchi/core/security/request_interceptor.py +294 -0
  404. patchi/core/security/runtime_validator.py +245 -0
  405. patchi/core/security/saml_sso_agent.py +401 -0
  406. patchi/core/security/sast_agent.py +213 -0
  407. patchi/core/security/sast_gate.py +95 -0
  408. patchi/core/security/scheduler.py +287 -0
  409. patchi/core/security/secrets_runtime_agent.py +476 -0
  410. patchi/core/security/security_agents.py +175 -0
  411. patchi/core/security/security_config.py +551 -0
  412. patchi/core/security/security_probe.py +762 -0
  413. patchi/core/security/security_taint.py +1005 -0
  414. patchi/core/security/sensitive_data_agent.py +271 -0
  415. patchi/core/security/service_mesh_agent.py +412 -0
  416. patchi/core/security/session_management_agent.py +245 -0
  417. patchi/core/security/ssrf_agent.py +422 -0
  418. patchi/core/security/supply_chain.py +449 -0
  419. patchi/core/security/threat_model_generator.py +378 -0
  420. patchi/core/security/threat_model_updater.py +196 -0
  421. patchi/core/security/tool_adapters.py +135 -0
  422. patchi/core/security/tool_verify.py +173 -0
  423. patchi/core/security/websocket_security_agent.py +188 -0
  424. patchi/core/snapshot.py +242 -0
  425. patchi/core/tenant.py +386 -0
  426. patchi/core/testing/__init__.py +17 -0
  427. patchi/core/testing/_browser.py +147 -0
  428. patchi/core/testing/ads_agent.py +141 -0
  429. patchi/core/testing/api_contract_agent.py +450 -0
  430. patchi/core/testing/app_discovery_agent.py +641 -0
  431. patchi/core/testing/app_launcher.py +184 -0
  432. patchi/core/testing/browser_test_agent.py +516 -0
  433. patchi/core/testing/e2e_flow_agent.py +354 -0
  434. patchi/core/testing/flake_detector_agent.py +292 -0
  435. patchi/core/testing/gate.py +31 -0
  436. patchi/core/testing/live_test_runner.py +304 -0
  437. patchi/core/testing/live_v2/__init__.py +1 -0
  438. patchi/core/testing/live_v2/browser_pool.py +349 -0
  439. patchi/core/testing/live_v2/browser_test_runner.py +313 -0
  440. patchi/core/testing/live_v2/runner.py +626 -0
  441. patchi/core/testing/live_v2/screenshot_manager.py +474 -0
  442. patchi/core/testing/live_v2/stress_orchestrator.py +523 -0
  443. patchi/core/testing/live_v2/video_recorder.py +303 -0
  444. patchi/core/testing/regression_agent.py +302 -0
  445. patchi/core/testing/security_test_agent.py +253 -0
  446. patchi/core/testing/stress_test_agent.py +203 -0
  447. patchi/core/testing/test_agents.py +50 -0
  448. patchi/core/testing/test_config.py +194 -0
  449. patchi/core/testing/ui_accessibility_agent.py +324 -0
  450. patchi/core/testing/ui_button_agent.py +294 -0
  451. patchi/core/testing/ui_layout_agent.py +330 -0
  452. patchi/core/testing/unit_test_agent.py +1149 -0
  453. patchi/core/testing/visual_regression_agent.py +260 -0
  454. patchi/web/__init__.py +1 -0
  455. patchi/web/api/__init__.py +1 -0
  456. patchi/web/api/brain_map.py +114 -0
  457. patchi/web/api/charts.py +47 -0
  458. patchi/web/api/chat.py +224 -0
  459. patchi/web/api/cicd.py +576 -0
  460. patchi/web/api/dast_evidence.py +49 -0
  461. patchi/web/api/dev_check.py +320 -0
  462. patchi/web/api/fix.py +134 -0
  463. patchi/web/api/guard.py +65 -0
  464. patchi/web/api/hosted.py +128 -0
  465. patchi/web/api/hosted_v2.py +234 -0
  466. patchi/web/api/live_testing.py +699 -0
  467. patchi/web/api/scan.py +582 -0
  468. patchi/web/api/smart.py +111 -0
  469. patchi/web/api/tenant.py +117 -0
  470. patchi/web/api/tools.py +85 -0
  471. patchi/web/api_legacy.py +2401 -0
  472. patchi/web/app.py +310 -0
  473. patchi/web/events.py +483 -0
  474. patchi/web/routes/__init__.py +1 -0
  475. patchi/web/routes/assurance.py +541 -0
  476. patchi/web/routes/brain.py +36 -0
  477. patchi/web/routes/charter.py +227 -0
  478. patchi/web/routes/chat.py +23 -0
  479. patchi/web/routes/dashboard.py +558 -0
  480. patchi/web/routes/dashboard_v2.py +402 -0
  481. patchi/web/routes/findings.py +330 -0
  482. patchi/web/routes/guard.py +35 -0
  483. patchi/web/routes/history.py +126 -0
  484. patchi/web/routes/landing.py +52 -0
  485. patchi/web/routes/live_testing.py +24 -0
  486. patchi/web/routes/review.py +57 -0
  487. patchi/web/routes/self_improvement.py +110 -0
  488. patchi/web/routes/settings.py +49 -0
  489. patchi/web/routes/tokens.py +29 -0
  490. patchi/web/spawn.py +139 -0
  491. patchi/web/ws.py +141 -0
  492. patchi-0.7.5.dist-info/METADATA +315 -0
  493. patchi-0.7.5.dist-info/RECORD +496 -0
  494. patchi-0.7.5.dist-info/WHEEL +5 -0
  495. patchi-0.7.5.dist-info/licenses/LICENSE +60 -0
  496. patchi-0.7.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,573 @@
1
+ """DocClaimAgent — LLM-powered documentation claim miner.
2
+
3
+ Reads all project documentation files (README, docs/, .md, .txt, .rst, .adoc),
4
+ extracts structured verifiable claims via LLM, and hands them to the Brain
5
+ for structural cross-referencing against the actual codebase.
6
+
7
+ Usage (via Brain.scan):
8
+ agent = DocClaimAgent()
9
+ result = agent.run(AgentInput(root=root, config=config, ...))
10
+ claims = result.data["claims"] # list[Claim]
11
+
12
+ Fallback (--offline mode):
13
+ from patchi.core.brain.doc_validator import validate_project_docs
14
+ result = validate_project_docs(...) # regex-based heuristic
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ import logging
20
+ from concurrent.futures import ThreadPoolExecutor, as_completed
21
+ from dataclasses import dataclass, field
22
+ from pathlib import Path
23
+
24
+ from loguru import logger
25
+
26
+ from patchi.core.brain.languages import DEFAULT_IGNORE_DIRS
27
+
28
+ from .base import (
29
+ AgentGroup,
30
+ AgentInput,
31
+ AgentResult,
32
+ BaseAgent,
33
+ register,
34
+ )
35
+
36
+ # Doc file discovery patterns — covers every common project doc format
37
+ _DOC_PATTERNS = [
38
+ "README*",
39
+ "CONTRIBUTING*",
40
+ "CHANGELOG*",
41
+ "CHANGES*",
42
+ "HISTORY*",
43
+ "SECURITY*",
44
+ "CODE_OF_CONDUCT*",
45
+ "SUPPORT*",
46
+ "AUTHORS*",
47
+ "MAINTAINERS*",
48
+ "GOVERNANCE*",
49
+ "ROADMAP*",
50
+ "VISION*",
51
+ "GOALS*",
52
+ "MILESTONES*",
53
+ "LICENSE*",
54
+ "COPYING*",
55
+ "NOTICE*",
56
+ "*.md",
57
+ "*.MD",
58
+ "*.txt",
59
+ "*.TXT",
60
+ "*.rst",
61
+ "*.RST",
62
+ "*.adoc",
63
+ "*.ADOC",
64
+ "*.asciidoc",
65
+ "*.mdown",
66
+ "docs/**/*",
67
+ "documentation/**/*",
68
+ "wiki/**/*",
69
+ "man/**/*",
70
+ ]
71
+
72
+ _EXCLUDED_DIRS = DEFAULT_IGNORE_DIRS | {"files 6", ".vscode", ".idea"}
73
+
74
+ _CLAIM_EXTRACTION_PROMPT = """\
75
+ You are reading a documentation file from a software project.
76
+ Extract every concrete, verifiable claim about the project into a JSON array.
77
+
78
+ A claim is a statement that CAN be checked against source code — something that is either true or false based on what the code actually does.
79
+
80
+ INCLUDE claims about:
81
+ - Features the project supports ("supports JWT", "handles file uploads")
82
+ - CLI commands and flags ("`p scan --deep` runs deep scan")
83
+ - API endpoints and methods ("POST /api/v1/users")
84
+ - Configuration keys and options ("`debug: true` enables debug mode")
85
+ - Supported formats, platforms, protocols ("exports to CSV", "runs on Linux/macOS")
86
+ - Dependencies and integrations ("uses Redis for caching", "integrates with Slack")
87
+ - Architecture patterns ("uses MVC architecture", "plugin-based")
88
+ - Security measures ("audit logging", "rate limiting")
89
+ - Test capabilities ("supports browser tests via Playwright")
90
+
91
+ EXCLUDE:
92
+ - Opinions, intros, thank-yous ("we love contributors", "built with care")
93
+ - License text, copyright notices
94
+ - Contribution guidelines, setup instructions for contributors
95
+ - Hypotheticals, future plans ("we plan to add", "coming soon")
96
+ - Generic meta-commentary about the project
97
+ - Things that are self-evident from reading ("this is a README file")
98
+
99
+ Output ONLY a JSON array of objects with this structure:
100
+ [
101
+ {{
102
+ "claim": "The exact sentence from the doc",
103
+ "category": "feature|cli_command|api_endpoint|config_key|dependency|architecture|format_support|security|test_capability",
104
+ "evidence_hints": [
105
+ "route: POST /api/v1/users",
106
+ "function: verify_jwt_token",
107
+ "class: JwtMiddleware",
108
+ "cli: p scan --deep",
109
+ "import: redis",
110
+ "file_match: config/**/*.yaml",
111
+ "config_key: debug",
112
+ "package: pytest",
113
+ "symbol: JWT_SECRET"
114
+ ]
115
+ }}
116
+ ]
117
+
118
+ If there are zero verifiable claims, output: []
119
+ Do NOT output anything else — no explanations, no markdown.
120
+ """
121
+
122
+
123
+ _log = logging.getLogger("patchi.agents.doc_claim_agent")
124
+
125
+
126
+ @dataclass
127
+ class Claim:
128
+ """A single verifiable claim extracted from documentation."""
129
+
130
+ text: str
131
+ source_file: str
132
+ category: str # feature, cli_command, api_endpoint, config_key, dependency, architecture, format_support, security, test_capability
133
+ evidence_hints: list[str] # hints for the brain to verify structurally
134
+ verified: bool = False # set by the brain after cross-referencing
135
+ evidence: list[str] = field(default_factory=list) # actual verification evidence
136
+
137
+
138
+ @register
139
+ class DocClaimAgent(BaseAgent):
140
+ """LLM-powered documentation claim miner.
141
+
142
+ Discovers all project documentation files, sends each to the LLM
143
+ with a structured extraction prompt, and returns structured claims
144
+ with evidence hints for the brain to verify against the codebase.
145
+
146
+ Falls back gracefully: if LLM is unavailable, returns empty claims
147
+ and the caller should fall back to doc_validator heuristic.
148
+ """
149
+
150
+ group = AgentGroup.SCANNER
151
+ name = "DocClaimAgent"
152
+ timeout = 120 # LLM calls can take time
153
+
154
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
155
+ import os
156
+
157
+ if os.environ.get("PATCHI_OFFLINE"):
158
+ logger.info("Offline mode — DocClaimAgent skipped")
159
+ result.data["claims"] = []
160
+ result.data["doc_files_found"] = []
161
+ result.data["offline"] = True
162
+ return
163
+
164
+ doc_files = self._discover_docs(inp.root)
165
+ if not doc_files:
166
+ logger.info("No documentation files found")
167
+ result.data["claims"] = []
168
+ result.data["doc_files_found"] = []
169
+ return
170
+
171
+ result.data["doc_files_found"] = [str(p) for p in doc_files]
172
+
173
+ all_claims: list[dict] = []
174
+ ai_config = inp.config.get("ai", {})
175
+ has_ai = bool(ai_config.get("keys") or ai_config.get("local_model_name"))
176
+
177
+ if not has_ai:
178
+ logger.info(
179
+ "No AI configured — DocClaimAgent returns no claims (fallback to heuristic)"
180
+ )
181
+ result.data["claims"] = []
182
+ result.data["ai_unavailable"] = True
183
+ return
184
+
185
+ max_workers = min(len(doc_files), 4)
186
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
187
+ future_to_doc = {}
188
+ for doc_path in doc_files:
189
+ try:
190
+ text = doc_path.read_text(encoding="utf-8", errors="replace")
191
+ except Exception as e:
192
+ logger.warning(f"Cannot read {doc_path}: {e}")
193
+ continue
194
+
195
+ if len(text.strip()) < 20:
196
+ continue
197
+
198
+ future = executor.submit(
199
+ self._extract_claims, text, doc_path, result, inp.config, ai_config
200
+ )
201
+ future_to_doc[future] = doc_path
202
+
203
+ for future in as_completed(future_to_doc):
204
+ doc_path = future_to_doc[future]
205
+ try:
206
+ claims = future.result()
207
+ except Exception as e:
208
+ logger.warning(f"Claim extraction failed for {doc_path.name}: {e}")
209
+ result.errors.append(f"{doc_path.name}: {e}")
210
+ continue
211
+
212
+ for c in claims:
213
+ c["source_file"] = str(doc_path.relative_to(inp.root))
214
+ all_claims.append(c)
215
+
216
+ result.ai_calls_made += 1
217
+
218
+ result.data["claims"] = all_claims
219
+ result.data["total_claims"] = len(all_claims)
220
+ logger.info(f"DocClaimAgent: {len(all_claims)} claims from {len(doc_files)} files")
221
+
222
+ def _discover_docs(self, root: Path) -> list[Path]:
223
+ """Discover all documentation files in the project tree."""
224
+ found: list[Path] = []
225
+ seen: set[Path] = set()
226
+
227
+ for pattern in _DOC_PATTERNS:
228
+ for path in root.glob(pattern):
229
+ resolved = path.resolve()
230
+ if resolved in seen:
231
+ continue
232
+ # Skip excluded dirs
233
+ rel = resolved.relative_to(root).as_posix()
234
+ parts = rel.replace("\\", "/").split("/")
235
+ if any(p in _EXCLUDED_DIRS for p in parts):
236
+ continue
237
+ # Skip binary/irrelevant files by extension check
238
+ if resolved.suffix.lower() not in (
239
+ ".md",
240
+ ".txt",
241
+ ".rst",
242
+ ".adoc",
243
+ ".asciidoc",
244
+ ".mdown",
245
+ ".mdx",
246
+ ) and resolved.name not in (
247
+ "README",
248
+ "CONTRIBUTING",
249
+ "CHANGELOG",
250
+ "CHANGES",
251
+ "HISTORY",
252
+ "SECURITY",
253
+ "CODE_OF_CONDUCT",
254
+ "LICENSE",
255
+ "COPYING",
256
+ "NOTICE",
257
+ "SUPPORT",
258
+ "AUTHORS",
259
+ "MAINTAINERS",
260
+ "GOVERNANCE",
261
+ "ROADMAP",
262
+ "VISION",
263
+ "GOALS",
264
+ "MILESTONES",
265
+ ):
266
+ # Skip non-text files matched by docs/**/* (binary, images, etc.)
267
+ if resolved.suffix in (
268
+ ".png",
269
+ ".jpg",
270
+ ".gif",
271
+ ".svg",
272
+ ".ico",
273
+ ".pdf",
274
+ ".zip",
275
+ ".gz",
276
+ ".tar",
277
+ ".bin",
278
+ ".exe",
279
+ ".dll",
280
+ ".so",
281
+ ".dylib",
282
+ ".pyc",
283
+ ".woff",
284
+ ".woff2",
285
+ ".ttf",
286
+ ".eot",
287
+ ):
288
+ continue
289
+ seen.add(resolved)
290
+ found.append(resolved)
291
+
292
+ found.sort(key=lambda p: p.as_posix())
293
+ return found
294
+
295
+ def _extract_claims(
296
+ self,
297
+ text: str,
298
+ doc_path: Path,
299
+ result: AgentResult,
300
+ config: dict,
301
+ ai_config: dict,
302
+ ) -> list[dict]:
303
+ """Send a single doc file to the LLM and parse the claim JSON."""
304
+ from patchi.core.ai.client import call_ai
305
+
306
+ # Truncate very large doc files to avoid token blowout
307
+ text = text[:15000]
308
+
309
+ prompt = _CLAIM_EXTRACTION_PROMPT + "\n\n--- DOCUMENT ---\n" + text
310
+ system_prompt = "You are a documentation analyser. Output only valid JSON."
311
+
312
+ max_retries = 2
313
+ for attempt in range(max_retries):
314
+ try:
315
+ response = call_ai(
316
+ config=config,
317
+ system_prompt=system_prompt,
318
+ user_prompt=prompt,
319
+ max_tokens=4096,
320
+ temperature=0.1,
321
+ )
322
+ except Exception as e:
323
+ logger.warning(
324
+ f"LLM extraction failed for {doc_path.name} (attempt {attempt + 1}): {e}"
325
+ )
326
+ if attempt < max_retries - 1:
327
+ continue
328
+ result.errors.append(f"{doc_path.name}: LLM error — {e}")
329
+ return []
330
+
331
+ if not response:
332
+ if attempt < max_retries - 1:
333
+ continue
334
+ return []
335
+
336
+ claims = self._parse_claims(response, doc_path)
337
+ if not claims and attempt < max_retries - 1:
338
+ logger.debug(f"Empty claims for {doc_path.name}, retrying...")
339
+ continue
340
+
341
+ return claims
342
+
343
+ return []
344
+
345
+ def _parse_claims(self, response: str, doc_path: Path) -> list[dict]:
346
+ """Parse the LLM response into a list of claim dicts."""
347
+ text = response.strip()
348
+
349
+ # Strip markdown code fences if present
350
+ if text.startswith("```"):
351
+ lines = text.splitlines()
352
+ if len(lines) >= 2:
353
+ lines = lines[1:] if lines[0].startswith("```") else lines
354
+ if lines and lines[-1].strip().startswith("```"):
355
+ lines = lines[:-1]
356
+ text = "\n".join(lines).strip()
357
+
358
+ # Try to extract JSON array if embedded in other text
359
+ start = text.find("[")
360
+ end = text.rfind("]")
361
+ if start >= 0 and end > start:
362
+ text = text[start : end + 1]
363
+
364
+ try:
365
+ claims = json.loads(text)
366
+ if not isinstance(claims, list):
367
+ logger.warning(f"LLM returned non-array for {doc_path.name}")
368
+ return []
369
+ except (json.JSONDecodeError, ValueError) as e:
370
+ logger.warning(f"Failed to parse LLM claims from {doc_path.name}: {e}")
371
+ return []
372
+
373
+ # Validate structure
374
+ valid = []
375
+ for c in claims:
376
+ if isinstance(c, dict) and "claim" in c and "category" in c:
377
+ if not isinstance(c.get("evidence_hints"), list):
378
+ c["evidence_hints"] = []
379
+ valid.append(c)
380
+
381
+ return valid
382
+
383
+
384
+ # ── Brain integration helper ──────────────────────────────────────────────
385
+
386
+
387
+ def verify_claims_against_code(
388
+ claims: list[dict],
389
+ file_infos: list,
390
+ routes: list,
391
+ symbol_graph=None,
392
+ import_graph=None,
393
+ config: dict | None = None,
394
+ ) -> list[dict]:
395
+ """Cross-reference each claim's evidence_hints against the codebase.
396
+
397
+ Returns the claims list with verified=True/False and evidence populated.
398
+ """
399
+ if not claims:
400
+ return claims
401
+
402
+ # Build lookup sets from scan results
403
+ route_set: set[str] = set()
404
+ for r in routes:
405
+ if hasattr(r, "method"):
406
+ route_set.add(r.method.upper() + " " + r.path)
407
+ route_set.add((r.method.upper() + " " + r.path).lower())
408
+ route_set.add(r.path.lower())
409
+ elif isinstance(r, dict):
410
+ method = r.get("method", "GET")
411
+ path = r.get("path", "/")
412
+ route_set.add(method.upper() + " " + path)
413
+ route_set.add((method.upper() + " " + path).lower())
414
+ route_set.add(path.lower())
415
+
416
+ file_paths: set[str] = set()
417
+ file_names: set[str] = set()
418
+ func_names: set[str] = set()
419
+ class_names: set[str] = set()
420
+ import_names: set[str] = set()
421
+ symbols: set[str] = set()
422
+
423
+ for fi in file_infos:
424
+ if hasattr(fi, "path"):
425
+ rel = fi.path
426
+ elif isinstance(fi, dict):
427
+ rel = fi.get("path", "")
428
+ else:
429
+ continue
430
+ file_paths.add(rel.lower())
431
+ file_names.add(Path(rel).stem.lower())
432
+ funcs = fi.functions if hasattr(fi, "functions") else fi.get("functions", [])
433
+ classes = fi.classes if hasattr(fi, "classes") else fi.get("classes", [])
434
+ imports = fi.imports if hasattr(fi, "imports") else fi.get("imports", [])
435
+ if funcs:
436
+ func_names.update(
437
+ f.name.lower() if hasattr(f, "name") else f.get("name", "").lower() for f in funcs
438
+ )
439
+ if classes:
440
+ class_names.update(
441
+ c.name.lower() if hasattr(c, "name") else c.get("name", "").lower() for c in classes
442
+ )
443
+ if imports:
444
+ import_names.update(
445
+ i.source.lower() if hasattr(i, "source") else i.get("source", "").lower()
446
+ for i in imports
447
+ )
448
+
449
+ if symbol_graph:
450
+ try:
451
+ for sym in symbol_graph.get_all_symbols():
452
+ symbols.add(sym.name.lower())
453
+ symbols.add(sym.qualified_name.lower())
454
+ except Exception as e:
455
+ _log.warning("verify_claims_against_code failed: %s", e)
456
+
457
+ cli_flags: set[str] = set()
458
+ if config:
459
+ cli_parser = config.get("_cli_parser")
460
+ if cli_parser:
461
+ for action in cli_parser._actions:
462
+ for opt in action.option_strings:
463
+ cli_flags.add(opt.lower())
464
+
465
+ # Build import graph node set for dependency verification
466
+ ig_nodes: set[str] = set()
467
+ if import_graph:
468
+ try:
469
+ if hasattr(import_graph, "graph") and hasattr(import_graph.graph, "nodes"):
470
+ ig_nodes = set(import_graph.graph.nodes())
471
+ elif hasattr(import_graph, "nodes"):
472
+ ig_nodes = set(import_graph.nodes())
473
+ except Exception as e:
474
+ _log.warning("verify_claims_against_code failed: %s", e)
475
+
476
+ for claim in claims:
477
+ hints = claim.get("evidence_hints", [])
478
+ evidence: list[str] = []
479
+
480
+ for hint in hints:
481
+ hint_lower = hint.lower()
482
+
483
+ # Route check
484
+ if hint_lower.startswith("route:"):
485
+ target = hint_lower.replace("route:", "").strip()
486
+ if target in route_set:
487
+ evidence.append(f"matched_route:{hint}")
488
+
489
+ # Function/class/symbol check
490
+ elif hint_lower.startswith("function:"):
491
+ target = hint_lower.replace("function:", "").strip()
492
+ if target in func_names:
493
+ evidence.append(f"matched_function:{hint}")
494
+
495
+ elif hint_lower.startswith("class:"):
496
+ target = hint_lower.replace("class:", "").strip()
497
+ if target in class_names:
498
+ evidence.append(f"matched_class:{hint}")
499
+
500
+ elif hint_lower.startswith("symbol:"):
501
+ target = hint_lower.replace("symbol:", "").strip()
502
+ if target in symbols:
503
+ evidence.append(f"matched_symbol:{hint}")
504
+
505
+ # CLI flag check
506
+ elif hint_lower.startswith("cli:"):
507
+ target = hint_lower.replace("cli:", "").strip()
508
+ if target in cli_flags:
509
+ evidence.append(f"matched_cli:{hint}")
510
+
511
+ # Import/package check (now also uses import_graph)
512
+ elif hint_lower.startswith("import:"):
513
+ target = hint_lower.replace("import:", "").strip()
514
+ if target in import_names:
515
+ evidence.append(f"matched_import:{hint}")
516
+ elif ig_nodes:
517
+ for node in ig_nodes:
518
+ if target in node.lower():
519
+ evidence.append(f"matched_import_graph:{hint}")
520
+ break
521
+
522
+ elif hint_lower.startswith("package:"):
523
+ target = hint_lower.replace("package:", "").strip()
524
+ if target in import_names or target in file_names:
525
+ evidence.append(f"matched_package:{hint}")
526
+ elif ig_nodes:
527
+ for node in ig_nodes:
528
+ if target in node.lower():
529
+ evidence.append(f"matched_import_graph:{hint}")
530
+ break
531
+
532
+ # Config key check
533
+ elif hint_lower.startswith("config_key:"):
534
+ target = hint_lower.replace("config_key:", "").strip()
535
+ if config and target in str(config).lower():
536
+ evidence.append(f"matched_config:{hint}")
537
+
538
+ # File match check
539
+ elif hint_lower.startswith("file_match:"):
540
+ target = hint_lower.replace("file_match:", "").strip()
541
+ if any(target in fp for fp in file_paths):
542
+ evidence.append(f"matched_file:{hint}")
543
+ elif ig_nodes:
544
+ for node in ig_nodes:
545
+ if target in node.lower():
546
+ evidence.append(f"matched_file_via_import_graph:{hint}")
547
+ break
548
+
549
+ # Dependency verification via import graph
550
+ elif hint_lower.startswith("dependency:"):
551
+ target = hint_lower.replace("dependency:", "").strip()
552
+ if ig_nodes:
553
+ for node in ig_nodes:
554
+ if target in node.lower():
555
+ evidence.append(f"matched_import_graph_dep:{hint}")
556
+ break
557
+ if target in import_names:
558
+ evidence.append(f"matched_import:{hint}")
559
+
560
+ # General keyword search (fallback)
561
+ else:
562
+ if hint_lower in func_names or hint_lower in class_names or hint_lower in symbols:
563
+ evidence.append(f"matched_symbol:{hint}")
564
+ elif ig_nodes:
565
+ for node in ig_nodes:
566
+ if hint_lower in node.lower():
567
+ evidence.append(f"matched_import_graph:{hint}")
568
+ break
569
+
570
+ claim["verified"] = len(evidence) > 0
571
+ claim["evidence"] = evidence
572
+
573
+ return claims