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.
- patchi/__init__.py +1 -0
- patchi/__main__.py +13 -0
- patchi/cli/__init__.py +0 -0
- patchi/cli/command_families.py +235 -0
- patchi/cli/commands/__init__.py +0 -0
- patchi/cli/commands/access_cmd.py +65 -0
- patchi/cli/commands/agent_stats_cmd.py +273 -0
- patchi/cli/commands/agents_cmd.py +187 -0
- patchi/cli/commands/ai_cmd.py +308 -0
- patchi/cli/commands/assure_cmd.py +538 -0
- patchi/cli/commands/audit_cmd.py +286 -0
- patchi/cli/commands/auto_cmd.py +110 -0
- patchi/cli/commands/blame_cmd.py +53 -0
- patchi/cli/commands/chains_cmd.py +327 -0
- patchi/cli/commands/charter_cmd.py +173 -0
- patchi/cli/commands/chat_cmd.py +597 -0
- patchi/cli/commands/check_cmd.py +232 -0
- patchi/cli/commands/cleanup_cmd.py +328 -0
- patchi/cli/commands/cockpit_cmd.py +208 -0
- patchi/cli/commands/commands_cmd.py +160 -0
- patchi/cli/commands/cross_repo_cmd.py +113 -0
- patchi/cli/commands/deps_cmd.py +110 -0
- patchi/cli/commands/dev_check_cmd.py +342 -0
- patchi/cli/commands/dev_cmd.py +445 -0
- patchi/cli/commands/doctor_cmd.py +457 -0
- patchi/cli/commands/findings_cmd.py +187 -0
- patchi/cli/commands/fix_cmd.py +648 -0
- patchi/cli/commands/fix_review_cmd.py +273 -0
- patchi/cli/commands/heatmap_cmd.py +62 -0
- patchi/cli/commands/help_cmd.py +159 -0
- patchi/cli/commands/hosted_cmd.py +980 -0
- patchi/cli/commands/init.py +456 -0
- patchi/cli/commands/key_cmd.py +571 -0
- patchi/cli/commands/learn_cmd.py +140 -0
- patchi/cli/commands/link_cmd.py +104 -0
- patchi/cli/commands/log_cmd.py +57 -0
- patchi/cli/commands/memory_cmd.py +215 -0
- patchi/cli/commands/model_cmd.py +207 -0
- patchi/cli/commands/notify_cmd.py +213 -0
- patchi/cli/commands/notify_prompts.py +29 -0
- patchi/cli/commands/patch_cmd.py +231 -0
- patchi/cli/commands/plan_cmd.py +128 -0
- patchi/cli/commands/plugins_cmd.py +307 -0
- patchi/cli/commands/queue_cmd.py +152 -0
- patchi/cli/commands/quick_cmd.py +159 -0
- patchi/cli/commands/ready_cmd.py +292 -0
- patchi/cli/commands/report_cmd.py +416 -0
- patchi/cli/commands/restrict_cmd.py +127 -0
- patchi/cli/commands/review_cmd.py +225 -0
- patchi/cli/commands/rules_cmd.py +233 -0
- patchi/cli/commands/scan_cmd.py +1908 -0
- patchi/cli/commands/settings_cmd.py +45 -0
- patchi/cli/commands/status_cmd.py +679 -0
- patchi/cli/commands/test_cmd.py +1052 -0
- patchi/cli/commands/trend_cmd.py +296 -0
- patchi/cli/commands/undo_cmd.py +235 -0
- patchi/cli/commands/update_cmd.py +345 -0
- patchi/cli/commands/verify_cmd.py +56 -0
- patchi/cli/commands/vr_cmd.py +440 -0
- patchi/cli/commands/watch_cmd.py +304 -0
- patchi/cli/commands/web_cmd.py +114 -0
- patchi/cli/console.py +101 -0
- patchi/cli/display/__init__.py +1 -0
- patchi/cli/display/live_audit.py +269 -0
- patchi/cli/display/live_progress.py +169 -0
- patchi/cli/framework.py +241 -0
- patchi/cli/logo.py +128 -0
- patchi/cli/main.py +222 -0
- patchi/cli/registry.py +1171 -0
- patchi/cli/style.py +68 -0
- patchi/cli/themes.py +187 -0
- patchi/cli/ux.py +444 -0
- patchi/core/__init__.py +0 -0
- patchi/core/agents/__init__.py +0 -0
- patchi/core/agents/api_fuzzer_agent.py +141 -0
- patchi/core/agents/attack_agent.py +242 -0
- patchi/core/agents/base.py +1012 -0
- patchi/core/agents/bug_predictor_agent.py +58 -0
- patchi/core/agents/build_tool_validator.py +150 -0
- patchi/core/agents/cache.py +192 -0
- patchi/core/agents/chaos_agent.py +133 -0
- patchi/core/agents/cicd_generator.py +240 -0
- patchi/core/agents/comment_scanner.py +280 -0
- patchi/core/agents/console_logging_agent.py +156 -0
- patchi/core/agents/coordinator.py +858 -0
- patchi/core/agents/core_scanner.py +269 -0
- patchi/core/agents/coverage_prioritizer.py +181 -0
- patchi/core/agents/dead_code_hygiene.py +296 -0
- patchi/core/agents/dead_code_scanner.py +692 -0
- patchi/core/agents/dependency_scanner.py +725 -0
- patchi/core/agents/doc_claim_agent.py +573 -0
- patchi/core/agents/duplicate_scanner.py +568 -0
- patchi/core/agents/env_scanner.py +410 -0
- patchi/core/agents/feature_flag_agent.py +89 -0
- patchi/core/agents/frontend_framework_agent.py +133 -0
- patchi/core/agents/goroutine_agent.py +82 -0
- patchi/core/agents/governor.py +1972 -0
- patchi/core/agents/i18n_agent.py +95 -0
- patchi/core/agents/license_compliance.py +247 -0
- patchi/core/agents/linking_agent.py +251 -0
- patchi/core/agents/memory_profiler_agent.py +102 -0
- patchi/core/agents/modernization_agent.py +84 -0
- patchi/core/agents/mutation_agent.py +174 -0
- patchi/core/agents/nl_query_agent.py +74 -0
- patchi/core/agents/promise_rejection_agent.py +100 -0
- patchi/core/agents/refactoring_agent.py +286 -0
- patchi/core/agents/resource_leak_agent.py +91 -0
- patchi/core/agents/route_graph_scanner.py +951 -0
- patchi/core/agents/run_agent.py +106 -0
- patchi/core/agents/rust_unwrap_agent.py +100 -0
- patchi/core/agents/sbom_generator.py +284 -0
- patchi/core/agents/scanners.py +67 -0
- patchi/core/agents/side/__init__.py +1 -0
- patchi/core/agents/side/build_agent.py +65 -0
- patchi/core/agents/side/format_agent.py +50 -0
- patchi/core/agents/side/install_agent.py +63 -0
- patchi/core/agents/side_file_scanner.py +549 -0
- patchi/core/agents/snapshot_drift_detector.py +146 -0
- patchi/core/agents/spa_route_inventory.py +203 -0
- patchi/core/agents/test_scanner.py +442 -0
- patchi/core/agents/tool_health.py +109 -0
- patchi/core/agents/tool_runner.py +162 -0
- patchi/core/agents/type_scanner.py +168 -0
- patchi/core/agents/ui_scanner.py +558 -0
- patchi/core/ai/__init__.py +20 -0
- patchi/core/ai/agent_audit.py +153 -0
- patchi/core/ai/agent_profiler.py +277 -0
- patchi/core/ai/client.py +499 -0
- patchi/core/ai/cost_tracker.py +107 -0
- patchi/core/ai/model_router.py +318 -0
- patchi/core/ai/orchestrator.py +625 -0
- patchi/core/ai/prompts.py +681 -0
- patchi/core/ai/smart.py +302 -0
- patchi/core/ai/tool_executor.py +24 -0
- patchi/core/ai/tools/__init__.py +10 -0
- patchi/core/ai/tools/executor.py +621 -0
- patchi/core/ai/tools/realize.py +1160 -0
- patchi/core/ai/tools/registry.py +1577 -0
- patchi/core/assurance/__init__.py +33 -0
- patchi/core/assurance/builder.py +211 -0
- patchi/core/assurance/graph.py +342 -0
- patchi/core/assurance/invariants.py +168 -0
- patchi/core/atomic.py +61 -0
- patchi/core/attackers/__init__.py +17 -0
- patchi/core/attackers/api_abuse.py +89 -0
- patchi/core/attackers/auth_bypass.py +73 -0
- patchi/core/attackers/base.py +91 -0
- patchi/core/attackers/business_logic.py +88 -0
- patchi/core/attackers/planner.py +108 -0
- patchi/core/attackers/priv_escalation.py +88 -0
- patchi/core/attackers/recon.py +87 -0
- patchi/core/brain/__init__.py +0 -0
- patchi/core/brain/ast_utils/__init__.py +81 -0
- patchi/core/brain/ast_utils/assignments.py +219 -0
- patchi/core/brain/ast_utils/calls.py +139 -0
- patchi/core/brain/ast_utils/config.py +132 -0
- patchi/core/brain/ast_utils/control_flow.py +74 -0
- patchi/core/brain/ast_utils/dead_symbols.py +205 -0
- patchi/core/brain/ast_utils/decorators.py +108 -0
- patchi/core/brain/ast_utils/helpers.py +144 -0
- patchi/core/brain/ast_utils/imports.py +169 -0
- patchi/core/brain/ast_utils/scan.py +143 -0
- patchi/core/brain/ast_utils/taint.py +310 -0
- patchi/core/brain/audit.py +379 -0
- patchi/core/brain/baseline.py +195 -0
- patchi/core/brain/blast_radius.py +312 -0
- patchi/core/brain/body_tags.py +144 -0
- patchi/core/brain/brain.py +1161 -0
- patchi/core/brain/brain_context.py +393 -0
- patchi/core/brain/brain_watcher.py +340 -0
- patchi/core/brain/charter.py +473 -0
- patchi/core/brain/classifier.py +214 -0
- patchi/core/brain/cockpit.py +350 -0
- patchi/core/brain/code_query.py +710 -0
- patchi/core/brain/contract.py +889 -0
- patchi/core/brain/contract_diff.py +155 -0
- patchi/core/brain/council.py +800 -0
- patchi/core/brain/cross_repo.py +104 -0
- patchi/core/brain/dead_code_tools.py +157 -0
- patchi/core/brain/doc_validator.py +197 -0
- patchi/core/brain/domain_activator.py +2183 -0
- patchi/core/brain/enriched_context.py +220 -0
- patchi/core/brain/file_corpus.py +220 -0
- patchi/core/brain/framework.py +953 -0
- patchi/core/brain/freshness.py +273 -0
- patchi/core/brain/git_aware.py +206 -0
- patchi/core/brain/heatmap.py +104 -0
- patchi/core/brain/ignore_parser.py +153 -0
- patchi/core/brain/import_graph.py +486 -0
- patchi/core/brain/languages.py +397 -0
- patchi/core/brain/layered_brain.py +419 -0
- patchi/core/brain/learning.py +229 -0
- patchi/core/brain/orphaned_endpoints.py +313 -0
- patchi/core/brain/personas/__init__.py +648 -0
- patchi/core/brain/personas/bad_user.py +165 -0
- patchi/core/brain/personas/base.py +349 -0
- patchi/core/brain/proactive.py +807 -0
- patchi/core/brain/project_context.py +121 -0
- patchi/core/brain/project_reader.py +521 -0
- patchi/core/brain/reasoning.py +269 -0
- patchi/core/brain/route_detector/__init__.py +15 -0
- patchi/core/brain/route_detector/base.py +31 -0
- patchi/core/brain/route_detector/csharp.py +209 -0
- patchi/core/brain/route_detector/file_based.py +81 -0
- patchi/core/brain/route_detector/go.py +152 -0
- patchi/core/brain/route_detector/java.py +195 -0
- patchi/core/brain/route_detector/javascript.py +197 -0
- patchi/core/brain/route_detector/php.py +158 -0
- patchi/core/brain/route_detector/python.py +193 -0
- patchi/core/brain/route_detector/registry.py +25 -0
- patchi/core/brain/route_detector/ruby.py +134 -0
- patchi/core/brain/route_detector/rust.py +216 -0
- patchi/core/brain/route_detector/swift.py +118 -0
- patchi/core/brain/route_mapper.py +265 -0
- patchi/core/brain/scanner.py +2136 -0
- patchi/core/brain/secrets.py +280 -0
- patchi/core/brain/symbol_graph.py +1955 -0
- patchi/core/brain/team.py +28 -0
- patchi/core/brain/trace_log.py +132 -0
- patchi/core/brain/type_checker/__init__.py +65 -0
- patchi/core/brain/type_checker/base.py +60 -0
- patchi/core/brain/type_checker/csharp.py +59 -0
- patchi/core/brain/type_checker/dart.py +59 -0
- patchi/core/brain/type_checker/go.py +81 -0
- patchi/core/brain/type_checker/java.py +75 -0
- patchi/core/brain/type_checker/kotlin.py +75 -0
- patchi/core/brain/type_checker/php.py +88 -0
- patchi/core/brain/type_checker/python.py +87 -0
- patchi/core/brain/type_checker/rust.py +81 -0
- patchi/core/brain/type_checker/swift.py +59 -0
- patchi/core/brain/type_checker/typescript.py +298 -0
- patchi/core/brain/understander.py +130 -0
- patchi/core/brain/verify.py +379 -0
- patchi/core/campaigns/__init__.py +14 -0
- patchi/core/campaigns/base.py +62 -0
- patchi/core/campaigns/data_flow_abuse.py +153 -0
- patchi/core/campaigns/orchestrator.py +140 -0
- patchi/core/campaigns/state_transitions.py +178 -0
- patchi/core/ci_bundle.py +104 -0
- patchi/core/config.py +311 -0
- patchi/core/constants.py +245 -0
- patchi/core/debug/__init__.py +25 -0
- patchi/core/debug/adapters/__init__.py +11 -0
- patchi/core/debug/adapters/codelldb.py +379 -0
- patchi/core/debug/adapters/node.py +222 -0
- patchi/core/debug/adapters/powershell.py +210 -0
- patchi/core/debug/adapters/python.py +244 -0
- patchi/core/debug/capture.py +210 -0
- patchi/core/debug/dap_client.py +268 -0
- patchi/core/detector/__init__.py +0 -0
- patchi/core/detector/audit.py +194 -0
- patchi/core/detector/bus.py +226 -0
- patchi/core/detector/dispatcher.py +275 -0
- patchi/core/detector/event.py +254 -0
- patchi/core/detector/sigma_engine.py +378 -0
- patchi/core/detector/triage.py +289 -0
- patchi/core/export/sarif.py +113 -0
- patchi/core/fix/__init__.py +17 -0
- patchi/core/fix/applier.py +574 -0
- patchi/core/fix/base.py +510 -0
- patchi/core/fix/code_fixer.py +169 -0
- patchi/core/fix/dead_code_remover.py +131 -0
- patchi/core/fix/fix_agents.py +775 -0
- patchi/core/fix/patch.py +444 -0
- patchi/core/fix/risk_gate.py +383 -0
- patchi/core/fix/security_fixer.py +93 -0
- patchi/core/fix/verify_loop.py +204 -0
- patchi/core/fuzz/__init__.py +8 -0
- patchi/core/fuzz/corpus.py +137 -0
- patchi/core/fuzz/input_fuzzer.py +256 -0
- patchi/core/fuzz/sequence_fuzzer.py +162 -0
- patchi/core/fuzz/state_fuzzer.py +236 -0
- patchi/core/health.py +407 -0
- patchi/core/hosted/__init__.py +1 -0
- patchi/core/hosted/anomaly.py +298 -0
- patchi/core/hosted/audit_log.py +102 -0
- patchi/core/hosted/compliance_report.py +148 -0
- patchi/core/hosted/ip_reputation.py +169 -0
- patchi/core/hosted/log_parsers.py +251 -0
- patchi/core/hosted/tokens.py +127 -0
- patchi/core/hosted/watchlist.py +164 -0
- patchi/core/hosted/webhooks.py +156 -0
- patchi/core/memory.py +437 -0
- patchi/core/notifications/__init__.py +18 -0
- patchi/core/notifications/channels.py +192 -0
- patchi/core/notifications/digest.py +141 -0
- patchi/core/notifications/escalation.py +158 -0
- patchi/core/notifications/notifier.py +182 -0
- patchi/core/notifications/quiet_hours.py +69 -0
- patchi/core/plugins/__init__.py +48 -0
- patchi/core/plugins/analyzer.py +326 -0
- patchi/core/plugins/analyzers/__init__.py +6 -0
- patchi/core/plugins/analyzers/dead_code_analyzer.py +120 -0
- patchi/core/plugins/analyzers/secret_scanner.py +158 -0
- patchi/core/plugins/analyzers/todo_scanner.py +115 -0
- patchi/core/plugins/registry.py +335 -0
- patchi/core/queue.py +375 -0
- patchi/core/reliability/__init__.py +13 -0
- patchi/core/reliability/chaos.py +163 -0
- patchi/core/reliability/fault_injector.py +172 -0
- patchi/core/reliability/idempotency.py +128 -0
- patchi/core/reliability/recovery.py +147 -0
- patchi/core/runtime/__init__.py +5 -0
- patchi/core/runtime/tracer.py +230 -0
- patchi/core/scan_bus.py +156 -0
- patchi/core/security/__init__.py +1 -0
- patchi/core/security/ai_validator.py +462 -0
- patchi/core/security/app_mapper.py +216 -0
- patchi/core/security/app_profile.py +464 -0
- patchi/core/security/attack_feedback.py +141 -0
- patchi/core/security/attack_scenarios/__init__.py +1 -0
- patchi/core/security/attack_tree.py +176 -0
- patchi/core/security/auth_audit_agent.py +344 -0
- patchi/core/security/authz_agent.py +602 -0
- patchi/core/security/auto_fix_proactive.py +620 -0
- patchi/core/security/auto_fixer.py +660 -0
- patchi/core/security/auto_ticket.py +53 -0
- patchi/core/security/bandit_agent.py +152 -0
- patchi/core/security/blast_radius.py +226 -0
- patchi/core/security/browser_tester.py +333 -0
- patchi/core/security/business_logic_agent.py +263 -0
- patchi/core/security/catch_block_auditor.py +395 -0
- patchi/core/security/cdn_cache_agent.py +357 -0
- patchi/core/security/chain_analyzer.py +321 -0
- patchi/core/security/chain_to_assurance.py +168 -0
- patchi/core/security/charter.py +510 -0
- patchi/core/security/cloud_waf_detector.py +295 -0
- patchi/core/security/codeql_agent.py +233 -0
- patchi/core/security/compliance_agent.py +348 -0
- patchi/core/security/confidence_gate.py +423 -0
- patchi/core/security/container_scanner.py +282 -0
- patchi/core/security/crypto_agent.py +378 -0
- patchi/core/security/cve_monitor.py +262 -0
- patchi/core/security/dast_agent.py +865 -0
- patchi/core/security/defectdojo.py +238 -0
- patchi/core/security/defenders/__init__.py +75 -0
- patchi/core/security/defenders/auth_middleware.py +115 -0
- patchi/core/security/defenders/base.py +97 -0
- patchi/core/security/defenders/block_ip.py +71 -0
- patchi/core/security/defenders/block_ws_origin.py +36 -0
- patchi/core/security/defenders/crypto_fix.py +118 -0
- patchi/core/security/defenders/enforce_rate_limit.py +108 -0
- patchi/core/security/defenders/fix_code.py +93 -0
- patchi/core/security/defenders/invalidate_session.py +40 -0
- patchi/core/security/defenders/patch_config.py +70 -0
- patchi/core/security/defenders/rotate_secret.py +54 -0
- patchi/core/security/defenders/suspend_account.py +41 -0
- patchi/core/security/defenders/update_dependency.py +160 -0
- patchi/core/security/defense_layer.py +217 -0
- patchi/core/security/dependency_vulnerability_agent.py +616 -0
- patchi/core/security/detection_pipeline.py +289 -0
- patchi/core/security/dns_security_agent.py +497 -0
- patchi/core/security/domain_activator_v2.py +913 -0
- patchi/core/security/domain_loader.py +1004 -0
- patchi/core/security/domains/__init__.py +0 -0
- patchi/core/security/email_authentication_agent.py +417 -0
- patchi/core/security/env_var_validator.py +304 -0
- patchi/core/security/evidence.py +100 -0
- patchi/core/security/falco_runtime_agent.py +387 -0
- patchi/core/security/fix-playbooks/__init__.py +0 -0
- patchi/core/security/gated_finding.py +85 -0
- patchi/core/security/git_diff_activator.py +367 -0
- patchi/core/security/governance.py +208 -0
- patchi/core/security/gradual_gate.py +34 -0
- patchi/core/security/history.py +315 -0
- patchi/core/security/iac_scanner.py +250 -0
- patchi/core/security/ignore_expiry.py +111 -0
- patchi/core/security/ignore_learner.py +416 -0
- patchi/core/security/injection_agent.py +319 -0
- patchi/core/security/insecure_randomness_agent.py +288 -0
- patchi/core/security/intent_analyzer.py +326 -0
- patchi/core/security/jwt_agent.py +453 -0
- patchi/core/security/kubernetes_agent.py +638 -0
- patchi/core/security/layer2_orchestrator.py +341 -0
- patchi/core/security/llm_security_agent.py +252 -0
- patchi/core/security/misconfig_agent.py +600 -0
- patchi/core/security/mobile_security_agent.py +474 -0
- patchi/core/security/network_agent.py +376 -0
- patchi/core/security/noise_filter.py +274 -0
- patchi/core/security/orchestrator.py +313 -0
- patchi/core/security/pattern_context.py +125 -0
- patchi/core/security/pattern_loader.py +101 -0
- patchi/core/security/pentest/__init__.py +4 -0
- patchi/core/security/pentest/base.py +58 -0
- patchi/core/security/pentest/dalfox_adapter.py +40 -0
- patchi/core/security/pentest/ffuf_adapter.py +48 -0
- patchi/core/security/pentest/nuclei_adapter.py +50 -0
- patchi/core/security/pentest/registry.py +80 -0
- patchi/core/security/pentest/shannon_adapter.py +107 -0
- patchi/core/security/pentest/sqlmap_adapter.py +56 -0
- patchi/core/security/pentest/zap_adapter.py +52 -0
- patchi/core/security/plan_auditor.py +276 -0
- patchi/core/security/policy_engine.py +272 -0
- patchi/core/security/prechecks.py +172 -0
- patchi/core/security/precommit_hook.py +247 -0
- patchi/core/security/privacy_agent.py +361 -0
- patchi/core/security/push_notification_agent.py +417 -0
- patchi/core/security/pysa_agent.py +143 -0
- patchi/core/security/reasoning.py +442 -0
- patchi/core/security/red_team_agent.py +184 -0
- patchi/core/security/red_team_engine.py +1142 -0
- patchi/core/security/remediation.py +360 -0
- patchi/core/security/request_interceptor.py +294 -0
- patchi/core/security/runtime_validator.py +245 -0
- patchi/core/security/saml_sso_agent.py +401 -0
- patchi/core/security/sast_agent.py +213 -0
- patchi/core/security/sast_gate.py +95 -0
- patchi/core/security/scheduler.py +287 -0
- patchi/core/security/secrets_runtime_agent.py +476 -0
- patchi/core/security/security_agents.py +175 -0
- patchi/core/security/security_config.py +551 -0
- patchi/core/security/security_probe.py +762 -0
- patchi/core/security/security_taint.py +1005 -0
- patchi/core/security/sensitive_data_agent.py +271 -0
- patchi/core/security/service_mesh_agent.py +412 -0
- patchi/core/security/session_management_agent.py +245 -0
- patchi/core/security/ssrf_agent.py +422 -0
- patchi/core/security/supply_chain.py +449 -0
- patchi/core/security/threat_model_generator.py +378 -0
- patchi/core/security/threat_model_updater.py +196 -0
- patchi/core/security/tool_adapters.py +135 -0
- patchi/core/security/tool_verify.py +173 -0
- patchi/core/security/websocket_security_agent.py +188 -0
- patchi/core/snapshot.py +242 -0
- patchi/core/tenant.py +386 -0
- patchi/core/testing/__init__.py +17 -0
- patchi/core/testing/_browser.py +147 -0
- patchi/core/testing/ads_agent.py +141 -0
- patchi/core/testing/api_contract_agent.py +450 -0
- patchi/core/testing/app_discovery_agent.py +641 -0
- patchi/core/testing/app_launcher.py +184 -0
- patchi/core/testing/browser_test_agent.py +516 -0
- patchi/core/testing/e2e_flow_agent.py +354 -0
- patchi/core/testing/flake_detector_agent.py +292 -0
- patchi/core/testing/gate.py +31 -0
- patchi/core/testing/live_test_runner.py +304 -0
- patchi/core/testing/live_v2/__init__.py +1 -0
- patchi/core/testing/live_v2/browser_pool.py +349 -0
- patchi/core/testing/live_v2/browser_test_runner.py +313 -0
- patchi/core/testing/live_v2/runner.py +626 -0
- patchi/core/testing/live_v2/screenshot_manager.py +474 -0
- patchi/core/testing/live_v2/stress_orchestrator.py +523 -0
- patchi/core/testing/live_v2/video_recorder.py +303 -0
- patchi/core/testing/regression_agent.py +302 -0
- patchi/core/testing/security_test_agent.py +253 -0
- patchi/core/testing/stress_test_agent.py +203 -0
- patchi/core/testing/test_agents.py +50 -0
- patchi/core/testing/test_config.py +194 -0
- patchi/core/testing/ui_accessibility_agent.py +324 -0
- patchi/core/testing/ui_button_agent.py +294 -0
- patchi/core/testing/ui_layout_agent.py +330 -0
- patchi/core/testing/unit_test_agent.py +1149 -0
- patchi/core/testing/visual_regression_agent.py +260 -0
- patchi/web/__init__.py +1 -0
- patchi/web/api/__init__.py +1 -0
- patchi/web/api/brain_map.py +114 -0
- patchi/web/api/charts.py +47 -0
- patchi/web/api/chat.py +224 -0
- patchi/web/api/cicd.py +576 -0
- patchi/web/api/dast_evidence.py +49 -0
- patchi/web/api/dev_check.py +320 -0
- patchi/web/api/fix.py +134 -0
- patchi/web/api/guard.py +65 -0
- patchi/web/api/hosted.py +128 -0
- patchi/web/api/hosted_v2.py +234 -0
- patchi/web/api/live_testing.py +699 -0
- patchi/web/api/scan.py +582 -0
- patchi/web/api/smart.py +111 -0
- patchi/web/api/tenant.py +117 -0
- patchi/web/api/tools.py +85 -0
- patchi/web/api_legacy.py +2401 -0
- patchi/web/app.py +310 -0
- patchi/web/events.py +483 -0
- patchi/web/routes/__init__.py +1 -0
- patchi/web/routes/assurance.py +541 -0
- patchi/web/routes/brain.py +36 -0
- patchi/web/routes/charter.py +227 -0
- patchi/web/routes/chat.py +23 -0
- patchi/web/routes/dashboard.py +558 -0
- patchi/web/routes/dashboard_v2.py +402 -0
- patchi/web/routes/findings.py +330 -0
- patchi/web/routes/guard.py +35 -0
- patchi/web/routes/history.py +126 -0
- patchi/web/routes/landing.py +52 -0
- patchi/web/routes/live_testing.py +24 -0
- patchi/web/routes/review.py +57 -0
- patchi/web/routes/self_improvement.py +110 -0
- patchi/web/routes/settings.py +49 -0
- patchi/web/routes/tokens.py +29 -0
- patchi/web/spawn.py +139 -0
- patchi/web/ws.py +141 -0
- patchi-0.7.5.dist-info/METADATA +315 -0
- patchi-0.7.5.dist-info/RECORD +496 -0
- patchi-0.7.5.dist-info/WHEEL +5 -0
- patchi-0.7.5.dist-info/licenses/LICENSE +60 -0
- patchi-0.7.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Live Cockpit — data layer for `p cockpit` (a.k.a. `p watch --live`).
|
|
3
|
+
|
|
4
|
+
This is a *view over the brain that already exists*. It adds no analysis of its
|
|
5
|
+
own; it just gathers signals the rest of Patchi already computes and shapes them
|
|
6
|
+
for a single glanceable dashboard:
|
|
7
|
+
|
|
8
|
+
health → patchi.core.health.compute
|
|
9
|
+
drift → patchi.core.brain.audit.compute_drift (Plan-vs-Built)
|
|
10
|
+
fixes → patchi.core.brain.proactive.build_fix_list (Prioritized Fix List)
|
|
11
|
+
blast → patchi.core.brain.reasoning.impact_analysis (change blast radius)
|
|
12
|
+
secrets → patchi.core.brain.secrets.scan_secrets (on-change sweep)
|
|
13
|
+
|
|
14
|
+
Every patchi import is wrapped so the cockpit still renders (in a degraded state)
|
|
15
|
+
when the brain has not been built yet — it just tells the user to run `p scan`.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import threading
|
|
22
|
+
import time
|
|
23
|
+
from collections import deque
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from patchi.core.brain.file_corpus import FileCorpus
|
|
30
|
+
|
|
31
|
+
from patchi.core.brain.languages import DEFAULT_IGNORE_DIRS
|
|
32
|
+
|
|
33
|
+
_log = logging.getLogger("patchi.core.brain.cockpit")
|
|
34
|
+
|
|
35
|
+
# ── Watcher config (self-contained, no watchdog dependency) ──────────────────────
|
|
36
|
+
|
|
37
|
+
_WATCH_EXTS = {
|
|
38
|
+
".py",
|
|
39
|
+
".js",
|
|
40
|
+
".ts",
|
|
41
|
+
".tsx",
|
|
42
|
+
".jsx",
|
|
43
|
+
".rs",
|
|
44
|
+
".go",
|
|
45
|
+
".rb",
|
|
46
|
+
".java",
|
|
47
|
+
".env",
|
|
48
|
+
".toml",
|
|
49
|
+
".yaml",
|
|
50
|
+
".yml",
|
|
51
|
+
".json",
|
|
52
|
+
".cfg",
|
|
53
|
+
".ini",
|
|
54
|
+
}
|
|
55
|
+
_SKIP_DIRS = DEFAULT_IGNORE_DIRS | {".turbo", ".codebase-memory"}
|
|
56
|
+
_MAX_WATCH_FILES = 20_000
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ── State ────────────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class Event:
|
|
64
|
+
ts: float
|
|
65
|
+
level: str # info | warn | crit
|
|
66
|
+
text: str
|
|
67
|
+
|
|
68
|
+
def stamp(self) -> str:
|
|
69
|
+
return time.strftime("%H:%M:%S", time.localtime(self.ts))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class CockpitState:
|
|
74
|
+
root: Path
|
|
75
|
+
area: str | None = None
|
|
76
|
+
|
|
77
|
+
health_total: int | None = None
|
|
78
|
+
health_grade: str = "?"
|
|
79
|
+
health_color: str = "#FACC15"
|
|
80
|
+
|
|
81
|
+
drift: dict = field(default_factory=dict)
|
|
82
|
+
fixes: list[dict] = field(default_factory=list)
|
|
83
|
+
|
|
84
|
+
last_file: str = ""
|
|
85
|
+
blast_affected: list[str] = field(default_factory=list)
|
|
86
|
+
blast_impacted: list[str] = field(default_factory=list)
|
|
87
|
+
blast_summary: str = ""
|
|
88
|
+
|
|
89
|
+
secrets: list[dict] = field(default_factory=list) # deduped, most-recent last
|
|
90
|
+
events: deque = field(default_factory=lambda: deque(maxlen=200))
|
|
91
|
+
|
|
92
|
+
metrics_at: float = 0.0
|
|
93
|
+
fixes_at: float = 0.0
|
|
94
|
+
fixes_computing: bool = False
|
|
95
|
+
_fixes_lock: threading.Lock = field(default_factory=threading.Lock, repr=False)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def add_event(state: CockpitState, level: str, text: str) -> None:
|
|
99
|
+
state.events.append(Event(ts=time.time(), level=level, text=text))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ── Signal gatherers (each fully guarded) ────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _get_health(state: CockpitState) -> None:
|
|
106
|
+
try:
|
|
107
|
+
from patchi.core.health import compute
|
|
108
|
+
|
|
109
|
+
hs = compute(state.root)
|
|
110
|
+
state.health_total = hs.total
|
|
111
|
+
state.health_grade = hs.grade
|
|
112
|
+
state.health_color = hs.color
|
|
113
|
+
except Exception as e:
|
|
114
|
+
# Most common cause is simply "no brain built yet" (p scan not run),
|
|
115
|
+
# which is an expected, frequent state on a fresh project — debug, not
|
|
116
|
+
# warning, so a normal cockpit session against an unscanned repo doesn't
|
|
117
|
+
# spam the log on every refresh interval.
|
|
118
|
+
_log.debug("Health computation unavailable (likely no brain yet): %s", e)
|
|
119
|
+
state.health_total = None
|
|
120
|
+
state.health_grade = "?"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _get_drift(state: CockpitState) -> None:
|
|
124
|
+
try:
|
|
125
|
+
from patchi.core.brain.audit import compute_drift
|
|
126
|
+
|
|
127
|
+
state.drift = compute_drift(state.root) or {}
|
|
128
|
+
except Exception as e:
|
|
129
|
+
_log.debug("Drift computation unavailable (likely no brain/plan yet): %s", e)
|
|
130
|
+
state.drift = {}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _get_fixes(state: CockpitState) -> None:
|
|
134
|
+
try:
|
|
135
|
+
from patchi.core.brain.proactive import build_fix_list, rank_fixes
|
|
136
|
+
|
|
137
|
+
fixes = build_fix_list(state.root, area=state.area)
|
|
138
|
+
fixes = rank_fixes(fixes)
|
|
139
|
+
state.fixes = [
|
|
140
|
+
{
|
|
141
|
+
"fix_type": f.fix_type,
|
|
142
|
+
"file": f.file,
|
|
143
|
+
"name": f.name,
|
|
144
|
+
"description": f.description,
|
|
145
|
+
"safe": f.safe,
|
|
146
|
+
}
|
|
147
|
+
for f in fixes
|
|
148
|
+
]
|
|
149
|
+
except Exception as e:
|
|
150
|
+
_log.debug("Fix list computation unavailable (likely no brain yet): %s", e)
|
|
151
|
+
state.fixes = []
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def update_metrics(state: CockpitState) -> None:
|
|
155
|
+
"""Refresh the *fast* whole-project signals (health + drift, both <0.1s)."""
|
|
156
|
+
_get_health(state)
|
|
157
|
+
_get_drift(state)
|
|
158
|
+
state.metrics_at = time.time()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def refresh_fixes_async(state: CockpitState) -> threading.Thread | None:
|
|
162
|
+
"""
|
|
163
|
+
Recompute the Prioritized Fix List off the UI thread.
|
|
164
|
+
|
|
165
|
+
``build_fix_list`` scans the whole project (import graph + proactive agent)
|
|
166
|
+
and can take many seconds on large repos, so it must never block rendering.
|
|
167
|
+
A lock ensures only one computation runs at a time; the UI shows a
|
|
168
|
+
'computing…' hint while ``fixes_computing`` is set.
|
|
169
|
+
|
|
170
|
+
Returns the background Thread (or None if a computation was already running
|
|
171
|
+
and this call was a no-op) so a caller that wants a best-effort bounded wait
|
|
172
|
+
— e.g. ``--once`` — can ``.join(timeout=...)`` it instead of blocking forever.
|
|
173
|
+
"""
|
|
174
|
+
if state.fixes_computing:
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
def _work() -> None:
|
|
178
|
+
with state._fixes_lock:
|
|
179
|
+
state.fixes_computing = True
|
|
180
|
+
try:
|
|
181
|
+
_get_fixes(state)
|
|
182
|
+
state.fixes_at = time.time()
|
|
183
|
+
finally:
|
|
184
|
+
state.fixes_computing = False
|
|
185
|
+
|
|
186
|
+
t = threading.Thread(target=_work, daemon=True)
|
|
187
|
+
t.start()
|
|
188
|
+
return t
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def gather_full(
|
|
192
|
+
root: Path,
|
|
193
|
+
area: str | None = None,
|
|
194
|
+
scan_secrets: bool = False,
|
|
195
|
+
fixes_sync: bool = False,
|
|
196
|
+
fixes_timeout: float = 12.0,
|
|
197
|
+
) -> CockpitState:
|
|
198
|
+
"""
|
|
199
|
+
Build a fresh cockpit state (called once at startup).
|
|
200
|
+
|
|
201
|
+
Fast signals (health, drift) are computed synchronously; the fix list is
|
|
202
|
+
always computed in the background so a large project can never hang the
|
|
203
|
+
cockpit. When ``fixes_sync`` is set (used by ``--once``, so a single frame
|
|
204
|
+
ideally contains everything), this *waits up to ``fixes_timeout`` seconds*
|
|
205
|
+
for that background computation to finish before returning — a bounded
|
|
206
|
+
best-effort wait, not a blocking call. On a large project where the fix
|
|
207
|
+
list genuinely can't finish in time, the frame still renders on schedule
|
|
208
|
+
with ``fixes_computing`` left set, rather than hanging indefinitely.
|
|
209
|
+
"""
|
|
210
|
+
state = CockpitState(root=Path(root), area=area)
|
|
211
|
+
update_metrics(state)
|
|
212
|
+
|
|
213
|
+
if state.health_total is None:
|
|
214
|
+
add_event(state, "warn", "No brain found — run `p scan` to populate the cockpit.")
|
|
215
|
+
else:
|
|
216
|
+
add_event(
|
|
217
|
+
state, "info", f"Cockpit armed · health {state.health_total} ({state.health_grade})"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
thread = refresh_fixes_async(state)
|
|
221
|
+
if fixes_sync and thread is not None:
|
|
222
|
+
thread.join(timeout=fixes_timeout)
|
|
223
|
+
if state.fixes_computing:
|
|
224
|
+
add_event(
|
|
225
|
+
state,
|
|
226
|
+
"warn",
|
|
227
|
+
f"Fix list still computing after {fixes_timeout:.0f}s (large project) — "
|
|
228
|
+
"showing partial frame; scope with --area for a faster pass.",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if scan_secrets:
|
|
232
|
+
_sweep_secrets(state, paths=None, announce=True)
|
|
233
|
+
|
|
234
|
+
return state
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# ── On-change refresh (cheap, scoped to the saved files) ─────────────────────────
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def refresh_on_change(state: CockpitState, changed: list[str]) -> None:
|
|
241
|
+
if not changed:
|
|
242
|
+
return
|
|
243
|
+
state.last_file = changed[-1]
|
|
244
|
+
add_event(
|
|
245
|
+
state,
|
|
246
|
+
"info",
|
|
247
|
+
f"saved {', '.join(changed[:3])}"
|
|
248
|
+
+ (f" (+{len(changed) - 3} more)" if len(changed) > 3 else ""),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
_get_blast(state, changed)
|
|
252
|
+
_sweep_secrets(state, paths=changed, announce=False)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _get_blast(state: CockpitState, changed: list[str]) -> None:
|
|
256
|
+
try:
|
|
257
|
+
from patchi.core.brain.reasoning import ReasoningEngine
|
|
258
|
+
|
|
259
|
+
analysis = ReasoningEngine(state.root).impact_analysis(changed)
|
|
260
|
+
state.blast_affected = list(analysis.affected_layers)
|
|
261
|
+
state.blast_impacted = list(analysis.impacted_layers)
|
|
262
|
+
state.blast_summary = analysis.summary
|
|
263
|
+
if analysis.impacted_layers:
|
|
264
|
+
add_event(
|
|
265
|
+
state, "warn", f"blast radius: {len(analysis.impacted_layers)} downstream layer(s)"
|
|
266
|
+
)
|
|
267
|
+
except Exception as e:
|
|
268
|
+
# Triggered per-save during an active session (not the one-time "fresh
|
|
269
|
+
# project" case above) -- more likely a real problem, so this is worth
|
|
270
|
+
# a warning rather than debug.
|
|
271
|
+
_log.warning("Blast radius analysis failed for %s: %s", changed, e)
|
|
272
|
+
state.blast_summary = "Impact needs a layered brain — run `p scan`."
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _sweep_secrets(state: CockpitState, paths: list[str] | None, announce: bool) -> None:
|
|
276
|
+
try:
|
|
277
|
+
from patchi.core.brain.secrets import scan_secrets
|
|
278
|
+
|
|
279
|
+
hits = scan_secrets(state.root, paths)
|
|
280
|
+
seen = {(s["path"], s["line"], s["rule"]) for s in state.secrets}
|
|
281
|
+
new = 0
|
|
282
|
+
for h in hits:
|
|
283
|
+
key = (h.path, h.line, h.rule)
|
|
284
|
+
if key in seen:
|
|
285
|
+
continue
|
|
286
|
+
seen.add(key)
|
|
287
|
+
state.secrets.append({"path": h.path, "line": h.line, "rule": h.rule})
|
|
288
|
+
new += 1
|
|
289
|
+
if new:
|
|
290
|
+
add_event(
|
|
291
|
+
state, "crit", f"{new} secret(s) detected — {hits[-1].rule} in {hits[-1].path}"
|
|
292
|
+
)
|
|
293
|
+
elif announce:
|
|
294
|
+
add_event(state, "info", "secrets sweep clean")
|
|
295
|
+
except Exception as e:
|
|
296
|
+
# A silent failure here is exactly the dangerous case for a security
|
|
297
|
+
# tool: the cockpit would look clean while a secrets sweep never
|
|
298
|
+
# actually ran. Warning, not debug.
|
|
299
|
+
_log.warning("Secrets sweep failed for paths=%s: %s", paths, e)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# ── Self-contained source watcher (mtime poll, no external deps) ─────────────────
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class SourceWatcher:
|
|
306
|
+
def __init__(self, root: Path, corpus: FileCorpus | None = None):
|
|
307
|
+
self.root = Path(root)
|
|
308
|
+
self._corpus = corpus
|
|
309
|
+
self._mtimes = self._scan()
|
|
310
|
+
|
|
311
|
+
def _scan(self) -> dict[str, float]:
|
|
312
|
+
out: dict[str, float] = {}
|
|
313
|
+
count = 0
|
|
314
|
+
if self._corpus is not None:
|
|
315
|
+
for entry in self._corpus.entries.values():
|
|
316
|
+
if count >= _MAX_WATCH_FILES:
|
|
317
|
+
break
|
|
318
|
+
if entry.abs_path.suffix.lower() not in _WATCH_EXTS:
|
|
319
|
+
continue
|
|
320
|
+
try:
|
|
321
|
+
out[entry.abs_path.as_posix()] = entry.abs_path.stat().st_mtime
|
|
322
|
+
count += 1
|
|
323
|
+
except OSError:
|
|
324
|
+
continue
|
|
325
|
+
else:
|
|
326
|
+
for p in self.root.rglob("*"):
|
|
327
|
+
if count >= _MAX_WATCH_FILES:
|
|
328
|
+
break
|
|
329
|
+
try:
|
|
330
|
+
if any(part in _SKIP_DIRS for part in p.parts):
|
|
331
|
+
continue
|
|
332
|
+
if p.suffix.lower() not in _WATCH_EXTS:
|
|
333
|
+
continue
|
|
334
|
+
if not p.is_file():
|
|
335
|
+
continue
|
|
336
|
+
out[p.as_posix()] = p.stat().st_mtime
|
|
337
|
+
count += 1
|
|
338
|
+
except OSError:
|
|
339
|
+
continue
|
|
340
|
+
return out
|
|
341
|
+
|
|
342
|
+
def poll(self) -> list[str]:
|
|
343
|
+
current = self._scan()
|
|
344
|
+
changed = [
|
|
345
|
+
str(Path(p).relative_to(self.root).as_posix())
|
|
346
|
+
for p, m in current.items()
|
|
347
|
+
if self._mtimes.get(p) != m
|
|
348
|
+
]
|
|
349
|
+
self._mtimes = current
|
|
350
|
+
return changed
|