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
patchi/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.7.5"
patchi/__main__.py ADDED
@@ -0,0 +1,13 @@
1
+ """Allow running patchi as a module: python -m patchi"""
2
+
3
+ import sys
4
+
5
+
6
+ def main():
7
+ from patchi.cli.main import main as cli_main
8
+
9
+ return cli_main()
10
+
11
+
12
+ if __name__ == "__main__":
13
+ sys.exit(main() or 0)
patchi/cli/__init__.py ADDED
File without changes
@@ -0,0 +1,235 @@
1
+ """
2
+ Command Families — organized command discovery for Patchi CLI.
3
+
4
+ Groups commands into families. `p <family>` runs the default action.
5
+ `p <family> commands` shows all subcommands in that family.
6
+
7
+ Usage:
8
+ from patchi.cli.command_families import FAMILIES, get_family, list_families
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass, field
14
+
15
+
16
+ @dataclass
17
+ class CommandFamily:
18
+ """A family of related commands."""
19
+
20
+ name: str
21
+ description: str
22
+ default_command: str | None = None # What `p <family>` runs
23
+ commands: list[str] = field(default_factory=list) # Subcommands in this family
24
+ agent_domain: str | None = None # Linked AgentDomain for agent discovery
25
+
26
+
27
+ FAMILIES: dict[str, CommandFamily] = {
28
+ "test": CommandFamily(
29
+ name="test",
30
+ description="Testing — unit, e2e, visual, stress, regression",
31
+ default_command="test",
32
+ commands=["unit", "e2e", "visual", "stress", "generate", "report", "config", "regression"],
33
+ agent_domain="testing",
34
+ ),
35
+ "scan": CommandFamily(
36
+ name="scan",
37
+ description="Scanning — full analysis, security, structure",
38
+ default_command="scan",
39
+ commands=["deep", "security", "structure", "since", "contract", "changed"],
40
+ agent_domain="code_quality",
41
+ ),
42
+ "fix": CommandFamily(
43
+ name="fix",
44
+ description="Fixing — propose and apply fixes",
45
+ default_command="fix",
46
+ commands=["auto", "review", "patch", "undo", "redo", "rollback"],
47
+ agent_domain="integration",
48
+ ),
49
+ "ready": CommandFamily(
50
+ name="ready",
51
+ description="Ship readiness — is my code ready to ship?",
52
+ default_command="ready",
53
+ commands=["quick", "ci", "json", "baseline"],
54
+ agent_domain="testing",
55
+ ),
56
+ "security": CommandFamily(
57
+ name="security",
58
+ description="Security — vulnerability scanning and hardening",
59
+ default_command="security",
60
+ commands=["chains", "findings", "rules", "deps", "assure", "charter", "restrict"],
61
+ agent_domain="security",
62
+ ),
63
+ "web": CommandFamily(
64
+ name="web",
65
+ description="Web dashboard — launch and manage the web UI",
66
+ default_command="web",
67
+ commands=["host", "port", "open", "project"],
68
+ agent_domain="infrastructure",
69
+ ),
70
+ "status": CommandFamily(
71
+ name="status",
72
+ description="Status — project health and monitoring",
73
+ default_command="status",
74
+ commands=["doctor", "cockpit", "trend", "heatmap"],
75
+ agent_domain="infrastructure",
76
+ ),
77
+ "audit": CommandFamily(
78
+ name="audit",
79
+ description="Auditing — full project audit",
80
+ default_command="audit",
81
+ commands=["quick", "plan", "intent", "html", "json"],
82
+ agent_domain="code_quality",
83
+ ),
84
+ "config": CommandFamily(
85
+ name="config",
86
+ description="Configuration — settings and preferences",
87
+ default_command="config",
88
+ commands=["key", "model", "settings", "brain", "access", "link", "notify", "rules", "plugins"],
89
+ agent_domain="infrastructure",
90
+ ),
91
+ "agent": CommandFamily(
92
+ name="agent",
93
+ description="Agents — list, run, and manage agents",
94
+ default_command="agents",
95
+ commands=["list", "run", "stats", "reset"],
96
+ agent_domain="infrastructure",
97
+ ),
98
+ "git": CommandFamily(
99
+ name="git",
100
+ description="Git — blame and log integration",
101
+ default_command="git",
102
+ commands=["blame", "log"],
103
+ agent_domain="infrastructure",
104
+ ),
105
+ "maintain": CommandFamily(
106
+ name="maintain",
107
+ description="Maintenance — updates and cleanup",
108
+ default_command="update",
109
+ commands=["cleanup", "doctor"],
110
+ agent_domain="infrastructure",
111
+ ),
112
+ "hosted": CommandFamily(
113
+ name="hosted",
114
+ description="Hosted — production monitoring",
115
+ default_command="hosted",
116
+ commands=["init", "worker", "daemon", "guard", "status", "logs", "token", "block"],
117
+ agent_domain="infrastructure",
118
+ ),
119
+ "ai": CommandFamily(
120
+ name="ai",
121
+ description="AI — chat and model configuration",
122
+ default_command="ai",
123
+ commands=["status", "test", "add", "remove", "profiles"],
124
+ agent_domain="infrastructure",
125
+ ),
126
+ "report": CommandFamily(
127
+ name="report",
128
+ description="Reporting — generate and export reports",
129
+ default_command="report",
130
+ commands=["export", "weekly"],
131
+ agent_domain="testing",
132
+ ),
133
+ "init": CommandFamily(
134
+ name="init",
135
+ description="Initialize — project setup",
136
+ default_command="init",
137
+ commands=["auto", "no-logo"],
138
+ agent_domain="infrastructure",
139
+ ),
140
+ "queue": CommandFamily(
141
+ name="queue",
142
+ description="Queue — manage the scan queue",
143
+ default_command="queue",
144
+ commands=["pause", "resume", "skip", "mode"],
145
+ agent_domain="infrastructure",
146
+ ),
147
+ "plan": CommandFamily(
148
+ name="plan",
149
+ description="Planning — plan changes",
150
+ default_command="plan",
151
+ commands=["format", "missing-import"],
152
+ agent_domain="infrastructure",
153
+ ),
154
+ "verify": CommandFamily(
155
+ name="verify",
156
+ description="Verify — independent re-run of tests",
157
+ default_command="verify",
158
+ commands=[],
159
+ agent_domain="testing",
160
+ ),
161
+ "help": CommandFamily(
162
+ name="help",
163
+ description="Help — show help and documentation",
164
+ default_command="help",
165
+ commands=["all", "group", "json", "write-md"],
166
+ agent_domain="infrastructure",
167
+ ),
168
+ "watch": CommandFamily(
169
+ name="watch",
170
+ description="Watch — auto-scan on file saves",
171
+ default_command="watch",
172
+ commands=[],
173
+ agent_domain="infrastructure",
174
+ ),
175
+ "doctor": CommandFamily(
176
+ name="doctor",
177
+ description="Doctor — system health check",
178
+ default_command="doctor",
179
+ commands=[],
180
+ agent_domain="infrastructure",
181
+ ),
182
+ "trend": CommandFamily(
183
+ name="trend",
184
+ description="Trend — historical analysis",
185
+ default_command="trend",
186
+ commands=[],
187
+ agent_domain="testing",
188
+ ),
189
+ "memory": CommandFamily(
190
+ name="memory",
191
+ description="Memory — persistent context and learning",
192
+ default_command="memory",
193
+ commands=[],
194
+ agent_domain="infrastructure",
195
+ ),
196
+ }
197
+
198
+
199
+ def get_family(name: str) -> CommandFamily | None:
200
+ """Get a family by name."""
201
+ return FAMILIES.get(name)
202
+
203
+
204
+ def list_families() -> list[CommandFamily]:
205
+ """List all families in order."""
206
+ return list(FAMILIES.values())
207
+
208
+
209
+ def find_family_for_command(command: str) -> CommandFamily | None:
210
+ """Find which family a command belongs to."""
211
+ for family in FAMILIES.values():
212
+ if command in family.commands or command == family.default_command:
213
+ return family
214
+ return None
215
+
216
+
217
+ def format_family_help(family: CommandFamily) -> str:
218
+ """Format help text for a family."""
219
+ lines = [f"p {family.name} — {family.description}"]
220
+ if family.commands:
221
+ lines.append("")
222
+ lines.append("Subcommands:")
223
+ for cmd in family.commands:
224
+ lines.append(f" p {family.name} {cmd}")
225
+ return "\n".join(lines)
226
+
227
+
228
+ def format_all_families() -> str:
229
+ """Format help text for all families."""
230
+ lines = ["Patchi Command Families", "=" * 40, ""]
231
+ for family in FAMILIES.values():
232
+ lines.append(f"p {family.name:12} — {family.description}")
233
+ lines.append("")
234
+ lines.append("Use 'p <family> commands' to see all subcommands in a family.")
235
+ return "\n".join(lines)
File without changes
@@ -0,0 +1,65 @@
1
+ """
2
+ `p access` — Manage dev access tokens.
3
+ add [--env-var VAR] <name> | list | remove <name>
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ from patchi.cli.console import con
11
+ from patchi.core import memory as mem
12
+
13
+
14
+ def run(
15
+ access_cmd: str | None = None,
16
+ name: str | None = None,
17
+ env_var: str | None = None,
18
+ root: Path | None = None,
19
+ ) -> None:
20
+ if access_cmd == "add":
21
+ run_add(name, env_var, root)
22
+ elif access_cmd == "list":
23
+ run_list(root)
24
+ elif access_cmd == "remove":
25
+ run_remove(name, root)
26
+
27
+
28
+ def run_add(name: str | None = None, env_var: str | None = None, root: Path | None = None) -> None:
29
+ if not name:
30
+ con.print("[red]Usage: p access add <name> [--env-var VAR][/red]")
31
+ return
32
+ r = root or _require_root()
33
+ mem.save_token(name, env_var or name.upper(), r) # type: ignore[arg-type]
34
+ con.print(f"[#4ADE80]✓[/#4ADE80] Token [bold]{name}[/bold] saved.")
35
+
36
+
37
+ def run_list(root: Path | None = None) -> None:
38
+ r = root or _require_root()
39
+ tokens = mem.list_tokens(r)
40
+ if not tokens:
41
+ con.print("[dim]No dev access tokens configured.[/dim]")
42
+ for t in tokens:
43
+ con.print(f" [bold]{t['name']}[/bold] [dim]env: {t['env_var']}[/dim]")
44
+
45
+
46
+ def run_remove(name: str | None, root: Path | None = None) -> None:
47
+ if not name:
48
+ con.print("[red]Usage: p access remove <name>[/red]")
49
+ return
50
+ r = root or _require_root()
51
+ ok = mem.remove_token(name, r)
52
+ if ok:
53
+ con.print(f"[#4ADE80]✓[/#4ADE80] Token [bold]{name}[/bold] removed.")
54
+ else:
55
+ con.print(f"[yellow]Token {name!r} not found.[/yellow]")
56
+
57
+
58
+ def _require_root() -> Path:
59
+ try:
60
+ from patchi.core.config import require_project_root
61
+
62
+ return require_project_root()
63
+ except RuntimeError as e:
64
+ con.print(f"[red]{e}[/red]")
65
+ raise SystemExit(1) from e
@@ -0,0 +1,273 @@
1
+ """
2
+ `p agent-stats` — Unified agent profiling and learning brain state.
3
+
4
+ Combines the original `p profile` (agent profiler stats) and `p learning`
5
+ (learning brain accept/reject patterns) into a single command.
6
+
7
+ Usage:
8
+ p agent-stats — show all agent profiles + learning state
9
+ p agent-stats auth_agent — show details for one agent
10
+ p agent-stats --json — machine-readable output
11
+ p agent-stats --profile — show only agent profiler stats
12
+ p agent-stats --learning — show only learning brain state
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import logging
19
+ from pathlib import Path
20
+
21
+ from rich.table import Table
22
+
23
+ from patchi.cli.console import con
24
+ from patchi.core.config import require_project_root
25
+
26
+ _log = logging.getLogger("patchi.cli.agent_stats_cmd")
27
+
28
+
29
+ def run(
30
+ agent_name: str | None = None,
31
+ json_output: bool = False,
32
+ root: Path | None = None,
33
+ profile: bool = False,
34
+ learning: bool = False,
35
+ ) -> None:
36
+ """Entry point for `p agent-stats`."""
37
+ try:
38
+ r = root or require_project_root()
39
+ except RuntimeError as e:
40
+ con.print(f"[red]{e}[/red]")
41
+ return
42
+
43
+ # Specific agent detail
44
+ if agent_name:
45
+ _show_agent_detail(r, agent_name)
46
+ return
47
+
48
+ # Collect data from both sources
49
+ profile_data = _get_profile_data(r)
50
+ learning_data = _get_learning_data(r)
51
+
52
+ if json_output:
53
+ payload = {
54
+ "profile": profile_data,
55
+ "learning": learning_data,
56
+ }
57
+ con.print(json.dumps(payload, indent=2, default=str))
58
+ return
59
+
60
+ con.print()
61
+ con.print("[bold #C8621A]── Agent Stats ──[/bold #C8621A]")
62
+ con.print()
63
+
64
+ # Show profiler stats (unless --learning only)
65
+ if not learning:
66
+ _show_profile_section(profile_data)
67
+
68
+ # Show learning state (unless --profile only)
69
+ if not profile:
70
+ _show_learning_section(learning_data)
71
+
72
+ con.print()
73
+
74
+
75
+ def _get_profile_data(root: Path) -> dict:
76
+ """Get agent profiler data."""
77
+ try:
78
+ from patchi.core.ai.agent_profiler import get_all_profiles, get_profile_summary
79
+
80
+ profiles = get_all_profiles(root)
81
+ summary = get_profile_summary(root)
82
+ return {
83
+ "summary": summary,
84
+ "profiles": {k: v.to_dict() for k, v in profiles.items()},
85
+ }
86
+ except ImportError:
87
+ return {"summary": {}, "profiles": {}}
88
+
89
+
90
+ def _get_learning_data(root: Path) -> dict:
91
+ """Get learning brain data."""
92
+ try:
93
+ from patchi.core.brain.learning import get_summary
94
+
95
+ return get_summary(root)
96
+ except ImportError:
97
+ return {}
98
+
99
+
100
+ def _show_profile_section(data: dict) -> None:
101
+ """Show the agent profiler section."""
102
+ summary = data.get("summary", {})
103
+ profiles = data.get("profiles", {})
104
+
105
+ if not profiles:
106
+ con.print("[dim]No agent profiles yet. Run a scan to start collecting data.[/dim]")
107
+ con.print()
108
+ return
109
+
110
+ con.print("[bold]Agent Profiler[/bold]")
111
+ con.print()
112
+
113
+ # Summary line
114
+ con.print(
115
+ f" [bold]{summary.get('agents', 0)}[/bold] agents · "
116
+ f"[bold]{summary.get('total_runs', 0)}[/bold] total runs · "
117
+ f"[bold]${summary.get('total_cost', 0):.4f}[/bold] total cost · "
118
+ f"[bold]{summary.get('total_findings', 0)}[/bold] findings · "
119
+ f"accuracy [bold]{summary.get('avg_accuracy', 0) * 100:.1f}%[/bold]"
120
+ )
121
+ con.print()
122
+
123
+ # Table
124
+ table = Table(show_header=True, header_style="bold #C8621A", box=None)
125
+ table.add_column("Agent", style="bold")
126
+ table.add_column("Runs", justify="right")
127
+ table.add_column("P50", justify="right")
128
+ table.add_column("P95", justify="right")
129
+ table.add_column("Findings", justify="right")
130
+ table.add_column("Accuracy", justify="right")
131
+ table.add_column("Cost", justify="right")
132
+ table.add_column("Model")
133
+
134
+ for name, p in sorted(profiles.items(), key=lambda x: -x[1].get("run_count", 0)):
135
+ run_count = p.get("run_count", 0)
136
+ p50 = p.get("p50_wall_ms", 0)
137
+ p95 = p.get("p95_wall_ms", 0)
138
+ findings = p.get("total_findings", 0)
139
+ accuracy = p.get("accuracy", 0)
140
+ cost = p.get("total_cost_usd", 0)
141
+ model = p.get("most_used_model", "—")
142
+
143
+ acc_str = f"{accuracy * 100:.0f}%" if run_count >= 3 else "n/a"
144
+ acc_color = (
145
+ ("green" if accuracy >= 0.8 else "yellow" if accuracy >= 0.5 else "red")
146
+ if run_count >= 3
147
+ else "dim"
148
+ )
149
+
150
+ p95_str = f"{p95:.0f}ms"
151
+ p95_color = "red" if p95 > 5000 else "yellow" if p95 > 2000 else ""
152
+
153
+ table.add_row(
154
+ name,
155
+ str(run_count),
156
+ f"{p50:.0f}ms",
157
+ f"[{p95_color}]{p95_str}[/{p95_color}]" if p95_color else p95_str,
158
+ str(findings),
159
+ f"[{acc_color}]{acc_str}[/{acc_color}]",
160
+ f"${cost:.4f}",
161
+ model or "—",
162
+ )
163
+
164
+ con.print(table)
165
+
166
+ # Slowest agents
167
+ slowest = summary.get("slowest", [])
168
+ if slowest:
169
+ con.print()
170
+ con.print(" [bold]Slowest agents (P95):[/bold]")
171
+ for s in slowest[:5]:
172
+ con.print(f" {s.get('agent', '?')}: [dim]{s.get('p95_ms', 0)}ms[/dim]")
173
+ con.print()
174
+
175
+
176
+ def _show_learning_section(data: dict) -> None:
177
+ """Show the learning brain section."""
178
+ if not data:
179
+ con.print("[dim]No learning data yet. Run scans and accept/reject findings to build patterns.[/dim]")
180
+ con.print()
181
+ return
182
+
183
+ con.print("[bold]Learning Brain[/bold]")
184
+ con.print()
185
+
186
+ # Acceptances
187
+ acceptances = data.get("acceptances", {})
188
+ if acceptances:
189
+ con.print("[bold green]Accepted Findings[/bold green]")
190
+ table = Table(show_header=True, header_style="bold", box=None)
191
+ table.add_column("Finding Type")
192
+ table.add_column("Count", justify="right")
193
+ for ftype, count in sorted(acceptances.items(), key=lambda x: -x[1]):
194
+ table.add_row(ftype, str(count))
195
+ con.print(table)
196
+ con.print()
197
+ else:
198
+ con.print("[dim]No acceptance data yet.[/dim]")
199
+ con.print()
200
+
201
+ # Rejections
202
+ rejections = data.get("rejections", {})
203
+ if rejections:
204
+ con.print("[bold red]Rejected Findings[/bold red]")
205
+ table = Table(show_header=True, header_style="bold", box=None)
206
+ table.add_column("Finding Type")
207
+ table.add_column("Count", justify="right")
208
+ for ftype, count in sorted(rejections.items(), key=lambda x: -x[1]):
209
+ table.add_row(ftype, str(count))
210
+ con.print(table)
211
+ con.print()
212
+ else:
213
+ con.print("[dim]No rejection data yet.[/dim]")
214
+ con.print()
215
+
216
+ # Agent trust
217
+ trust = data.get("agent_trust", {})
218
+ if trust:
219
+ con.print("[bold]Agent Trust Scores[/bold]")
220
+ table = Table(show_header=True, header_style="bold", box=None)
221
+ table.add_column("Agent")
222
+ table.add_column("Trust", justify="right")
223
+ table.add_column("Bar")
224
+ for agent, score in sorted(trust.items(), key=lambda x: -x[1]):
225
+ bar_len = int(score * 20)
226
+ bar = "#" * bar_len + "." * (20 - bar_len)
227
+ color = "green" if score >= 0.7 else "yellow" if score >= 0.4 else "red"
228
+ table.add_row(
229
+ agent, f"[{color}]{score * 100:.0f}%[/{color}]", f"[{color}]{bar}[/{color}]"
230
+ )
231
+ con.print(table)
232
+ con.print()
233
+ else:
234
+ con.print("[dim]No agent trust data yet.[/dim]")
235
+ con.print()
236
+
237
+ # Skip types
238
+ skip = data.get("skip_types", [])
239
+ if skip:
240
+ con.print("[bold yellow]Auto-Skipped Types[/bold yellow]")
241
+ for ftype in skip:
242
+ con.print(f" [yellow]* {ftype}[/yellow]")
243
+ con.print()
244
+
245
+
246
+ def _show_agent_detail(root: Path, agent_name: str) -> None:
247
+ """Show detailed profile for one agent."""
248
+ try:
249
+ from patchi.core.ai.agent_profiler import get_profile
250
+ except ImportError:
251
+ con.print("[red]Agent profiler not available.[/red]")
252
+ return
253
+
254
+ profile = get_profile(root, agent_name)
255
+ if profile.run_count == 0:
256
+ con.print(f"[dim]No data for agent '{agent_name}'.[/dim]")
257
+ return
258
+
259
+ con.print()
260
+ con.print(f"[bold #C8621A]── Agent Profile: {agent_name} ──[/bold #C8621A]")
261
+ con.print()
262
+ con.print(f" Runs: [bold]{profile.run_count}[/bold]")
263
+ con.print(f" P50 latency: [bold]{profile.p50_wall_ms:.0f}ms[/bold]")
264
+ con.print(f" P95 latency: [bold]{profile.p95_wall_ms:.0f}ms[/bold]")
265
+ con.print(f" Total wall: {profile.total_wall_ms / 1000:.1f}s")
266
+ con.print(f" Findings: [bold]{profile.total_findings}[/bold]")
267
+ con.print(f" Accepted: [green]{profile.total_accepted}[/green]")
268
+ con.print(f" Rejected: [red]{profile.total_rejected}[/red]")
269
+ if profile.run_count >= 3:
270
+ con.print(f" Accuracy: [bold]{profile.accuracy * 100:.1f}%[/bold]")
271
+ con.print(f" Avg/run: {profile.avg_findings_per_run:.1f} findings")
272
+ con.print(f" Total cost: [bold]${profile.total_cost_usd:.4f}[/bold]")
273
+ con.print()