cycode 3.13.1.dev1__tar.gz → 3.13.1.dev2__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.
Files changed (212) hide show
  1. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/PKG-INFO +1 -1
  2. cycode-3.13.1.dev2/cycode/__init__.py +1 -0
  3. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/__init__.py +12 -11
  4. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/consts.py +3 -3
  5. cycode-3.13.1.dev2/cycode/cli/apps/ai_guardrails/scan/claude_config.py +159 -0
  6. cycode-3.13.1.dev2/cycode/cli/apps/ai_guardrails/scan/cursor_config.py +36 -0
  7. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/handlers.py +0 -3
  8. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/payload.py +3 -3
  9. cycode-3.13.1.dev2/cycode/cli/apps/ai_guardrails/session_start_command.py +150 -0
  10. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/ai_security_manager_client.py +18 -0
  11. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/pyproject.toml +1 -1
  12. cycode-3.13.1.dev1/cycode/__init__.py +0 -1
  13. cycode-3.13.1.dev1/cycode/cli/apps/ai_guardrails/ensure_auth_command.py +0 -21
  14. cycode-3.13.1.dev1/cycode/cli/apps/ai_guardrails/scan/claude_config.py +0 -44
  15. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/LICENCE +0 -0
  16. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/README.md +0 -0
  17. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/__main__.py +0 -0
  18. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/__init__.py +0 -0
  19. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/app.py +0 -0
  20. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/__init__.py +0 -0
  21. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/activation_manager.py +0 -0
  22. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
  23. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/hooks_manager.py +0 -0
  24. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/install_command.py +0 -0
  25. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
  26. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
  27. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
  28. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/response_builders.py +0 -0
  29. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +0 -0
  30. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/types.py +0 -0
  31. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
  32. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
  33. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
  34. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
  35. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
  36. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
  37. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
  38. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/api/__init__.py +0 -0
  39. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/api/api_command.py +0 -0
  40. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/api/openapi_spec.py +0 -0
  41. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/__init__.py +0 -0
  42. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/auth_command.py +0 -0
  43. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/auth_common.py +0 -0
  44. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/auth_manager.py +0 -0
  45. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/models.py +0 -0
  46. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/__init__.py +0 -0
  47. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/configure_command.py +0 -0
  48. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/consts.py +0 -0
  49. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/messages.py +0 -0
  50. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/prompts.py +0 -0
  51. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ignore/__init__.py +0 -0
  52. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ignore/ignore_command.py +0 -0
  53. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/mcp/__init__.py +0 -0
  54. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/mcp/mcp_command.py +0 -0
  55. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/__init__.py +0 -0
  56. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/report_command.py +0 -0
  57. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/__init__.py +0 -0
  58. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/common.py +0 -0
  59. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
  60. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
  61. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
  62. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
  63. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
  64. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
  65. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/__init__.py +0 -0
  66. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/report_import_command.py +0 -0
  67. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
  68. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
  69. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/sca_options.py +0 -0
  70. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/__init__.py +0 -0
  71. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/aggregation_report.py +0 -0
  72. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/code_scanner.py +0 -0
  73. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
  74. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
  75. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
  76. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/detection_excluder.py +0 -0
  77. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/path/__init__.py +0 -0
  78. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/path/path_command.py +0 -0
  79. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
  80. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
  81. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
  82. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
  83. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
  84. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
  85. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
  86. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/repository/__init__.py +0 -0
  87. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
  88. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
  89. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
  90. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
  91. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_command.py +0 -0
  92. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_parameters.py +0 -0
  93. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_result.py +0 -0
  94. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/__init__.py +0 -0
  95. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/get_cli_status.py +0 -0
  96. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/models.py +0 -0
  97. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/status_command.py +0 -0
  98. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/version_command.py +0 -0
  99. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/cli_types.py +0 -0
  100. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/config.py +0 -0
  101. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/console.py +0 -0
  102. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/consts.py +0 -0
  103. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/__init__.py +0 -0
  104. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/custom_exceptions.py +0 -0
  105. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
  106. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
  107. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_errors.py +0 -0
  108. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
  109. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
  110. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/__init__.py +0 -0
  111. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/commit_range_documents.py +0 -0
  112. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
  113. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/file_excluder.py +0 -0
  114. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/iac/__init__.py +0 -0
  115. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
  116. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/models/__init__.py +0 -0
  117. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
  118. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/path_documents.py +0 -0
  119. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/repository_documents.py +0 -0
  120. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/__init__.py +0 -0
  121. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
  122. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
  123. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
  124. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
  125. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
  126. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
  127. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
  128. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +0 -0
  129. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
  130. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +0 -0
  131. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +0 -0
  132. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
  133. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
  134. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/php/__init__.py +0 -0
  135. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
  136. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/python/__init__.py +0 -0
  137. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
  138. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
  139. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
  140. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
  141. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
  142. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
  143. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
  144. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/walk_ignore.py +0 -0
  145. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/zip_documents.py +0 -0
  146. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/logger.py +0 -0
  147. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/main.py +0 -0
  148. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/models.py +0 -0
  149. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/__init__.py +0 -0
  150. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/console_printer.py +0 -0
  151. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/json_printer.py +0 -0
  152. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/printer_base.py +0 -0
  153. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/rich_printer.py +0 -0
  154. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/__init__.py +0 -0
  155. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
  156. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table.py +0 -0
  157. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table_models.py +0 -0
  158. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table_printer.py +0 -0
  159. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table_printer_base.py +0 -0
  160. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/text_printer.py +0 -0
  161. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/__init__.py +0 -0
  162. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
  163. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_data.py +0 -0
  164. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
  165. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
  166. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
  167. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/rich_helpers.py +0 -0
  168. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/__init__.py +0 -0
  169. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/base_file_manager.py +0 -0
  170. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/config_file_manager.py +0 -0
  171. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/configuration_manager.py +0 -0
  172. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/credentials_manager.py +0 -0
  173. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/jwt_creator.py +0 -0
  174. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/__init__.py +0 -0
  175. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/binary_utils.py +0 -0
  176. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/enum_utils.py +0 -0
  177. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/get_api_client.py +0 -0
  178. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/git_proxy.py +0 -0
  179. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/ignore_utils.py +0 -0
  180. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/jwt_utils.py +0 -0
  181. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/path_utils.py +0 -0
  182. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/progress_bar.py +0 -0
  183. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/scan_batch.py +0 -0
  184. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/scan_utils.py +0 -0
  185. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/shell_executor.py +0 -0
  186. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/string_utils.py +0 -0
  187. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/task_timer.py +0 -0
  188. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/url_utils.py +0 -0
  189. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/version_checker.py +0 -0
  190. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/yaml_utils.py +0 -0
  191. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/config.py +0 -0
  192. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/__init__.py +0 -0
  193. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
  194. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/auth_client.py +0 -0
  195. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/base_token_auth_client.py +0 -0
  196. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cli_activation_client.py +0 -0
  197. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/client_creator.py +0 -0
  198. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/config.py +0 -0
  199. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/config_dev.py +0 -0
  200. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_client.py +0 -0
  201. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_client_base.py +0 -0
  202. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_dev_based_client.py +0 -0
  203. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
  204. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_token_based_client.py +0 -0
  205. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/headers.py +0 -0
  206. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/import_sbom_client.py +0 -0
  207. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/logger.py +0 -0
  208. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/models.py +0 -0
  209. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/report_client.py +0 -0
  210. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/scan_client.py +0 -0
  211. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/scan_config_base.py +0 -0
  212. {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/logger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycode
3
- Version: 3.13.1.dev1
3
+ Version: 3.13.1.dev2
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
@@ -0,0 +1 @@
1
+ __version__ = '3.13.1.dev2' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
@@ -1,23 +1,24 @@
1
1
  import typer
2
2
 
3
- from cycode.cli.apps.ai_guardrails.ensure_auth_command import ensure_auth_command
4
- from cycode.cli.apps.ai_guardrails.install_command import install_command
5
- from cycode.cli.apps.ai_guardrails.scan.scan_command import scan_command
6
- from cycode.cli.apps.ai_guardrails.status_command import status_command
7
- from cycode.cli.apps.ai_guardrails.uninstall_command import uninstall_command
3
+ from cycode.cli.apps.ai_guardrails.install_command import install_command as _install_command
4
+ from cycode.cli.apps.ai_guardrails.scan.scan_command import scan_command as _scan_command
5
+ from cycode.cli.apps.ai_guardrails.session_start_command import session_start_command as _session_start_command
6
+ from cycode.cli.apps.ai_guardrails.status_command import status_command as _status_command
7
+ from cycode.cli.apps.ai_guardrails.uninstall_command import uninstall_command as _uninstall_command
8
8
 
9
9
  app = typer.Typer(name='ai-guardrails', no_args_is_help=True, hidden=True)
10
10
 
11
- app.command(hidden=True, name='install', short_help='Install AI guardrails hooks for supported IDEs.')(install_command)
11
+ app.command(hidden=True, name='install', short_help='Install AI guardrails hooks for supported IDEs.')(_install_command)
12
12
  app.command(hidden=True, name='uninstall', short_help='Remove AI guardrails hooks from supported IDEs.')(
13
- uninstall_command
13
+ _uninstall_command
14
14
  )
15
- app.command(hidden=True, name='status', short_help='Show AI guardrails hook installation status.')(status_command)
15
+ app.command(hidden=True, name='status', short_help='Show AI guardrails hook installation status.')(_status_command)
16
16
  app.command(
17
17
  hidden=True,
18
18
  name='scan',
19
19
  short_help='Scan content from AI IDE hooks for secrets (reads JSON from stdin).',
20
- )(scan_command)
21
- app.command(hidden=True, name='ensure-auth', short_help='Ensure authentication, triggering auth if needed.')(
22
- ensure_auth_command
20
+ )(_scan_command)
21
+ app.command(hidden=True, name='session-start', short_help='Handle session start: auth, conversation, session context.')(
22
+ _session_start_command
23
23
  )
24
+ app.command(hidden=True, name='ensure-auth', short_help='[Deprecated] Alias for session-start.')(_session_start_command)
@@ -84,7 +84,7 @@ DEFAULT_IDE = AIIDEType.CURSOR
84
84
 
85
85
  # Command used in hooks
86
86
  CYCODE_SCAN_PROMPT_COMMAND = 'cycode ai-guardrails scan'
87
- CYCODE_ENSURE_AUTH_COMMAND = 'cycode ai-guardrails ensure-auth'
87
+ CYCODE_SESSION_START_COMMAND = 'cycode ai-guardrails session-start'
88
88
 
89
89
 
90
90
  def _get_cursor_hooks_config(async_mode: bool = False) -> dict:
@@ -92,7 +92,7 @@ def _get_cursor_hooks_config(async_mode: bool = False) -> dict:
92
92
  config = IDE_CONFIGS[AIIDEType.CURSOR]
93
93
  command = f'{CYCODE_SCAN_PROMPT_COMMAND} &' if async_mode else CYCODE_SCAN_PROMPT_COMMAND
94
94
  hooks = {event: [{'command': command}] for event in config.hook_events}
95
- hooks['sessionStart'] = [{'command': CYCODE_ENSURE_AUTH_COMMAND}]
95
+ hooks['sessionStart'] = [{'command': f'{CYCODE_SESSION_START_COMMAND} --ide cursor'}]
96
96
 
97
97
  return {
98
98
  'version': 1,
@@ -119,7 +119,7 @@ def _get_claude_code_hooks_config(async_mode: bool = False) -> dict:
119
119
  'SessionStart': [
120
120
  {
121
121
  'matcher': 'startup',
122
- 'hooks': [{'type': 'command', 'command': CYCODE_ENSURE_AUTH_COMMAND}],
122
+ 'hooks': [{'type': 'command', 'command': f'{CYCODE_SESSION_START_COMMAND} --ide claude-code'}],
123
123
  }
124
124
  ],
125
125
  'UserPromptSubmit': [
@@ -0,0 +1,159 @@
1
+ """Reader for ~/.claude.json configuration file.
2
+
3
+ Extracts user email from the Claude Code global config file
4
+ for use in AI guardrails scan enrichment.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ from cycode.logger import get_logger
12
+
13
+ logger = get_logger('AI Guardrails Claude Config')
14
+
15
+ _CLAUDE_CONFIG_PATH = Path.home() / '.claude.json'
16
+ _CLAUDE_SETTINGS_PATH = Path.home() / '.claude' / 'settings.json'
17
+
18
+
19
+ def load_claude_config(config_path: Optional[Path] = None) -> Optional[dict]:
20
+ """Load and parse ~/.claude.json.
21
+
22
+ Args:
23
+ config_path: Override path for testing. Defaults to ~/.claude.json.
24
+
25
+ Returns:
26
+ Parsed dict or None if file is missing or invalid.
27
+ """
28
+ path = config_path or _CLAUDE_CONFIG_PATH
29
+ if not path.exists():
30
+ logger.debug('Claude config file not found', extra={'path': str(path)})
31
+ return None
32
+ try:
33
+ content = path.read_text(encoding='utf-8')
34
+ return json.loads(content)
35
+ except Exception as e:
36
+ logger.debug('Failed to load Claude config file', exc_info=e)
37
+ return None
38
+
39
+
40
+ def get_user_email(config: dict) -> Optional[str]:
41
+ """Extract user email from Claude config.
42
+
43
+ Reads oauthAccount.emailAddress from the config dict.
44
+ """
45
+ return config.get('oauthAccount', {}).get('emailAddress')
46
+
47
+
48
+ def get_mcp_servers(config: dict) -> Optional[dict]:
49
+ """Extract MCP servers from Claude config.
50
+
51
+ Reads mcpServers from the config dict.
52
+ """
53
+ return config.get('mcpServers')
54
+
55
+
56
+ def load_claude_settings(settings_path: Optional[Path] = None) -> Optional[dict]:
57
+ """Load and parse ~/.claude/settings.json.
58
+
59
+ Args:
60
+ settings_path: Override path for testing. Defaults to ~/.claude/settings.json.
61
+
62
+ Returns:
63
+ Parsed dict or None if file is missing or invalid.
64
+ """
65
+ path = settings_path or _CLAUDE_SETTINGS_PATH
66
+ if not path.exists():
67
+ logger.debug('Claude settings file not found', extra={'path': str(path)})
68
+ return None
69
+ try:
70
+ content = path.read_text(encoding='utf-8')
71
+ return json.loads(content)
72
+ except Exception as e:
73
+ logger.debug('Failed to load Claude settings file', exc_info=e)
74
+ return None
75
+
76
+
77
+ def _resolve_marketplace_path(marketplace: dict) -> Optional[Path]:
78
+ """
79
+ Resolve filesystem path for a directory-type marketplace.
80
+ """
81
+ source = marketplace.get('source', {})
82
+ if source.get('source') != 'directory':
83
+ return None
84
+ raw = source.get('path')
85
+ if not raw:
86
+ return None
87
+ path = Path(raw)
88
+ return path if path.is_dir() else None
89
+
90
+
91
+ def _load_plugin_json_file(plugin_path: Path, relative_path: str) -> Optional[dict]:
92
+ """Load and parse a JSON file inside a plugin directory.
93
+
94
+ Returns None if the file is missing, unreadable, or has invalid JSON.
95
+ """
96
+ target = plugin_path / relative_path
97
+ if not target.exists():
98
+ return None
99
+ try:
100
+ return json.loads(target.read_text(encoding='utf-8'))
101
+ except Exception as e:
102
+ logger.debug('Failed to load plugin file', extra={'path': str(target)}, exc_info=e)
103
+ return None
104
+
105
+
106
+ def resolve_plugins(settings: dict) -> tuple[dict, dict]:
107
+ """Resolve enabled plugins to their MCP servers and metadata.
108
+
109
+ Walks enabledPlugins from claude settings, resolves each plugin's 'marketplace' directory
110
+ via the 'extraKnownMarketplaces' field, and reads:
111
+ - <path>/.mcp.json for MCP servers (merged into a flat dict)
112
+ - <path>/.claude-plugin/plugin.json for metadata (name, version, description)
113
+
114
+ Args:
115
+ settings: Parsed ~/.claude/settings.json dict.
116
+
117
+ Returns:
118
+ Tuple of (merged_mcp_servers, enriched_plugins):
119
+ - merged_mcp_servers: {server_name: server_config, ...}
120
+ - enriched_plugins: {plugin_key: {"enabled": True, "name": ..., ...}, ...}
121
+ """
122
+ enabled = settings.get('enabledPlugins') or {}
123
+ marketplaces = settings.get('extraKnownMarketplaces') or {}
124
+ merged_mcp: dict = {}
125
+ enriched: dict = {}
126
+
127
+ for plugin_key, is_enabled in enabled.items():
128
+ if not is_enabled:
129
+ continue
130
+
131
+ entry: dict = {'enabled': True}
132
+ enriched[plugin_key] = entry
133
+
134
+ if '@' not in plugin_key:
135
+ continue
136
+
137
+ _plugin_name, marketplace_name = plugin_key.split('@', 1)
138
+ marketplace = marketplaces.get(marketplace_name)
139
+ if not marketplace:
140
+ continue
141
+
142
+ plugin_path = _resolve_marketplace_path(marketplace)
143
+ if plugin_path is None:
144
+ continue
145
+
146
+ metadata = _load_plugin_json_file(plugin_path, '.claude-plugin/plugin.json') or {}
147
+ for field in ('name', 'version', 'description'):
148
+ if field in metadata:
149
+ entry[field] = metadata[field]
150
+
151
+ mcp_config = _load_plugin_json_file(plugin_path, '.mcp.json') or {}
152
+ plugin_server_names = []
153
+ for server_name, server_cfg in (mcp_config.get('mcpServers') or {}).items():
154
+ merged_mcp[server_name] = server_cfg
155
+ plugin_server_names.append(server_name)
156
+ if plugin_server_names:
157
+ entry['mcp_server_names'] = plugin_server_names
158
+
159
+ return merged_mcp, enriched
@@ -0,0 +1,36 @@
1
+ """Reader for ~/.cursor/mcp.json configuration file.
2
+
3
+ Extracts MCP server definitions from the Cursor global config file
4
+ for use in AI guardrails session-context reporting.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ from cycode.logger import get_logger
12
+
13
+ logger = get_logger('AI Guardrails Cursor Config')
14
+
15
+ _CURSOR_MCP_CONFIG_PATH = Path.home() / '.cursor' / 'mcp.json'
16
+
17
+
18
+ def load_cursor_config(config_path: Optional[Path] = None) -> Optional[dict]:
19
+ """Load and parse ~/.cursor/mcp.json.
20
+
21
+ Args:
22
+ config_path: Override path for testing. Defaults to ~/.cursor/mcp.json.
23
+
24
+ Returns:
25
+ Parsed dict or None if file is missing or invalid.
26
+ """
27
+ path = config_path or _CURSOR_MCP_CONFIG_PATH
28
+ if not path.exists():
29
+ logger.debug('Cursor MCP config file not found', extra={'path': str(path)})
30
+ return None
31
+ try:
32
+ content = path.read_text(encoding='utf-8')
33
+ return json.loads(content)
34
+ except Exception as e:
35
+ logger.debug('Failed to load Cursor MCP config file', exc_info=e)
36
+ return None
@@ -42,7 +42,6 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
42
42
  response_builder = get_response_builder(ide)
43
43
 
44
44
  prompt_config = get_policy_value(policy, 'prompt', default={})
45
- ai_client.create_conversation(payload)
46
45
  if not get_policy_value(prompt_config, 'enabled', default=True):
47
46
  ai_client.create_event(payload, AiHookEventType.PROMPT, AIHookOutcome.ALLOWED)
48
47
  return response_builder.allow_prompt()
@@ -100,7 +99,6 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
100
99
  response_builder = get_response_builder(ide)
101
100
 
102
101
  file_read_config = get_policy_value(policy, 'file_read', default={})
103
- ai_client.create_conversation(payload)
104
102
  if not get_policy_value(file_read_config, 'enabled', default=True):
105
103
  ai_client.create_event(payload, AiHookEventType.FILE_READ, AIHookOutcome.ALLOWED)
106
104
  return response_builder.allow_permission()
@@ -203,7 +201,6 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
203
201
  response_builder = get_response_builder(ide)
204
202
 
205
203
  mcp_config = get_policy_value(policy, 'mcp', default={})
206
- ai_client.create_conversation(payload)
207
204
  if not get_policy_value(mcp_config, 'enabled', default=True):
208
205
  ai_client.create_event(payload, AiHookEventType.MCP_EXECUTION, AIHookOutcome.ALLOWED)
209
206
  return response_builder.allow_permission()
@@ -71,7 +71,7 @@ def _extract_generation_id(entry: dict) -> Optional[str]:
71
71
  return None
72
72
 
73
73
 
74
- def _extract_from_claude_transcript(
74
+ def extract_from_claude_transcript(
75
75
  transcript_path: str,
76
76
  ) -> tuple[Optional[str], Optional[str], Optional[str]]:
77
77
  """Extract IDE version, model, and latest generation ID from Claude Code transcript file.
@@ -123,7 +123,7 @@ class AIHookPayload:
123
123
  """Unified payload object that normalizes field names from different AI tools."""
124
124
 
125
125
  # Event identification
126
- event_name: str # Canonical event type (e.g., 'prompt', 'file_read', 'mcp_execution')
126
+ event_name: Optional[str] = None # Canonical event type (e.g., 'prompt', 'file_read', 'mcp_execution')
127
127
  conversation_id: Optional[str] = None
128
128
  generation_id: Optional[str] = None
129
129
 
@@ -206,7 +206,7 @@ class AIHookPayload:
206
206
  mcp_tool_name = parts[2]
207
207
 
208
208
  # Extract IDE version, model, and generation ID from transcript file
209
- ide_version, model, generation_id = _extract_from_claude_transcript(payload.get('transcript_path'))
209
+ ide_version, model, generation_id = extract_from_claude_transcript(payload.get('transcript_path'))
210
210
 
211
211
  # Extract user email from ~/.claude.json
212
212
  claude_config = load_claude_config()
@@ -0,0 +1,150 @@
1
+ import sys
2
+ from typing import TYPE_CHECKING, Annotated
3
+
4
+ import typer
5
+
6
+ from cycode.cli.apps.ai_guardrails.consts import AIIDEType
7
+ from cycode.cli.apps.ai_guardrails.scan.claude_config import (
8
+ get_mcp_servers,
9
+ get_user_email,
10
+ load_claude_config,
11
+ load_claude_settings,
12
+ resolve_plugins,
13
+ )
14
+ from cycode.cli.apps.ai_guardrails.scan.cursor_config import load_cursor_config
15
+ from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload, extract_from_claude_transcript
16
+ from cycode.cli.apps.ai_guardrails.scan.utils import safe_json_parse
17
+ from cycode.cli.apps.auth.auth_common import get_authorization_info
18
+ from cycode.cli.apps.auth.auth_manager import AuthManager
19
+ from cycode.cli.exceptions.handle_auth_errors import handle_auth_exception
20
+ from cycode.cli.utils.get_api_client import get_ai_security_manager_client
21
+ from cycode.logger import get_logger
22
+
23
+ if TYPE_CHECKING:
24
+ from cycode.cyclient.ai_security_manager_client import AISecurityManagerClient
25
+
26
+ logger = get_logger('AI Guardrails')
27
+
28
+
29
+ def _build_session_payload(payload: dict, ide: str) -> AIHookPayload:
30
+ """Build an AIHookPayload from a session-start stdin payload."""
31
+ if ide == AIIDEType.CLAUDE_CODE:
32
+ claude_config = load_claude_config()
33
+ ide_user_email = get_user_email(claude_config) if claude_config else None
34
+ ide_version, _, _ = extract_from_claude_transcript(payload.get('transcript_path'))
35
+
36
+ return AIHookPayload(
37
+ conversation_id=payload.get('session_id'),
38
+ ide_user_email=ide_user_email,
39
+ model=payload.get('model'),
40
+ ide_provider=AIIDEType.CLAUDE_CODE.value,
41
+ ide_version=ide_version,
42
+ )
43
+
44
+ # Cursor
45
+ return AIHookPayload(
46
+ conversation_id=payload.get('conversation_id'),
47
+ ide_user_email=payload.get('user_email'),
48
+ model=payload.get('model'),
49
+ ide_provider=AIIDEType.CURSOR.value,
50
+ ide_version=payload.get('cursor_version'),
51
+ )
52
+
53
+
54
+ def _get_claude_code_session_context() -> tuple[dict, dict]:
55
+ """Return (mcp_servers, enabled_plugins) for Claude Code.
56
+
57
+ Merges MCP servers from ~/.claude.json (user-configured) with those contributed
58
+ by enabled plugins. Plugin metadata (name, version, description) is included in
59
+ the enabled_plugins dict when resolvable.
60
+ """
61
+ config = load_claude_config()
62
+ mcp_servers = dict(get_mcp_servers(config) or {}) if config else {}
63
+
64
+ settings = load_claude_settings()
65
+ if settings:
66
+ plugin_mcp, enriched_plugins = resolve_plugins(settings)
67
+ mcp_servers.update(plugin_mcp)
68
+ else:
69
+ enriched_plugins = {}
70
+
71
+ return mcp_servers, enriched_plugins
72
+
73
+
74
+ def _get_cursor_session_context() -> tuple[dict, dict]:
75
+ """Return (mcp_servers, enabled_plugins) for Cursor. Cursor has no plugin system."""
76
+ config = load_cursor_config()
77
+ mcp_servers = dict(get_mcp_servers(config) or {}) if config else {}
78
+ return mcp_servers, {}
79
+
80
+
81
+ def _report_session_context(ai_client: 'AISecurityManagerClient', ide: str) -> None:
82
+ """Report IDE session context to the AI security manager. Never raises."""
83
+ try:
84
+ if ide == AIIDEType.CLAUDE_CODE:
85
+ mcp_servers, enabled_plugins = _get_claude_code_session_context()
86
+ elif ide == AIIDEType.CURSOR:
87
+ mcp_servers, enabled_plugins = _get_cursor_session_context()
88
+ else:
89
+ return
90
+
91
+ if not mcp_servers and not enabled_plugins:
92
+ return
93
+ ai_client.report_session_context(mcp_servers=mcp_servers, enabled_plugins=enabled_plugins)
94
+ except Exception as e:
95
+ logger.debug('Failed to report session context', exc_info=e)
96
+
97
+
98
+ def session_start_command(
99
+ ctx: typer.Context,
100
+ ide: Annotated[
101
+ str,
102
+ typer.Option(
103
+ '--ide',
104
+ help='IDE that triggered the session start.',
105
+ hidden=True,
106
+ ),
107
+ ] = AIIDEType.CURSOR.value,
108
+ ) -> None:
109
+ """Handle session start: ensure auth, create conversation, report session context."""
110
+ # Step 1: Ensure authentication
111
+ auth_info = get_authorization_info(ctx)
112
+ if auth_info is None:
113
+ logger.debug('Not authenticated, starting authentication')
114
+ try:
115
+ auth_manager = AuthManager()
116
+ auth_manager.authenticate()
117
+ except Exception as err:
118
+ handle_auth_exception(ctx, err)
119
+ return
120
+ else:
121
+ logger.debug('Already authenticated')
122
+
123
+ # Step 2: Read stdin payload (backward compat: old hooks pipe no stdin)
124
+ if sys.stdin.isatty():
125
+ logger.debug('No stdin payload (TTY), skipping session initialization')
126
+ return
127
+
128
+ stdin_data = sys.stdin.read().strip()
129
+ payload = safe_json_parse(stdin_data)
130
+ if not payload:
131
+ logger.debug('Empty or invalid stdin payload, skipping session initialization')
132
+ return
133
+
134
+ # Step 3: Build session payload and initialize API client
135
+ session_payload = _build_session_payload(payload, ide)
136
+
137
+ try:
138
+ ai_client = get_ai_security_manager_client(ctx)
139
+ except Exception as e:
140
+ logger.debug('Failed to initialize AI security client', exc_info=e)
141
+ return
142
+
143
+ # Step 4: Create conversation
144
+ try:
145
+ ai_client.create_conversation(session_payload)
146
+ except Exception as e:
147
+ logger.debug('Failed to create conversation during session start', exc_info=e)
148
+
149
+ # Step 5: Report session context (MCP servers)
150
+ _report_session_context(ai_client, ide)
@@ -17,6 +17,7 @@ class AISecurityManagerClient:
17
17
 
18
18
  _CONVERSATIONS_PATH = 'v4/ai-security/interactions/conversations'
19
19
  _EVENTS_PATH = 'v4/ai-security/interactions/events'
20
+ _SESSION_CONTEXT_PATH = 'v4/ai-security/interactions/session-context'
20
21
 
21
22
  def __init__(self, client: CycodeClientBase, service_config: 'AISecurityManagerServiceConfigBase') -> None:
22
23
  self.client = client
@@ -88,3 +89,20 @@ class AISecurityManagerClient:
88
89
  except Exception as e:
89
90
  logger.debug('Failed to create AI hook event', exc_info=e)
90
91
  # Don't fail the hook if tracking fails
92
+
93
+ def report_session_context(
94
+ self,
95
+ mcp_servers: Optional[dict] = None,
96
+ enabled_plugins: Optional[dict] = None,
97
+ ) -> None:
98
+ """Report session context to the backend."""
99
+ body: dict = {
100
+ 'mcp_servers': mcp_servers,
101
+ 'enabled_plugins': enabled_plugins,
102
+ }
103
+
104
+ try:
105
+ self.client.post(self._build_endpoint_path(self._SESSION_CONTEXT_PATH), body=body)
106
+ except Exception as e:
107
+ logger.debug('Failed to report session context', exc_info=e)
108
+ # Don't fail the session if reporting fails
@@ -21,7 +21,7 @@ classifiers = [
21
21
  "Programming Language :: Python :: 3.14",
22
22
  ]
23
23
  dynamic = ["dependencies"]
24
- version = "3.13.1.dev1"
24
+ version = "3.13.1.dev2"
25
25
 
26
26
  [project.scripts]
27
27
  cycode = "cycode.cli.app:app"
@@ -1 +0,0 @@
1
- __version__ = '3.13.1.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
@@ -1,21 +0,0 @@
1
- import typer
2
-
3
- from cycode.cli.apps.auth.auth_common import get_authorization_info
4
- from cycode.cli.apps.auth.auth_manager import AuthManager
5
- from cycode.cli.exceptions.handle_auth_errors import handle_auth_exception
6
- from cycode.cli.logger import logger
7
-
8
-
9
- def ensure_auth_command(ctx: typer.Context) -> None:
10
- """Ensure the user is authenticated, triggering authentication if needed."""
11
- auth_info = get_authorization_info(ctx)
12
- if auth_info is not None:
13
- logger.debug('Already authenticated')
14
- return
15
-
16
- logger.debug('Not authenticated, starting authentication')
17
- try:
18
- auth_manager = AuthManager()
19
- auth_manager.authenticate()
20
- except Exception as err:
21
- handle_auth_exception(ctx, err)
@@ -1,44 +0,0 @@
1
- """Reader for ~/.claude.json configuration file.
2
-
3
- Extracts user email from the Claude Code global config file
4
- for use in AI guardrails scan enrichment.
5
- """
6
-
7
- import json
8
- from pathlib import Path
9
- from typing import Optional
10
-
11
- from cycode.logger import get_logger
12
-
13
- logger = get_logger('AI Guardrails Claude Config')
14
-
15
- _CLAUDE_CONFIG_PATH = Path.home() / '.claude.json'
16
-
17
-
18
- def load_claude_config(config_path: Optional[Path] = None) -> Optional[dict]:
19
- """Load and parse ~/.claude.json.
20
-
21
- Args:
22
- config_path: Override path for testing. Defaults to ~/.claude.json.
23
-
24
- Returns:
25
- Parsed dict or None if file is missing or invalid.
26
- """
27
- path = config_path or _CLAUDE_CONFIG_PATH
28
- if not path.exists():
29
- logger.debug('Claude config file not found', extra={'path': str(path)})
30
- return None
31
- try:
32
- content = path.read_text(encoding='utf-8')
33
- return json.loads(content)
34
- except Exception as e:
35
- logger.debug('Failed to load Claude config file', exc_info=e)
36
- return None
37
-
38
-
39
- def get_user_email(config: dict) -> Optional[str]:
40
- """Extract user email from Claude config.
41
-
42
- Reads oauthAccount.emailAddress from the config dict.
43
- """
44
- return config.get('oauthAccount', {}).get('emailAddress')
File without changes
File without changes