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,208 @@
1
+ """
2
+ `p cockpit` — the live cockpit (a single glanceable dashboard for a coding session).
3
+
4
+ p cockpit live dashboard, refreshes as you save files
5
+ p cockpit --area src/api scope the fix list to a subtree
6
+ p cockpit --once render one frame and exit (CI / smoke test)
7
+ p cockpit --interval 30 seconds between full health/drift refreshes
8
+
9
+ It is a *view layer only*: every number comes from signals Patchi already
10
+ computes (health score, Plan-vs-Built drift, Prioritized Fix List, blast radius,
11
+ secrets sweep). No new analysis lives here.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import time
17
+ from pathlib import Path
18
+
19
+ from rich.console import Group
20
+ from rich.layout import Layout
21
+ from rich.live import Live
22
+ from rich.panel import Panel
23
+ from rich.table import Table
24
+ from rich.text import Text
25
+
26
+ from patchi.cli.console import con
27
+ from patchi.core.brain import cockpit as ck
28
+ from patchi.core.config import require_project_root
29
+
30
+ _FIX_LABEL = {
31
+ "charter_violation": ("Charter", "#FF4D6D"),
32
+ "signature_callers": ("Breaking", "#FF8C42"),
33
+ "missing_import": ("Missing import", "#FACC15"),
34
+ "dead_code": ("Dead code", "#9AA0A6"),
35
+ "unused_import": ("Unused import", "#9AA0A6"),
36
+ "format": ("Format", "#86EFAC"),
37
+ }
38
+ _LEVEL_STYLE = {"info": "#86EFAC", "warn": "#FACC15", "crit": "#FF4D6D"}
39
+
40
+
41
+ # ── Panels ───────────────────────────────────────────────────────────────────────
42
+
43
+
44
+ def _header(state: ck.CockpitState) -> Panel:
45
+ t = Table.grid(expand=True)
46
+ t.add_column(justify="left")
47
+ t.add_column(justify="right")
48
+
49
+ if state.health_total is None:
50
+ health = Text("Health — (run `p scan`)", style="#9AA0A6")
51
+ else:
52
+ health = Text.assemble(
53
+ ("Health ", "#F2EDD6"),
54
+ (f"{state.health_total}", f"bold {state.health_color}"),
55
+ (f" {state.health_grade}", state.health_color),
56
+ )
57
+
58
+ drift = state.drift or {}
59
+ if not drift.get("has_plan"):
60
+ plan = Text("Plan no baseline (`p audit save`)", style="#9AA0A6")
61
+ elif drift.get("clean"):
62
+ plan = Text("Plan ✓ ON PLAN", style="bold #86EFAC")
63
+ else:
64
+ sd = drift.get("scope_diff", {}) or {}
65
+ delta = sd.get("added_count", 0) + sd.get("removed_count", 0) + sd.get("modified_count", 0)
66
+ nf = drift.get("new_findings", 0)
67
+ plan = Text.assemble(
68
+ ("Plan ⚠ DRIFT", "bold #FF4D6D"),
69
+ (f" ({delta} file Δ, +{nf} findings)", "#FF8C42"),
70
+ )
71
+
72
+ t.add_row(health, plan)
73
+ return Panel(t, border_style="#2A3D28", padding=(0, 1))
74
+
75
+
76
+ def _fix_panel(state: ck.CockpitState) -> Panel:
77
+ if not state.fixes:
78
+ if state.fixes_computing or state.fixes_at == 0:
79
+ body = Text("Computing fix list…", style="#FACC15")
80
+ else:
81
+ body = Text("No fixes proposed — clean, or run `p scan` first.", style="#9AA0A6")
82
+ return Panel(
83
+ body, title="[bold #C8621A]Prioritized Fix List[/bold #C8621A]", border_style="#2A3D28"
84
+ )
85
+
86
+ tbl = Table(show_header=True, header_style="dim", box=None, expand=True, pad_edge=False)
87
+ tbl.add_column("#", justify="right", width=2, style="dim")
88
+ tbl.add_column("Type", width=14)
89
+ tbl.add_column("Where", overflow="ellipsis", no_wrap=True)
90
+ tbl.add_column("", justify="right", width=6)
91
+
92
+ for i, f in enumerate(state.fixes[:12], 1):
93
+ label, color = _FIX_LABEL.get(f["fix_type"], (f["fix_type"], "#F2EDD6"))
94
+ where = f["file"] + (f" · {f['name']}" if f["name"] else "")
95
+ tag = Text("safe", style="#86EFAC") if f["safe"] else Text("review", style="#FF8C42")
96
+ tbl.add_row(str(i), Text(label, style=color), where, tag)
97
+
98
+ extra = len(state.fixes) - 12
99
+ title = f"[bold #C8621A]Prioritized Fix List[/bold #C8621A] [dim]({len(state.fixes)})[/dim]"
100
+ grp = Group(tbl, Text(f" +{extra} more…", style="dim")) if extra > 0 else tbl
101
+ return Panel(grp, title=title, border_style="#2A3D28")
102
+
103
+
104
+ def _blast_panel(state: ck.CockpitState) -> Panel:
105
+ if not state.last_file:
106
+ body = Text("Save a file to see its blast radius.", style="#9AA0A6")
107
+ return Panel(
108
+ body, title="[bold #C8621A]Blast Radius[/bold #C8621A]", border_style="#2A3D28"
109
+ )
110
+
111
+ lines: list = [Text(state.blast_summary or "", style="#F2EDD6"), Text("")]
112
+ if state.blast_affected:
113
+ lines.append(Text("Directly affected:", style="bold #F2EDD6"))
114
+ lines += [Text(f" • {n}", style="#F2EDD6") for n in state.blast_affected]
115
+ if state.blast_impacted:
116
+ lines.append(Text("Downstream (blast):", style="bold #FF8C42"))
117
+ lines += [Text(f" • {n}", style="#FF8C42") for n in state.blast_impacted]
118
+ elif state.blast_affected:
119
+ lines.append(Text("No downstream dependents.", style="dim"))
120
+
121
+ title = f"[bold #C8621A]Blast Radius[/bold #C8621A] [dim]{state.last_file}[/dim]"
122
+ return Panel(Group(*lines), title=title, border_style="#2A3D28")
123
+
124
+
125
+ def _events_panel(state: ck.CockpitState) -> Panel:
126
+ if not state.events:
127
+ body = Text("Watching…", style="#9AA0A6")
128
+ else:
129
+ rows = []
130
+ for ev in list(state.events)[-8:]:
131
+ style = _LEVEL_STYLE.get(ev.level, "#F2EDD6")
132
+ mark = {"info": "·", "warn": "▲", "crit": "✖"}.get(ev.level, "·")
133
+ rows.append(
134
+ Text.assemble(
135
+ (f"{ev.stamp()} ", "dim"),
136
+ (f"{mark} ", style),
137
+ (ev.text, style),
138
+ )
139
+ )
140
+ body = Group(*rows)
141
+ sec = f" · [#FF4D6D]{len(state.secrets)} secret(s)[/#FF4D6D]" if state.secrets else ""
142
+ return Panel(body, title=f"[bold #C8621A]Events[/bold #C8621A]{sec}", border_style="#2A3D28")
143
+
144
+
145
+ # ── Layout ─────────────────────────────────────────────────────────────────────
146
+
147
+
148
+ def _build_layout() -> Layout:
149
+ root = Layout()
150
+ root.split_column(
151
+ Layout(name="header", size=3),
152
+ Layout(name="body", ratio=1),
153
+ Layout(name="events", size=10),
154
+ )
155
+ root["body"].split_row(Layout(name="fixes"), Layout(name="blast"))
156
+ return root
157
+
158
+
159
+ def _render(layout: Layout, state: ck.CockpitState) -> None:
160
+ layout["header"].update(_header(state))
161
+ layout["fixes"].update(_fix_panel(state))
162
+ layout["blast"].update(_blast_panel(state))
163
+ layout["events"].update(_events_panel(state))
164
+
165
+
166
+ # ── Entry point ────────────────────────────────────────────────────────────────
167
+
168
+
169
+ def run(
170
+ root: Path | None = None,
171
+ interval: float = 15.0,
172
+ area: str | None = None,
173
+ once: bool = False,
174
+ poll: float = 1.0,
175
+ scan_secrets: bool = False,
176
+ ) -> None:
177
+ try:
178
+ r = root or require_project_root()
179
+ except RuntimeError as e:
180
+ con.print(f"[red]{e}[/red]")
181
+ return
182
+
183
+ state = ck.gather_full(r, area=area, scan_secrets=scan_secrets, fixes_sync=once)
184
+ layout = _build_layout()
185
+ _render(layout, state)
186
+
187
+ if once:
188
+ con.print(layout)
189
+ return
190
+
191
+ watcher = ck.SourceWatcher(r)
192
+ con.print("[dim]Cockpit live — press Ctrl+C to exit.[/dim]")
193
+ try:
194
+ with Live(layout, console=con, refresh_per_second=4, screen=True):
195
+ last_full = time.time()
196
+ while True:
197
+ changed = watcher.poll()
198
+ if changed:
199
+ ck.refresh_on_change(state, changed)
200
+ ck.refresh_fixes_async(state)
201
+ if time.time() - last_full >= interval:
202
+ ck.update_metrics(state)
203
+ ck.refresh_fixes_async(state)
204
+ last_full = time.time()
205
+ _render(layout, state)
206
+ time.sleep(poll)
207
+ except KeyboardInterrupt:
208
+ con.print("[dim]Cockpit closed.[/dim]")
@@ -0,0 +1,160 @@
1
+ """
2
+ Commands command — shows command families and their subcommands.
3
+
4
+ Usage:
5
+ p commands → lists all families with descriptions
6
+ p commands --flat → alphabetical list of all commands
7
+ p commands --search <q> → search commands by name/description
8
+ p <family> commands → shows subcommands in that family
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import sys
14
+
15
+ from rich.table import Table
16
+
17
+ from patchi.cli.command_families import (
18
+ FAMILIES,
19
+ format_all_families,
20
+ format_family_help,
21
+ get_family,
22
+ list_families,
23
+ )
24
+ from patchi.cli.console import con
25
+ from patchi.cli.ux import format_header, colored_status
26
+
27
+
28
+ def run(args=None):
29
+ """Handle the commands command."""
30
+ if args is None:
31
+ args = sys.argv[1:]
32
+
33
+ # Parse arguments
34
+ flat = "--flat" in args
35
+ search = None
36
+ for i, arg in enumerate(args):
37
+ if arg == "--search" and i + 1 < len(args):
38
+ search = args[i + 1]
39
+ break
40
+
41
+ if flat:
42
+ _print_flat_commands()
43
+ elif search:
44
+ _search_commands(search)
45
+ else:
46
+ _print_families()
47
+
48
+
49
+ def _print_families():
50
+ """Print all families with descriptions."""
51
+ con.print()
52
+ con.print(format_header("Patchi Command Families"))
53
+ con.print()
54
+
55
+ table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
56
+ table.add_column("Family", style="cyan", ratio=1)
57
+ table.add_column("Description", ratio=3)
58
+ table.add_column("Subcommands", ratio=2)
59
+
60
+ for family in list_families():
61
+ cmds = ", ".join(family.commands[:5]) if family.commands else "—"
62
+ if len(family.commands) > 5:
63
+ cmds += f" (+{len(family.commands) - 5} more)"
64
+ table.add_row(
65
+ f"p {family.name}",
66
+ family.description,
67
+ f"[dim]{cmds}[/dim]",
68
+ )
69
+
70
+ con.print(table)
71
+ con.print()
72
+ con.print("[dim]Use 'p <family> commands' to see all subcommands in a family.[/dim]")
73
+ con.print()
74
+
75
+
76
+ def _print_flat_commands():
77
+ """Print all commands in alphabetical order."""
78
+ all_commands = []
79
+ for family in list_families():
80
+ all_commands.append((family.name, family.description))
81
+ for cmd in family.commands:
82
+ all_commands.append((f"{family.name} {cmd}", family.description))
83
+
84
+ all_commands.sort(key=lambda x: x[0])
85
+
86
+ con.print()
87
+ con.print(format_header("All Patchi Commands"))
88
+ con.print()
89
+
90
+ table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
91
+ table.add_column("Command", style="cyan", ratio=1)
92
+ table.add_column("Description", ratio=3)
93
+
94
+ for cmd, desc in all_commands:
95
+ table.add_row(f"p {cmd}", desc)
96
+
97
+ con.print(table)
98
+ con.print()
99
+
100
+
101
+ def _search_commands(query: str):
102
+ """Search commands by name or description."""
103
+ query = query.lower()
104
+ results = []
105
+
106
+ for family in list_families():
107
+ # Check family name and description
108
+ if query in family.name.lower() or query in family.description.lower():
109
+ results.append((family.name, family.description, "family"))
110
+
111
+ # Check subcommands
112
+ for cmd in family.commands:
113
+ if query in cmd.lower():
114
+ results.append((f"{family.name} {cmd}", family.description, "subcommand"))
115
+
116
+ con.print()
117
+ if results:
118
+ con.print(format_header(f"Commands matching '{query}'"))
119
+ con.print()
120
+
121
+ table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
122
+ table.add_column("Command", style="cyan", ratio=1)
123
+ table.add_column("Description", ratio=2)
124
+ table.add_column("Type", ratio=1)
125
+
126
+ for cmd, desc, cmd_type in results:
127
+ type_style = "blue" if cmd_type == "family" else "dim"
128
+ table.add_row(f"p {cmd}", desc, f"[{type_style}]{cmd_type}[/{type_style}]")
129
+
130
+ con.print(table)
131
+ else:
132
+ con.print(f"[yellow]No commands found matching '{query}'[/yellow]")
133
+
134
+ con.print()
135
+
136
+
137
+ def run_family_commands(family_name: str):
138
+ """Show commands for a specific family."""
139
+ family = get_family(family_name)
140
+ if family:
141
+ con.print()
142
+ con.print(format_header(f"p {family.name}", family.description))
143
+ con.print()
144
+
145
+ if family.commands:
146
+ table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
147
+ table.add_column("Command", style="cyan", ratio=1)
148
+ table.add_column("Full Command", ratio=2)
149
+
150
+ for cmd in family.commands:
151
+ table.add_row(cmd, f"p {family.name} {cmd}")
152
+
153
+ con.print(table)
154
+ else:
155
+ con.print("[dim]No subcommands — use 'p {family.name}' directly.[/dim]")
156
+
157
+ con.print()
158
+ else:
159
+ con.print(f"[red]Unknown family: {family_name}[/red]")
160
+ con.print(f"[dim]Available families: {', '.join(sorted(FAMILIES.keys()))}[/dim]")
@@ -0,0 +1,113 @@
1
+ """
2
+ Cross-repo CLI command.
3
+
4
+ Runs cross-repository intelligence analysis to find
5
+ Routes, Channels, and HTTP calls across multiple indexed projects.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+
11
+ def run(args):
12
+ """Entry point for the cross-repo command."""
13
+ from rich.console import Console
14
+ from rich.panel import Panel
15
+ from rich.table import Table
16
+
17
+ console = Console()
18
+
19
+ # Accept either the main.py subparser shape (action / --target) or the
20
+ # module's own register() shape (projects / --all / --mode).
21
+ target_projects = list(getattr(args, "projects", None) or [])
22
+ if getattr(args, "target", None):
23
+ target_projects = [args.target]
24
+ mode = getattr(args, "mode", None) or "cross-repo-intelligence"
25
+ action = getattr(args, "action", None)
26
+ if action == "health":
27
+ mode = "health"
28
+ elif action == "fix":
29
+ mode = "fix"
30
+ all_flag = getattr(args, "all", False)
31
+
32
+ if not target_projects:
33
+ console.print("[yellow]No target projects specified.[/yellow]")
34
+ console.print("Usage: p cross-repo project1 project2 [project3 ...]")
35
+ console.print("Or: p cross-repo --all (analyze all indexed projects)")
36
+ return
37
+
38
+ console.print(
39
+ Panel(
40
+ f"[bold]Cross-Repository Intelligence[/bold]\n"
41
+ f"Mode: {mode}\n"
42
+ f"Targets: {', '.join(target_projects)}",
43
+ border_style="blue",
44
+ )
45
+ )
46
+
47
+ try:
48
+ from patchi.codebase_memory import index_repository, list_projects
49
+
50
+ if all_flag:
51
+ all_projects = list_projects()
52
+ target_projects = [p["name"] for p in all_projects if p.get("name")]
53
+ console.print(f"[dim]Found {len(target_projects)} indexed projects[/dim]")
54
+
55
+ if len(target_projects) < 2:
56
+ console.print("[red]Need at least 2 projects for cross-repo analysis.[/red]")
57
+ return
58
+
59
+ console.print(
60
+ f"\n[bold]Running cross-repo intelligence on {len(target_projects)} projects...[/bold]\n"
61
+ )
62
+
63
+ results = index_repository(
64
+ repo_path=".",
65
+ mode="cross-repo-intelligence",
66
+ target_projects=target_projects,
67
+ )
68
+
69
+ if not results:
70
+ console.print("[yellow]No cross-repo links found.[/yellow]")
71
+ return
72
+
73
+ table = Table(title="Cross-Repository Links", border_style="green")
74
+ table.add_column("Source", style="cyan")
75
+ table.add_column("Target", style="magenta")
76
+ table.add_column("Type", style="yellow")
77
+ table.add_column("Detail", style="white")
78
+
79
+ for link in results if isinstance(results, list) else []:
80
+ source = str(link.get("source", link.get("from", "")))
81
+ target = str(link.get("target", link.get("to", "")))
82
+ link_type = str(link.get("type", link.get("edge_type", "")))
83
+ detail = str(link.get("detail", link.get("label", "")))[:80]
84
+ table.add_row(source, target, link_type, detail)
85
+
86
+ console.print(table)
87
+ console.print(
88
+ f"\n[green]Found {len(results) if isinstance(results, list) else 0} cross-repo links.[/green]"
89
+ )
90
+
91
+ except ImportError:
92
+ console.print("[red]codebase-memory module not available.[/red]")
93
+ console.print("Install it with: pip install codebase-memory")
94
+ except Exception as e:
95
+ console.print(f"[red]Error: {e}[/red]")
96
+
97
+
98
+ def register(subparsers):
99
+ """Register the cross-repo subcommand."""
100
+ parser = subparsers.add_parser(
101
+ "cross-repo",
102
+ help="Cross-repository intelligence analysis",
103
+ description="Find Routes, Channels, and HTTP calls across multiple indexed projects.",
104
+ )
105
+ parser.add_argument("projects", nargs="*", help="Target project names to analyze")
106
+ parser.add_argument("--all", action="store_true", help="Analyze all indexed projects")
107
+ parser.add_argument(
108
+ "--mode",
109
+ default="cross-repo-intelligence",
110
+ help="Analysis mode (default: cross-repo-intelligence)",
111
+ )
112
+ parser.set_defaults(func=run)
113
+ return parser
@@ -0,0 +1,110 @@
1
+ """
2
+ `p deps` — Supply chain security scan.
3
+
4
+ Usage:
5
+ p deps — run all dependency checks
6
+ p deps --sbom — generate SBOM
7
+ p deps --licenses — check license compliance
8
+ p deps --outdated — check for outdated packages
9
+ p deps --cve — check for known CVEs
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from pathlib import Path
15
+
16
+ from patchi.cli.console import con
17
+ from patchi.cli.display.live_progress import LiveProgress
18
+ from patchi.core.agents.base import AgentGroup, AgentInput, list_agents
19
+ from patchi.core.config import require_project_root
20
+
21
+
22
+ def run(
23
+ sbom: bool = False,
24
+ licenses: bool = False,
25
+ outdated: bool = False,
26
+ cve: bool = False,
27
+ json_output: bool = False,
28
+ root: Path | None = None,
29
+ ) -> None:
30
+ try:
31
+ r = root or require_project_root()
32
+ except RuntimeError as e:
33
+ con.print(f"[red]{e}[/red]")
34
+ return
35
+
36
+ from patchi.core import config as cfg_mod
37
+ from patchi.core import memory as mem
38
+
39
+ brain = mem.get_brain(r)
40
+ config = cfg_mod.load(r)
41
+
42
+ all_agents = {a.name: a for a in list_agents(AgentGroup.SECURITY)}
43
+ target_agents = []
44
+
45
+ no_flags = not any([sbom, licenses, outdated, cve])
46
+ if cve or no_flags:
47
+ target_agents.append("CVEMonitorAgent")
48
+ target_agents.append("DependencyCVEChecker")
49
+ if sbom or licenses or no_flags:
50
+ target_agents.append("SupplyChainAgent")
51
+ if outdated or no_flags:
52
+ target_agents.append("DependencyVulnerabilityAgent")
53
+
54
+ target_agents = list(dict.fromkeys(target_agents)) # dedup
55
+
56
+ results = []
57
+ lp = LiveProgress(con, title="Dependency scan")
58
+ lp.start()
59
+ for name in target_agents:
60
+ cls = all_agents.get(name)
61
+ if not cls:
62
+ lp.log(f" [yellow]Agent {name} not found.[/yellow]")
63
+ lp.update()
64
+ continue
65
+ lp.set_progress(len(results) + 1, len(target_agents), name)
66
+ lp.log(f" {name}…")
67
+ lp.update()
68
+ inp = AgentInput(
69
+ root=r,
70
+ scope=[],
71
+ brain=brain,
72
+ config=config,
73
+ on_message=lambda n, msg, s: (lp.log(f" {msg}", s), lp.update()),
74
+ )
75
+ try:
76
+ result = cls().run(inp)
77
+ results.append(result)
78
+ total_findings = sum(r.finding_count for r in results)
79
+ lp.set_findings(total_findings)
80
+ lp.log(f" [#4ADE80]v[/#4ADE80] {result.finding_count} findings", "")
81
+ except Exception as e:
82
+ lp.log(f" [red]x {name} failed: {e}[/red]", "red")
83
+ lp.update()
84
+
85
+ total_findings = sum(r.finding_count for r in results)
86
+ lp.stop(summary=f"{len(results)} agents - {total_findings} findings")
87
+
88
+ if json_output:
89
+ import json as _json
90
+
91
+ con.print(
92
+ _json.dumps(
93
+ {
94
+ "agents": [
95
+ {
96
+ "agent": getattr(res, "agent_name", ""),
97
+ "status": getattr(getattr(res, "status", None), "value", ""),
98
+ "findings": [
99
+ f.to_dict() if hasattr(f, "to_dict") else dict(f)
100
+ for f in (getattr(res, "findings", []) or [])
101
+ ],
102
+ }
103
+ for res in results
104
+ ],
105
+ "total_findings": total_findings,
106
+ },
107
+ indent=2,
108
+ default=str,
109
+ )
110
+ )