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,128 @@
1
+ """
2
+ `p plan` — Prioritized Fix List (Dream Assistant spec).
3
+
4
+ A standalone, ranked view of "here's what to fix first" across the project
5
+ (or a given area). Fixes are ranked by priority (charter/contract breaches and
6
+ breaking changes above cosmetic cleanups) and safe fixes are listed before the
7
+ ones that need human review.
8
+
9
+ p plan — ranked fix list for the whole project
10
+ p plan src/api — ranked fix list for one area
11
+ p plan --format — also include formatter fixes
12
+ p plan --json — machine-readable output
13
+ p plan --html report.html — write a self-contained HTML report (Playwright-viewable)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ from pathlib import Path
20
+
21
+ from rich.table import Table
22
+
23
+ from patchi.cli.console import con
24
+ from patchi.core.brain.proactive import _FIX_PRIORITY, build_fix_list
25
+ from patchi.core.config import require_project_root
26
+
27
+ _PRIORITY_LABEL = {
28
+ 1: "contract",
29
+ 2: "breaking",
30
+ 3: "missing-import",
31
+ 4: "dead-code",
32
+ 5: "unused-import",
33
+ 6: "format",
34
+ }
35
+
36
+
37
+ def run(
38
+ area: str | None = None,
39
+ include_format: bool = False,
40
+ include_missing_import: bool = False,
41
+ json_output: bool = False,
42
+ html: str | None = None,
43
+ root: Path | None = None,
44
+ ) -> None:
45
+ try:
46
+ r = root or require_project_root()
47
+ except RuntimeError as e:
48
+ con.print(f"[red]{e}[/red]")
49
+ return
50
+
51
+ fixes = build_fix_list(
52
+ r,
53
+ area=area,
54
+ include_format=include_format,
55
+ include_missing_import=include_missing_import,
56
+ )
57
+
58
+ if json_output:
59
+ con.print(json.dumps([f.to_dict() for f in fixes], indent=2))
60
+ return
61
+ if html:
62
+ _write_html(Path(html), fixes)
63
+ con.print(f"[#4ADE80]HTML report written to {html}[/#4ADE80]")
64
+ return
65
+
66
+ con.print()
67
+ if not fixes:
68
+ con.print("[#4ADE80]✓[/#4ADE80] No prioritized fixes proposed.")
69
+ con.print()
70
+ return
71
+
72
+ table = Table(show_header=True, header_style="bold #C8621A", box=None, pad_edge=False)
73
+ table.add_column("#", width=4)
74
+ table.add_column("Priority", width=14)
75
+ table.add_column("Safe", width=8)
76
+ table.add_column("Type", width=18)
77
+ table.add_column("File", width=36)
78
+ table.add_column("Fix")
79
+ for i, f in enumerate(fixes, 1):
80
+ pri = _FIX_PRIORITY.get(f.fix_type, 9)
81
+ label = _PRIORITY_LABEL.get(pri, "other")
82
+ safe = "[#4ADE80]safe[/#4ADE80]" if f.safe else "[#FACC15]review[/#FACC15]"
83
+ table.add_row(str(i), label, safe, f.fix_type, f.file, f.description)
84
+ con.print(
85
+ f"[bold #C8621A]Prioritized Fix List[/bold #C8621A] [dim]({len(fixes)} item(s))[/dim]"
86
+ )
87
+ con.print(table)
88
+ con.print()
89
+
90
+
91
+ def _write_html(path: Path, fixes) -> None:
92
+ rows = []
93
+ for i, f in enumerate(fixes, 1):
94
+ pri = _FIX_PRIORITY.get(f.fix_type, 9)
95
+ label = _PRIORITY_LABEL.get(pri, "other")
96
+ safe = "safe" if f.safe else "needs-review"
97
+ rows.append(
98
+ f"<tr class='{safe}'><td>{i}</td><td>{label}</td>"
99
+ f"<td>{safe}</td><td>{f.fix_type}</td><td>{f.file}</td>"
100
+ f"<td>{_esc(f.description)}</td></tr>"
101
+ )
102
+ html = f"""<!doctype html>
103
+ <html lang="en"><head><meta charset="utf-8">
104
+ <title>Patchi — Prioritized Fix List</title>
105
+ <style>
106
+ body {{ font-family: ui-monospace, Menlo, Consolas, monospace; background:#1a1714; color:#f2edd6; margin:2rem; }}
107
+ h1 {{ color:#c8621a; }}
108
+ table {{ border-collapse:collapse; width:100%; }}
109
+ th,td {{ text-align:left; padding:.4rem .6rem; border-bottom:1px solid #2a3d28; }}
110
+ th {{ color:#c8621a; }}
111
+ tr.safe td {{ color:#9be8a0; }}
112
+ tr.needs-review td {{ color:#facc15; }}
113
+ .muted {{ color:#b8a898; }}
114
+ </style></head>
115
+ <body>
116
+ <h1>Patchi — Prioritized Fix List</h1>
117
+ <p class="muted">{len(fixes)} item(s) ranked by priority.</p>
118
+ <table>
119
+ <tr><th>#</th><th>Priority</th><th>Safe</th><th>Type</th><th>File</th><th>Fix</th></tr>
120
+ {"".join(rows)}
121
+ </table>
122
+ </body></html>"""
123
+ path.parent.mkdir(parents=True, exist_ok=True)
124
+ path.write_text(html, encoding="utf-8")
125
+
126
+
127
+ def _esc(s: str) -> str:
128
+ return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
@@ -0,0 +1,307 @@
1
+ """
2
+ Plugin management commands for Patchi.
3
+
4
+ Usage:
5
+ p plugins list List all registered plugins
6
+ p plugins run <name> Run a specific plugin
7
+ p plugins run-all Run all plugins
8
+ p plugins info <name> Show plugin details
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import time
14
+ from pathlib import Path
15
+
16
+ from rich.console import Console
17
+ from rich.panel import Panel
18
+ from rich.table import Table
19
+
20
+ from patchi.core.plugins.analyzer import AnalyzerContext
21
+ from patchi.core.plugins.registry import get_registry
22
+
23
+ console = Console()
24
+
25
+
26
+ def cmd_plugins(args: list[str]) -> None:
27
+ """Handle plugin commands."""
28
+ if not args:
29
+ _show_help()
30
+ return
31
+
32
+ cmd = args[0]
33
+
34
+ if cmd == "list":
35
+ _list_plugins()
36
+ elif cmd == "run" and len(args) > 1:
37
+ _run_plugin(args[1])
38
+ elif cmd == "run-all":
39
+ _run_all_plugins()
40
+ elif cmd == "info" and len(args) > 1:
41
+ _show_plugin_info(args[1])
42
+ else:
43
+ _show_help()
44
+
45
+
46
+ def _show_help() -> None:
47
+ """Show help message."""
48
+ console.print(Panel.fit(
49
+ "[bold]Patchi Plugin System[/bold]\n\n"
50
+ "Commands:\n"
51
+ " p plugins list List all registered plugins\n"
52
+ " p plugins run <name> Run a specific plugin\n"
53
+ " p plugins run-all Run all plugins\n"
54
+ " p plugins info <name> Show plugin details\n\n"
55
+ "Plugin directories:\n"
56
+ " Built-in: patchi/core/plugins/analyzers/\n"
57
+ " User: ~/.patchi/plugins/\n"
58
+ " Project: .patchi/plugins/",
59
+ title="Plugin Help",
60
+ ))
61
+
62
+
63
+ def _list_plugins() -> None:
64
+ """List all registered plugins."""
65
+ registry = get_registry()
66
+ analyzers = registry.list_analyzers(enabled_only=False)
67
+
68
+ if not analyzers:
69
+ console.print("[yellow]No plugins found. Discovering...[/yellow]")
70
+ registry.discover()
71
+ analyzers = registry.list_analyzers(enabled_only=False)
72
+
73
+ table = Table(title="Registered Plugins")
74
+ table.add_column("Name", style="cyan")
75
+ table.add_column("Version", style="green")
76
+ table.add_column("Priority", justify="right")
77
+ table.add_column("Enabled", justify="center")
78
+ table.add_column("Description")
79
+
80
+ for analyzer in analyzers:
81
+ enabled = "✓" if analyzer.enabled else "✗"
82
+ style = "green" if analyzer.enabled else "dim"
83
+ table.add_row(
84
+ analyzer.name,
85
+ analyzer.version,
86
+ str(analyzer.priority),
87
+ f"[{style}]{enabled}[/{style}]",
88
+ analyzer.description[:50] + "..." if len(analyzer.description) > 50 else analyzer.description,
89
+ )
90
+
91
+ console.print(table)
92
+ console.print(f"\n[dim]Total: {len(analyzers)} plugins[/dim]")
93
+
94
+
95
+ def _run_plugin(name: str) -> None:
96
+ """Run a specific plugin."""
97
+ registry = get_registry()
98
+ analyzer = registry.get(name)
99
+
100
+ if not analyzer:
101
+ console.print(f"[red]Plugin '{name}' not found[/red]")
102
+ console.print("[dim]Run 'p plugins list' to see available plugins[/dim]")
103
+ return
104
+
105
+ console.print(f"[bold]Running plugin: {analyzer.name} v{analyzer.version}[/bold]")
106
+
107
+ # Build context from current project
108
+ root = Path.cwd()
109
+ context = AnalyzerContext(
110
+ root=root,
111
+ config=_load_config(root),
112
+ )
113
+
114
+ # Load files
115
+ console.print("[dim]Loading project files...[/dim]")
116
+ context.files = _load_files(root)
117
+
118
+ # Run the analyzer
119
+ console.print(f"[dim]Analyzing {len(context.files)} files...[/dim]")
120
+ t0 = time.time()
121
+ result = registry.run(name, context)
122
+ duration = time.time() - t0
123
+
124
+ # Display results
125
+ _display_result(analyzer.name, result, duration)
126
+
127
+
128
+ def _run_all_plugins() -> None:
129
+ """Run all plugins."""
130
+ registry = get_registry()
131
+ analyzers = registry.list_analyzers(enabled_only=True)
132
+
133
+ if not analyzers:
134
+ console.print("[yellow]No plugins found[/yellow]")
135
+ return
136
+
137
+ console.print(f"[bold]Running {len(analyzers)} plugins...[/bold]")
138
+
139
+ # Build context
140
+ root = Path.cwd()
141
+ context = AnalyzerContext(
142
+ root=root,
143
+ config=_load_config(root),
144
+ )
145
+
146
+ # Load files
147
+ console.print("[dim]Loading project files...[/dim]")
148
+ context.files = _load_files(root)
149
+
150
+ # Run all analyzers
151
+ t0 = time.time()
152
+ results = registry.run_all(context)
153
+ duration = time.time() - t0
154
+
155
+ # Display results
156
+ total_findings = sum(len(r.findings) for r in results.values())
157
+ console.print(f"\n[bold green]✓ Completed {len(results)} plugins in {duration:.1f}s[/bold green]")
158
+ console.print(f"[dim]Total findings: {total_findings}[/dim]")
159
+
160
+ for name, result in results.items():
161
+ if result.findings:
162
+ console.print(f"\n[bold]{name}:[/bold] {len(result.findings)} findings")
163
+ for finding in result.findings[:5]: # Show first 5
164
+ severity_color = {
165
+ "critical": "red",
166
+ "high": "red",
167
+ "medium": "yellow",
168
+ "low": "blue",
169
+ "info": "dim",
170
+ }.get(finding.severity.value, "white")
171
+ console.print(f" [{severity_color}]●[/{severity_color}] {finding.file}:{finding.line} - {finding.message}")
172
+
173
+
174
+ def _show_plugin_info(name: str) -> None:
175
+ """Show detailed info about a plugin."""
176
+ registry = get_registry()
177
+ analyzer = registry.get(name)
178
+
179
+ if not analyzer:
180
+ console.print(f"[red]Plugin '{name}' not found[/red]")
181
+ return
182
+
183
+ info = analyzer.describe()
184
+
185
+ console.print(Panel.fit(
186
+ f"[bold]Name:[/bold] {info['name']}\n"
187
+ f"[bold]Version:[/bold] {info['version']}\n"
188
+ f"[bold]Description:[/bold] {info['description']}\n"
189
+ f"[bold]Priority:[/bold] {info['priority']}\n"
190
+ f"[bold]Timeout:[/bold] {info['timeout']}s\n"
191
+ f"[bold]Enabled:[/bold] {info['enabled']}\n"
192
+ f"[bold]Languages:[/bold] {', '.join(info['supported_languages']) or 'all'}\n"
193
+ f"[bold]File Patterns:[/bold] {', '.join(info['supported_file_patterns']) or 'all'}",
194
+ title=f"Plugin: {name}",
195
+ ))
196
+
197
+
198
+ def _load_config(root: Path) -> dict:
199
+ """Load project config."""
200
+ try:
201
+ from patchi.core import config as cfg
202
+ return cfg.load(root)
203
+ except Exception:
204
+ return {}
205
+
206
+
207
+ def _load_files(root: Path) -> list:
208
+ """Load project files for analysis."""
209
+ from patchi.core.plugins.analyzer import FileNode
210
+
211
+ files = []
212
+ skip_dirs = {".git", "node_modules", "__pycache__", ".patchi", "venv", ".venv"}
213
+ skip_extensions = {".pyc", ".pyo", ".so", ".dll", ".exe", ".bin"}
214
+
215
+ for path in root.rglob("*"):
216
+ if path.is_file():
217
+ # Skip directories
218
+ if any(skip in path.parts for skip in skip_dirs):
219
+ continue
220
+ if path.suffix in skip_extensions:
221
+ continue
222
+
223
+ try:
224
+ content = path.read_text(encoding="utf-8", errors="ignore")
225
+ rel_path = str(path.relative_to(root))
226
+
227
+ # Detect language
228
+ language = _detect_language(path)
229
+
230
+ files.append(FileNode(
231
+ path=rel_path,
232
+ content=content,
233
+ language=language,
234
+ size=path.stat().st_size,
235
+ ))
236
+ except Exception:
237
+ continue
238
+
239
+ return files
240
+
241
+
242
+ def _detect_language(path: Path) -> str:
243
+ """Detect programming language from file extension."""
244
+ ext_map = {
245
+ ".py": "python",
246
+ ".js": "javascript",
247
+ ".ts": "typescript",
248
+ ".jsx": "javascript",
249
+ ".tsx": "typescript",
250
+ ".java": "java",
251
+ ".go": "go",
252
+ ".rs": "rust",
253
+ ".rb": "ruby",
254
+ ".php": "php",
255
+ ".c": "c",
256
+ ".cpp": "cpp",
257
+ ".h": "c",
258
+ ".hpp": "cpp",
259
+ ".cs": "csharp",
260
+ ".swift": "swift",
261
+ ".kt": "kotlin",
262
+ ".scala": "scala",
263
+ ".html": "html",
264
+ ".css": "css",
265
+ ".scss": "scss",
266
+ ".less": "less",
267
+ ".json": "json",
268
+ ".yaml": "yaml",
269
+ ".yml": "yaml",
270
+ ".toml": "toml",
271
+ ".xml": "xml",
272
+ ".sql": "sql",
273
+ ".sh": "shell",
274
+ ".bash": "shell",
275
+ ".zsh": "shell",
276
+ ".md": "markdown",
277
+ ".txt": "text",
278
+ }
279
+ return ext_map.get(path.suffix.lower(), "unknown")
280
+
281
+
282
+ def _display_result(name: str, result, duration: float) -> None:
283
+ """Display analysis results."""
284
+ if result.errors:
285
+ console.print("[red]Errors:[/red]")
286
+ for error in result.errors:
287
+ console.print(f" ✗ {error}")
288
+ return
289
+
290
+ console.print(f"\n[bold green]✓ {name} completed in {duration:.1f}s[/bold green]")
291
+ console.print(f"[dim]Files analyzed: {result.files_analyzed}[/dim]")
292
+ console.print(f"[dim]Findings: {len(result.findings)}[/dim]")
293
+
294
+ if result.findings:
295
+ console.print("\n[bold]Findings:[/bold]")
296
+ for finding in result.findings[:10]: # Show first 10
297
+ severity_color = {
298
+ "critical": "red",
299
+ "high": "red",
300
+ "medium": "yellow",
301
+ "low": "blue",
302
+ "info": "dim",
303
+ }.get(finding.severity.value, "white")
304
+ console.print(f" [{severity_color}]●[/{severity_color}] {finding.file}:{finding.line} - {finding.message}")
305
+
306
+ if len(result.findings) > 10:
307
+ console.print(f" [dim]... and {len(result.findings) - 10} more[/dim]")
@@ -0,0 +1,152 @@
1
+ from pathlib import Path
2
+
3
+ from rich.prompt import Confirm
4
+ from rich.table import Table
5
+ from rich.text import Text
6
+
7
+ from patchi.cli.console import con
8
+ from patchi.core import config as cfg
9
+ from patchi.core import queue as q
10
+ from patchi.core.constants import QueueMode
11
+
12
+ """
13
+ `p queue` — view and control the task queue.
14
+
15
+ Subcommands:
16
+ p queue — show queue overview
17
+ p queue pause — pause execution
18
+ p queue resume — resume
19
+ p queue skip — skip current active item
20
+ p queue clear — remove all waiting items
21
+ p queue mode single — set single queue mode
22
+ p queue mode multi — set multi queue mode
23
+ p queue mode off — disable queue
24
+ """
25
+
26
+
27
+ def _root(root: Path | None) -> Path | None:
28
+ return root
29
+
30
+
31
+ def run_show(root: Path | None = None) -> None:
32
+ """p queue — show current queue."""
33
+ try:
34
+ data = q.list_all(root)
35
+ s = q.stats(root)
36
+ except RuntimeError as e:
37
+ con.print(f"[red]{e}[/red]")
38
+ return
39
+
40
+ items = data["items"]
41
+ paused = data.get("paused", False)
42
+ current_mode = cfg.get_queue_mode(root)
43
+
44
+ con.print()
45
+ # Status line
46
+ status = "[yellow]PAUSED[/yellow]" if paused else "[#4ADE80]RUNNING[/#4ADE80]"
47
+ con.print(
48
+ f"[bold #F2EDD6]Queue[/bold #F2EDD6] "
49
+ f"{status} [dim]mode: {current_mode.value}[/dim] "
50
+ f"[dim]{s['waiting']} waiting · {s['active']} active · {s['done']} done[/dim]"
51
+ )
52
+ con.print()
53
+
54
+ if not items:
55
+ con.print("[dim]Nothing in the queue. Patchi is resting.[/dim]")
56
+ con.print()
57
+ return
58
+
59
+ table = Table(show_header=True, header_style="bold #C8621A", box=None, pad_edge=False)
60
+ table.add_column("ID", style="dim", width=10)
61
+ table.add_column("Type", style="#F2EDD6", width=12)
62
+ table.add_column("Target", style="#B8A898", width=20)
63
+ table.add_column("State", style="bold", width=10)
64
+ table.add_column("Agent", style="dim", width=16)
65
+
66
+ state_colors = {
67
+ "waiting": "#B8A898",
68
+ "active": "#C8621A",
69
+ "done": "#4ADE80",
70
+ "failed": "#FF4D6D",
71
+ "skipped": "dim",
72
+ }
73
+
74
+ for item in sorted(items, key=lambda i: i.get("created", ""), reverse=True):
75
+ state = item["state"]
76
+ color = state_colors.get(state, "white")
77
+ table.add_row(
78
+ item["id"],
79
+ item.get("type", "—"),
80
+ item.get("target", "—"),
81
+ Text(state, style=color),
82
+ item.get("agent") or "—",
83
+ )
84
+
85
+ con.print(table)
86
+ con.print()
87
+ con.print("[dim]p queue pause · p queue resume · p queue skip · p queue clear[/dim]")
88
+ con.print()
89
+
90
+
91
+ def run_pause(root: Path | None = None) -> None:
92
+ try:
93
+ q.pause(root)
94
+ con.print("[yellow]Queue paused.[/yellow] Use [bold]p queue resume[/bold] to continue.")
95
+ except RuntimeError as e:
96
+ con.print(f"[red]{e}[/red]")
97
+
98
+
99
+ def run_resume(root: Path | None = None) -> None:
100
+ try:
101
+ q.resume(root)
102
+ con.print("[#4ADE80]Queue resumed.[/#4ADE80]")
103
+ except RuntimeError as e:
104
+ con.print(f"[red]{e}[/red]")
105
+
106
+
107
+ def run_skip(root: Path | None = None) -> None:
108
+ try:
109
+ skipped_id = q.skip_active(root)
110
+ if skipped_id:
111
+ con.print(f"[dim]Skipped item [bold]{skipped_id}[/bold].[/dim]")
112
+ else:
113
+ con.print("[dim]Nothing active to skip.[/dim]")
114
+ except RuntimeError as e:
115
+ con.print(f"[red]{e}[/red]")
116
+
117
+
118
+ def run_clear(root: Path | None = None) -> None:
119
+ try:
120
+ s = q.stats(root)
121
+ waiting = s["waiting"]
122
+ if waiting == 0:
123
+ con.print("[dim]Queue is already empty.[/dim]")
124
+ return
125
+
126
+ con.print(f"[yellow]This will remove {waiting} waiting item(s).[/yellow]")
127
+ confirmed = Confirm.ask("Clear the queue?", default=False)
128
+ if confirmed:
129
+ removed = q.clear(root)
130
+ con.print(f"[#4ADE80]✓[/#4ADE80] Removed {removed} waiting item(s).")
131
+ else:
132
+ con.print("[dim]Cancelled.[/dim]")
133
+ except RuntimeError as e:
134
+ con.print(f"[red]{e}[/red]")
135
+
136
+
137
+ def run_set_mode(mode_str: str, root: Path | None = None) -> None:
138
+ """p queue mode <single|multi|off>"""
139
+ try:
140
+ mode = QueueMode(mode_str.lower())
141
+ except ValueError:
142
+ con.print(f"[red]Unknown mode: {mode_str!r}[/red]\n[dim]Valid: single · multi · off[/dim]")
143
+ return
144
+
145
+ try:
146
+ cfg.set_queue_mode(mode, root)
147
+ con.print(
148
+ f"[#4ADE80]✓[/#4ADE80] Queue mode set to [bold]{mode.value}[/bold].\n"
149
+ f"[dim]{mode.description()}[/dim]"
150
+ )
151
+ except RuntimeError as e:
152
+ con.print(f"[red]{e}[/red]")