cycode 3.15.4.dev1__tar.gz → 3.15.4.dev3__tar.gz
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.
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/PKG-INFO +3 -1
- cycode-3.15.4.dev3/cycode/__init__.py +1 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/hooks_manager.py +83 -25
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/ides/__init__.py +2 -1
- cycode-3.15.4.dev3/cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py +73 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/ides/base.py +20 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/ides/claude_code.py +32 -52
- cycode-3.15.4.dev3/cycode/cli/apps/ai_guardrails/ides/codex.py +310 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/ides/cursor.py +1 -1
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/handlers.py +66 -22
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/scan_command.py +20 -2
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/jwt_utils.py +8 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/pyproject.toml +3 -1
- cycode-3.15.4.dev1/cycode/__init__.py +0 -1
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/LICENCE +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/README.md +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/__main__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/app.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/activation_manager.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/consts.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/install_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/types.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/session_start_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/api/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/api/api_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/api/openapi_spec.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/auth/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/auth/auth_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/auth/auth_common.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/auth/auth_manager.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/auth/models.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/configure/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/configure/configure_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/configure/consts.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/configure/messages.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/configure/prompts.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ignore/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/ignore/ignore_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/mcp/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/mcp/mcp_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/report_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/common.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report_import/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report_import/report_import_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/sca_options.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/aggregation_report.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/code_scanner.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/detection_excluder.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/path/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/path/path_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/repository/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/scan_parameters.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/scan/scan_result.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/status/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/status/get_cli_status.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/status/models.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/status/status_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/apps/status/version_command.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/cli_types.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/config.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/console.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/consts.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/exceptions/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/exceptions/handle_errors.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/commit_range_documents.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/file_excluder.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/iac/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/models/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/php/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/python/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/walk_ignore.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/logger.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/main.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/models.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/console_printer.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/rich_printer.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/tables/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/utils/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/utils/detection_data.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/printers/utils/rich_helpers.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/user_settings/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/user_settings/configuration_manager.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/binary_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/ignore_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/path_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/url_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/version_checker.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/config.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/__init__.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/ai_security_manager_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/auth_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/base_token_auth_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/cli_activation_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/client_creator.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/config.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/config_dev.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/cycode_client_base.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/headers.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/import_sbom_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/logger.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/models.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/report_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/scan_client.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/cyclient/scan_config_base.py +0 -0
- {cycode-3.15.4.dev1 → cycode-3.15.4.dev3}/cycode/logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cycode
|
|
3
|
-
Version: 3.15.4.
|
|
3
|
+
Version: 3.15.4.dev3
|
|
4
4
|
Summary: Boost security in your dev lifecycle via SAST, SCA, Secrets & IaC scanning.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENCE
|
|
@@ -34,6 +34,8 @@ Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
|
34
34
|
Requires-Dist: requests (>=2.32.4,<3.0)
|
|
35
35
|
Requires-Dist: rich (>=13.9.4,<14)
|
|
36
36
|
Requires-Dist: tenacity (>=9.0.0,<9.1.0)
|
|
37
|
+
Requires-Dist: tomli (>=2.0.0,<3.0.0) ; python_version < "3.11"
|
|
38
|
+
Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
|
|
37
39
|
Requires-Dist: typer (>=0.15.3,<0.16.0)
|
|
38
40
|
Requires-Dist: urllib3 (>=2.4.0,<3.0.0)
|
|
39
41
|
Project-URL: Repository, https://github.com/cycodehq/cycode-cli
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '3.15.4.dev3' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -28,7 +28,7 @@ def _is_cycode_command(command: str) -> bool:
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def is_cycode_hook_entry(entry: dict) -> bool:
|
|
31
|
-
"""
|
|
31
|
+
"""True if any hook inside ``entry`` is owned by Cycode."""
|
|
32
32
|
command = entry.get('command', '')
|
|
33
33
|
if _is_cycode_command(command):
|
|
34
34
|
return True
|
|
@@ -40,6 +40,31 @@ def is_cycode_hook_entry(entry: dict) -> bool:
|
|
|
40
40
|
return False
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def _strip_cycode_from_entry(entry: dict) -> Optional[dict]:
|
|
44
|
+
"""Remove Cycode hooks from ``entry`` and return the remainder.
|
|
45
|
+
|
|
46
|
+
Returns ``None`` when nothing useful remains (Cursor-flat Cycode entry, or
|
|
47
|
+
every nested hook was Cycode). Non-Cycode hooks co-located in the same
|
|
48
|
+
entry are preserved.
|
|
49
|
+
"""
|
|
50
|
+
# Cursor format: the entry itself IS a single hook command.
|
|
51
|
+
if 'command' in entry and 'hooks' not in entry:
|
|
52
|
+
return None if _is_cycode_command(entry.get('command', '')) else entry
|
|
53
|
+
|
|
54
|
+
# Claude Code / Codex format: nested `hooks` list inside the entry.
|
|
55
|
+
nested = entry.get('hooks')
|
|
56
|
+
if isinstance(nested, list):
|
|
57
|
+
kept = [h for h in nested if not (isinstance(h, dict) and _is_cycode_command(h.get('command', '')))]
|
|
58
|
+
if not kept:
|
|
59
|
+
return None
|
|
60
|
+
if len(kept) == len(nested):
|
|
61
|
+
return entry # nothing Cycode-shaped inside; preserve identity
|
|
62
|
+
return {**entry, 'hooks': kept}
|
|
63
|
+
|
|
64
|
+
# Entry has neither shape we recognize — leave it alone defensively.
|
|
65
|
+
return entry
|
|
66
|
+
|
|
67
|
+
|
|
43
68
|
def _load_hooks_file(hooks_path: Path) -> Optional[dict]:
|
|
44
69
|
if not hooks_path.exists():
|
|
45
70
|
return None
|
|
@@ -108,50 +133,83 @@ def install_hooks(
|
|
|
108
133
|
|
|
109
134
|
for event, entries in rendered['hooks'].items():
|
|
110
135
|
existing['hooks'].setdefault(event, [])
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# Add new Cycode entries
|
|
136
|
+
existing['hooks'][event] = [
|
|
137
|
+
stripped for e in existing['hooks'][event] if (stripped := _strip_cycode_from_entry(e)) is not None
|
|
138
|
+
]
|
|
116
139
|
for entry in entries:
|
|
117
140
|
existing['hooks'][event].append(entry)
|
|
118
141
|
|
|
119
|
-
if _save_hooks_file(hooks_path, existing):
|
|
120
|
-
return
|
|
121
|
-
return False, f'Failed to install hooks to {hooks_path}'
|
|
142
|
+
if not _save_hooks_file(hooks_path, existing):
|
|
143
|
+
return False, f'Failed to install hooks to {hooks_path}'
|
|
122
144
|
|
|
145
|
+
message = f'AI guardrails hooks installed: {hooks_path}'
|
|
123
146
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
147
|
+
# IDE-specific extras (e.g. Codex enables a TOML feature flag).
|
|
148
|
+
extra_ok, extra_message = ide.post_install(scope, repo_path)
|
|
149
|
+
if not extra_ok:
|
|
150
|
+
return False, extra_message
|
|
151
|
+
if extra_message:
|
|
152
|
+
message = f'{message}\n {extra_message}'
|
|
127
153
|
|
|
128
|
-
|
|
129
|
-
if existing is None:
|
|
130
|
-
return True, f'No hooks file found at {hooks_path}'
|
|
154
|
+
return True, message
|
|
131
155
|
|
|
156
|
+
|
|
157
|
+
def _strip_cycode_entries(existing: dict) -> bool:
|
|
158
|
+
"""Mutate ``existing`` to drop Cycode hooks (surgically). Return True if anything changed."""
|
|
132
159
|
modified = False
|
|
133
160
|
for event in list(existing.get('hooks', {}).keys()):
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
before = existing['hooks'][event]
|
|
162
|
+
after: list = []
|
|
163
|
+
for e in before:
|
|
164
|
+
stripped = _strip_cycode_from_entry(e)
|
|
165
|
+
if stripped is None:
|
|
166
|
+
modified = True
|
|
167
|
+
continue
|
|
168
|
+
if stripped is not e:
|
|
169
|
+
modified = True
|
|
170
|
+
after.append(stripped)
|
|
171
|
+
if not after:
|
|
139
172
|
del existing['hooks'][event]
|
|
173
|
+
else:
|
|
174
|
+
existing['hooks'][event] = after
|
|
175
|
+
return modified
|
|
140
176
|
|
|
177
|
+
|
|
178
|
+
def _persist_uninstall(hooks_path: Path, existing: dict, modified: bool) -> tuple[bool, str]:
|
|
179
|
+
"""Apply the uninstall result to disk and return ``(success, message)``."""
|
|
141
180
|
if not modified:
|
|
142
181
|
return True, 'No Cycode hooks found to remove'
|
|
143
|
-
|
|
144
182
|
if not existing.get('hooks'):
|
|
145
183
|
try:
|
|
146
184
|
hooks_path.unlink()
|
|
147
|
-
return True, f'Removed hooks file: {hooks_path}'
|
|
148
185
|
except Exception as e:
|
|
149
186
|
logger.debug('Failed to delete hooks file', exc_info=e)
|
|
150
187
|
return False, f'Failed to remove hooks file: {hooks_path}'
|
|
188
|
+
return True, f'Removed hooks file: {hooks_path}'
|
|
189
|
+
if not _save_hooks_file(hooks_path, existing):
|
|
190
|
+
return False, f'Failed to update hooks file: {hooks_path}'
|
|
191
|
+
return True, f'Cycode hooks removed from: {hooks_path}'
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def uninstall_hooks(ide: IDE, scope: str = 'user', repo_path: Optional[Path] = None) -> tuple[bool, str]:
|
|
195
|
+
"""Remove Cycode AI guardrails hooks for ``ide``."""
|
|
196
|
+
hooks_path = ide.settings_path(scope, repo_path)
|
|
197
|
+
|
|
198
|
+
existing = _load_hooks_file(hooks_path)
|
|
199
|
+
if existing is None:
|
|
200
|
+
return True, f'No hooks file found at {hooks_path}'
|
|
151
201
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
202
|
+
modified = _strip_cycode_entries(existing)
|
|
203
|
+
file_ok, message = _persist_uninstall(hooks_path, existing, modified)
|
|
204
|
+
if not file_ok:
|
|
205
|
+
return False, message
|
|
206
|
+
|
|
207
|
+
extra_ok, extra_message = ide.post_uninstall(scope, repo_path)
|
|
208
|
+
if not extra_ok:
|
|
209
|
+
return False, extra_message
|
|
210
|
+
if extra_message:
|
|
211
|
+
message = f'{message}\n {extra_message}'
|
|
212
|
+
return True, message
|
|
155
213
|
|
|
156
214
|
|
|
157
215
|
def get_hooks_status(ide: IDE, scope: str = 'user', repo_path: Optional[Path] = None) -> dict:
|
|
@@ -9,11 +9,12 @@ import typer
|
|
|
9
9
|
|
|
10
10
|
from cycode.cli.apps.ai_guardrails.ides.base import IDE
|
|
11
11
|
from cycode.cli.apps.ai_guardrails.ides.claude_code import ClaudeCode
|
|
12
|
+
from cycode.cli.apps.ai_guardrails.ides.codex import Codex
|
|
12
13
|
from cycode.cli.apps.ai_guardrails.ides.cursor import Cursor
|
|
13
14
|
|
|
14
15
|
# Single source of truth: name → singleton instance.
|
|
15
16
|
# `--ide` choices and install/uninstall/status iteration both derive from this.
|
|
16
|
-
IDES: dict[str, IDE] = {ide.name: ide for ide in (Cursor(), ClaudeCode())}
|
|
17
|
+
IDES: dict[str, IDE] = {ide.name: ide for ide in (Cursor(), ClaudeCode(), Codex())}
|
|
17
18
|
|
|
18
19
|
# Default IDE used when `--ide` is omitted. Kept here so the value is colocated
|
|
19
20
|
# with the registry; no module outside `ides/` needs to know which IDE wins.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Shared plugin-resolution helpers for IDE integrations.
|
|
2
|
+
|
|
3
|
+
Both Claude Code and Codex use the same ``<plugin>@<marketplace>`` key convention
|
|
4
|
+
and emit the same telemetry shape — only the marketplace layout and manifest
|
|
5
|
+
location differ. ``walk_enabled_plugins`` is the IDE-agnostic loop; each IDE
|
|
6
|
+
supplies the two callables that vary (``locate_dir`` + ``read_plugin``).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Callable, Optional
|
|
12
|
+
|
|
13
|
+
from cycode.logger import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger('AI Guardrails Plugins')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_plugin_json(path: Path) -> Optional[dict]:
|
|
19
|
+
"""Load a JSON file inside a plugin directory; None if missing or invalid."""
|
|
20
|
+
if not path.exists():
|
|
21
|
+
return None
|
|
22
|
+
try:
|
|
23
|
+
return json.loads(path.read_text(encoding='utf-8'))
|
|
24
|
+
except Exception as e:
|
|
25
|
+
logger.debug('Failed to load plugin file, %s', {'path': str(path)}, exc_info=e)
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def walk_enabled_plugins(
|
|
30
|
+
plugin_entries: dict[str, Any],
|
|
31
|
+
is_enabled: Callable[[Any], bool],
|
|
32
|
+
locate_dir: Callable[[str, str], Optional[Path]],
|
|
33
|
+
read_plugin: Callable[[Path], tuple[dict, dict]],
|
|
34
|
+
) -> tuple[dict, dict]:
|
|
35
|
+
"""Iterate enabled plugins; merge their MCP servers and metadata.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
plugin_entries: ``{<plugin>@<marketplace>: settings}`` map from the IDE config.
|
|
39
|
+
is_enabled: returns True if ``settings`` indicates the plugin is on
|
|
40
|
+
(e.g. ``bool(settings)`` for Claude, ``settings.get('enabled')`` for Codex).
|
|
41
|
+
locate_dir: given ``(plugin_name, marketplace)``, returns the plugin's
|
|
42
|
+
filesystem path or None if it can't be resolved.
|
|
43
|
+
read_plugin: given the plugin path, returns ``(entry_fields, servers)``:
|
|
44
|
+
``entry_fields`` are extra metadata to attach to the inventory entry
|
|
45
|
+
(name/version/description/...), ``servers`` are MCP servers contributed.
|
|
46
|
+
|
|
47
|
+
Returns ``(merged_mcp_servers, enriched_plugins)``. Plugin keys without
|
|
48
|
+
``@`` (or that fail to resolve to a directory) still appear in the
|
|
49
|
+
inventory with just ``{'enabled': True}`` so we don't silently drop them.
|
|
50
|
+
"""
|
|
51
|
+
merged_mcp: dict = {}
|
|
52
|
+
enriched: dict = {}
|
|
53
|
+
|
|
54
|
+
for plugin_key, settings in plugin_entries.items():
|
|
55
|
+
if not is_enabled(settings):
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
entry: dict = {'enabled': True}
|
|
59
|
+
enriched[plugin_key] = entry
|
|
60
|
+
|
|
61
|
+
if '@' not in plugin_key:
|
|
62
|
+
continue
|
|
63
|
+
plugin_name, marketplace = plugin_key.split('@', 1)
|
|
64
|
+
|
|
65
|
+
plugin_dir = locate_dir(plugin_name, marketplace)
|
|
66
|
+
if plugin_dir is None:
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
plugin_fields, servers = read_plugin(plugin_dir)
|
|
70
|
+
entry.update(plugin_fields)
|
|
71
|
+
merged_mcp.update(servers)
|
|
72
|
+
|
|
73
|
+
return merged_mcp, enriched
|
|
@@ -108,6 +108,26 @@ class IDE(ABC):
|
|
|
108
108
|
``hooks_manager`` can treat them uniformly.
|
|
109
109
|
"""
|
|
110
110
|
|
|
111
|
+
def post_install(self, scope: str, repo_path: Optional[Path] = None) -> tuple[bool, str]:
|
|
112
|
+
"""Run IDE-specific actions after the hooks file is written.
|
|
113
|
+
|
|
114
|
+
Default: no-op success. Override to perform extra setup that doesn't
|
|
115
|
+
belong in the hooks file itself — e.g. Codex enables a
|
|
116
|
+
``[features] codex_hooks = true`` flag in its TOML config.
|
|
117
|
+
|
|
118
|
+
Returns ``(success, message)``. If ``success`` is False, the overall
|
|
119
|
+
install is considered failed.
|
|
120
|
+
"""
|
|
121
|
+
return True, ''
|
|
122
|
+
|
|
123
|
+
def post_uninstall(self, scope: str, repo_path: Optional[Path] = None) -> tuple[bool, str]:
|
|
124
|
+
"""Run IDE-specific cleanup after the hooks file is removed.
|
|
125
|
+
|
|
126
|
+
Default: no-op success. Override to undo whatever ``post_install``
|
|
127
|
+
wrote outside the hooks file.
|
|
128
|
+
"""
|
|
129
|
+
return True, ''
|
|
130
|
+
|
|
111
131
|
# --- runtime scan ---
|
|
112
132
|
|
|
113
133
|
@abstractmethod
|
|
@@ -7,6 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
from typing import ClassVar, Optional
|
|
8
8
|
|
|
9
9
|
from cycode.cli.apps.ai_guardrails.consts import CYCODE_SCAN_PROMPT_COMMAND, CYCODE_SESSION_START_COMMAND
|
|
10
|
+
from cycode.cli.apps.ai_guardrails.ides._plugin_utils import load_plugin_json, walk_enabled_plugins
|
|
10
11
|
from cycode.cli.apps.ai_guardrails.ides.base import IDE, DecisionAction, HookDecision
|
|
11
12
|
from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload
|
|
12
13
|
from cycode.cli.apps.ai_guardrails.scan.types import AiHookEventType
|
|
@@ -127,7 +128,7 @@ def load_claude_config(config_path: Optional[Path] = None) -> Optional[dict]:
|
|
|
127
128
|
"""Load and parse `~/.claude.json`. Returns None if missing/invalid."""
|
|
128
129
|
path = config_path or _CLAUDE_CONFIG_PATH
|
|
129
130
|
if not path.exists():
|
|
130
|
-
logger.debug('Claude config file not found',
|
|
131
|
+
logger.debug('Claude config file not found, %s', {'path': str(path)})
|
|
131
132
|
return None
|
|
132
133
|
try:
|
|
133
134
|
return json.loads(path.read_text(encoding='utf-8'))
|
|
@@ -150,7 +151,7 @@ def load_claude_settings(settings_path: Optional[Path] = None) -> Optional[dict]
|
|
|
150
151
|
"""Load and parse `~/.claude/settings.json`. Returns None if missing/invalid."""
|
|
151
152
|
path = settings_path or _CLAUDE_SETTINGS_PATH
|
|
152
153
|
if not path.exists():
|
|
153
|
-
logger.debug('Claude settings file not found',
|
|
154
|
+
logger.debug('Claude settings file not found, %s', {'path': str(path)})
|
|
154
155
|
return None
|
|
155
156
|
try:
|
|
156
157
|
return json.loads(path.read_text(encoding='utf-8'))
|
|
@@ -171,69 +172,47 @@ def _resolve_marketplace_path(marketplace: dict) -> Optional[Path]:
|
|
|
171
172
|
return path if path.is_dir() else None
|
|
172
173
|
|
|
173
174
|
|
|
174
|
-
def
|
|
175
|
-
"""
|
|
175
|
+
def _read_claude_plugin(plugin_dir: Path) -> tuple[dict, dict]:
|
|
176
|
+
"""Read one Claude Code plugin's manifest + MCP servers.
|
|
176
177
|
|
|
177
|
-
|
|
178
|
+
Claude hardcodes the MCP file at ``<plugin_dir>/.mcp.json`` and always
|
|
179
|
+
wraps it as ``{"mcpServers": {...}}``.
|
|
178
180
|
"""
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
except Exception as e:
|
|
185
|
-
logger.debug('Failed to load plugin file', extra={'path': str(target)}, exc_info=e)
|
|
186
|
-
return None
|
|
181
|
+
manifest = load_plugin_json(plugin_dir / '.claude-plugin' / 'plugin.json') or {}
|
|
182
|
+
entry: dict = {}
|
|
183
|
+
for field in ('name', 'version', 'description'):
|
|
184
|
+
if field in manifest:
|
|
185
|
+
entry[field] = manifest[field]
|
|
187
186
|
|
|
187
|
+
mcp_config = load_plugin_json(plugin_dir / '.mcp.json') or {}
|
|
188
|
+
servers: dict = mcp_config.get('mcpServers') or {}
|
|
189
|
+
if servers:
|
|
190
|
+
entry['mcp_server_names'] = list(servers.keys())
|
|
191
|
+
return entry, servers
|
|
188
192
|
|
|
189
|
-
def resolve_plugins(settings: dict) -> tuple[dict, dict]:
|
|
190
|
-
"""Resolve enabled plugins to their MCP servers and metadata.
|
|
191
193
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
- ``<path>/.mcp.json`` for MCP servers (merged into a flat dict)
|
|
195
|
-
- ``<path>/.claude-plugin/plugin.json`` for metadata (name, version, description)
|
|
194
|
+
def resolve_plugins(settings: dict) -> tuple[dict, dict]:
|
|
195
|
+
"""Walk Claude Code's ``enabledPlugins`` via the shared plugin walker.
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
Each enabled plugin's marketplace is resolved through
|
|
198
|
+
``extraKnownMarketplaces`` to a directory; the rest of the work
|
|
199
|
+
(manifest + ``.mcp.json``) is the shared ``_read_claude_plugin``.
|
|
198
200
|
"""
|
|
199
201
|
enabled = settings.get('enabledPlugins') or {}
|
|
200
202
|
marketplaces = settings.get('extraKnownMarketplaces') or {}
|
|
201
|
-
merged_mcp: dict = {}
|
|
202
|
-
enriched: dict = {}
|
|
203
203
|
|
|
204
|
-
|
|
205
|
-
if not is_enabled:
|
|
206
|
-
continue
|
|
207
|
-
|
|
208
|
-
entry: dict = {'enabled': True}
|
|
209
|
-
enriched[plugin_key] = entry
|
|
210
|
-
|
|
211
|
-
if '@' not in plugin_key:
|
|
212
|
-
continue
|
|
213
|
-
|
|
214
|
-
_plugin_name, marketplace_name = plugin_key.split('@', 1)
|
|
204
|
+
def _locate(_plugin_name: str, marketplace_name: str) -> Optional[Path]:
|
|
215
205
|
marketplace = marketplaces.get(marketplace_name)
|
|
216
206
|
if not marketplace:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
plugin_path = _resolve_marketplace_path(marketplace)
|
|
220
|
-
if plugin_path is None:
|
|
221
|
-
continue
|
|
222
|
-
|
|
223
|
-
metadata = _load_plugin_json_file(plugin_path, '.claude-plugin/plugin.json') or {}
|
|
224
|
-
for field in ('name', 'version', 'description'):
|
|
225
|
-
if field in metadata:
|
|
226
|
-
entry[field] = metadata[field]
|
|
227
|
-
|
|
228
|
-
mcp_config = _load_plugin_json_file(plugin_path, '.mcp.json') or {}
|
|
229
|
-
plugin_server_names = []
|
|
230
|
-
for server_name, server_cfg in (mcp_config.get('mcpServers') or {}).items():
|
|
231
|
-
merged_mcp[server_name] = server_cfg
|
|
232
|
-
plugin_server_names.append(server_name)
|
|
233
|
-
if plugin_server_names:
|
|
234
|
-
entry['mcp_server_names'] = plugin_server_names
|
|
207
|
+
return None
|
|
208
|
+
return _resolve_marketplace_path(marketplace)
|
|
235
209
|
|
|
236
|
-
return
|
|
210
|
+
return walk_enabled_plugins(
|
|
211
|
+
plugin_entries=enabled,
|
|
212
|
+
is_enabled=bool,
|
|
213
|
+
locate_dir=_locate,
|
|
214
|
+
read_plugin=_read_claude_plugin,
|
|
215
|
+
)
|
|
237
216
|
|
|
238
217
|
|
|
239
218
|
# --- IDE integration ----------------------------------------------------------
|
|
@@ -260,6 +239,7 @@ class ClaudeCode(IDE):
|
|
|
260
239
|
'hooks': {
|
|
261
240
|
'SessionStart': [
|
|
262
241
|
{
|
|
242
|
+
'matcher': 'startup|clear',
|
|
263
243
|
'hooks': [{'type': 'command', 'command': _SESSION_START_COMMAND}],
|
|
264
244
|
}
|
|
265
245
|
],
|