@ngocsangairvds/vsaf 3.1.27 → 3.2.1

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 (303) hide show
  1. package/package.json +2 -2
  2. package/src/global.js +70 -10
  3. package/tools/skills/vds-scripts-skill/.openskills.json +6 -0
  4. package/tools/skills/vds-scripts-skill/QUALITY.md +44 -0
  5. package/tools/skills/vds-scripts-skill/SKILL.md +135 -0
  6. package/tools/skills/vds-scripts-skill/references/audit-commands.md +171 -0
  7. package/tools/skills/vds-scripts-skill/references/capability-index.md +34 -0
  8. package/tools/skills/vds-scripts-skill/references/development-commands.md +12 -0
  9. package/tools/skills/vds-scripts-skill/references/google-sheets.md +73 -0
  10. package/tools/skills/vds-scripts-skill/references/integration-commands.md +17 -0
  11. package/tools/skills/vds-scripts-skill/references/platform-bootstrap.md +31 -0
  12. package/tools/skills/vds-scripts-skill/references/specialist-routing.md +14 -0
  13. package/tools/skills/vds-scripts-skill/references/validation-commands.md +15 -0
  14. package/tools/skills/vsaf-build/SKILL.md +32 -2
  15. package/tools/skills/vsaf-ship/SKILL.md +41 -10
  16. package/tools/skills/vsaf-test/SKILL.md +8 -0
  17. package/tools/vds-scripts/.mcp.json +11 -0
  18. package/tools/vds-scripts/.secrets.baseline +133 -0
  19. package/tools/vds-scripts/AGENTS.md +152 -0
  20. package/tools/vds-scripts/CLAUDE.md +101 -0
  21. package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
  22. package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
  23. package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
  24. package/tools/vds-scripts/README.md +97 -0
  25. package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
  26. package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
  27. package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
  28. package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
  29. package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
  30. package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
  31. package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
  32. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
  33. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
  34. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
  35. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
  36. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
  37. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
  38. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
  39. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
  40. package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
  41. package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
  42. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
  43. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
  44. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
  45. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
  46. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
  47. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
  48. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
  49. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
  50. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
  51. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
  52. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
  53. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
  54. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
  55. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
  56. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
  57. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
  58. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
  59. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
  60. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
  61. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
  62. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
  63. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
  64. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
  65. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
  66. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
  67. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
  68. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
  69. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
  70. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
  71. package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
  72. package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
  73. package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
  74. package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
  75. package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
  76. package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
  77. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
  78. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
  79. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
  80. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
  81. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
  82. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
  83. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
  84. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
  85. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
  86. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
  87. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
  88. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
  89. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
  90. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
  91. package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
  92. package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
  93. package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
  94. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
  95. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
  96. package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
  97. package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
  98. package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
  99. package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
  100. package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
  101. package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
  102. package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
  103. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
  104. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
  105. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
  106. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
  107. package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
  108. package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
  109. package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
  110. package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
  111. package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
  112. package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
  113. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
  114. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
  115. package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
  116. package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
  117. package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
  118. package/tools/vds-scripts/git_orchestrator/README.md +280 -0
  119. package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
  120. package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
  121. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
  122. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
  123. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
  124. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
  125. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
  126. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
  127. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
  128. package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
  129. package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
  130. package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
  131. package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
  132. package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
  133. package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
  134. package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
  135. package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
  136. package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
  137. package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
  138. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
  139. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
  140. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
  141. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
  142. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
  143. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
  144. package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
  145. package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
  146. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
  147. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
  148. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
  149. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
  150. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
  151. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
  152. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
  153. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
  154. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
  155. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
  156. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
  157. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
  158. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
  159. package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
  160. package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
  161. package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
  162. package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
  163. package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
  164. package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
  165. package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
  166. package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
  167. package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
  168. package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
  169. package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
  170. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
  171. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
  172. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
  173. package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
  174. package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
  175. package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
  176. package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
  177. package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
  178. package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
  179. package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
  180. package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
  181. package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
  182. package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
  183. package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
  184. package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
  185. package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
  186. package/tools/vds-scripts/mcp_server/README.md +140 -0
  187. package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
  188. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
  189. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
  190. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
  191. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
  192. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
  193. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
  194. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
  195. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
  196. package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
  197. package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
  198. package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
  199. package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
  200. package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
  201. package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
  202. package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
  203. package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
  204. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
  205. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
  206. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
  207. package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
  208. package/tools/vds-scripts/mypy.ini +5 -0
  209. package/tools/vds-scripts/pyproject.toml +29 -0
  210. package/tools/vds-scripts/repo-manifest.yaml +273 -0
  211. package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
  212. package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
  213. package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
  214. package/tools/vds-scripts/scripts/README.md +162 -0
  215. package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
  216. package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
  217. package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
  218. package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
  219. package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
  220. package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
  221. package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
  222. package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
  223. package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
  224. package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
  225. package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
  226. package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
  227. package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
  228. package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
  229. package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
  230. package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
  231. package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
  232. package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
  233. package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
  234. package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
  235. package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
  236. package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
  237. package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
  238. package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
  239. package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
  240. package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
  241. package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
  242. package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
  243. package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
  244. package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
  245. package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
  246. package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
  247. package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
  248. package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
  249. package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
  250. package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
  251. package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
  252. package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
  253. package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
  254. package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
  255. package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
  256. package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
  257. package/tools/vds-scripts/uv.lock +8 -0
  258. package/tools/vds-scripts/vds_cli/README.md +126 -0
  259. package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
  260. package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
  261. package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
  262. package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
  263. package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
  264. package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
  265. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
  266. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
  267. package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
  268. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
  269. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
  270. package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
  271. package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
  272. package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
  273. package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
  274. package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
  275. package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
  276. package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
  277. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
  278. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
  279. package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
  280. package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
  281. package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
  282. package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
  283. package/tools/vds-scripts/vds_cli/uv.lock +524 -0
  284. package/tools/vds-scripts/vds_cli_common/README.md +190 -0
  285. package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
  286. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
  287. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
  288. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
  289. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
  290. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
  291. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
  292. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
  293. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
  294. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
  295. package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
  296. package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
  297. package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
  298. package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
  299. package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
  300. package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
  301. package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
  302. package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
  303. package/tools/vds-scripts/vds_cli_common/tests/test_version.py +64 -0
@@ -0,0 +1,847 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import os
5
+ import time
6
+ from pathlib import Path
7
+ from uuid import uuid4
8
+
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+
13
+ from .logging_config import configure_logging
14
+ from .manifest import Manifest, ManifestFilters, load_manifest
15
+ from .orchestrator import GitOperation, orchestrate_repositories
16
+ from .reporting import print_summary, write_json_report
17
+
18
+ app = typer.Typer(help="High-throughput git operations across VDS projects.")
19
+
20
+
21
+ def _default_concurrency() -> int:
22
+ env_value = os.environ.get("VDS_GIT_CONCURRENCY_DEFAULT")
23
+ if env_value:
24
+ try:
25
+ parsed = int(env_value)
26
+ if parsed > 0:
27
+ return parsed
28
+ except ValueError:
29
+ pass
30
+
31
+ cpu_count = os.cpu_count() or 4
32
+ baseline = max(2, cpu_count // 2)
33
+ return min(8, baseline)
34
+
35
+
36
+ def _load_manifest(path: Path) -> Manifest:
37
+ if not path.exists():
38
+ raise typer.BadParameter(f"Manifest not found: {path}")
39
+ try:
40
+ return load_manifest(path)
41
+ except Exception as exc: # pragma: no cover - surfaced to CLI
42
+ raise typer.BadParameter(f"Failed to load manifest: {exc}") from exc
43
+
44
+
45
+ def _build_filters(
46
+ project: str | None,
47
+ match: str | None,
48
+ tags: list[str],
49
+ skip_tags: list[str],
50
+ ) -> ManifestFilters:
51
+ return ManifestFilters(
52
+ project=project,
53
+ name_match=match,
54
+ include_tags=set(tags or []),
55
+ exclude_tags=set(skip_tags or []),
56
+ )
57
+
58
+
59
+ @app.callback()
60
+ def callback(
61
+ ctx: typer.Context,
62
+ manifest_paths: list[Path] = typer.Option(
63
+ [Path("repo-manifest.yaml")],
64
+ "--manifest",
65
+ "-m",
66
+ help="Path(s) to repository manifest(s) (YAML).",
67
+ ),
68
+ concurrency: int | None = typer.Option(
69
+ None,
70
+ "--concurrency",
71
+ "-c",
72
+ help="Maximum concurrent git operations.",
73
+ ),
74
+ report_dir: Path = typer.Option(
75
+ Path("reports/git_runs"),
76
+ "--report-dir",
77
+ help="Directory for JSON run reports.",
78
+ ),
79
+ json_log: bool = typer.Option(
80
+ False,
81
+ "--json-log",
82
+ help="Emit structured JSON logs in addition to console output.",
83
+ ),
84
+ ) -> None:
85
+ """
86
+ Configure logging and attach manifest to the context for subcommands.
87
+ """
88
+
89
+ configure_logging(json_log=json_log)
90
+ report_dir.mkdir(parents=True, exist_ok=True)
91
+
92
+ manifests = [
93
+ _load_manifest(path)
94
+ for path in manifest_paths
95
+ ]
96
+
97
+ combined_manifest = Manifest.merge(manifests)
98
+
99
+ ctx.obj = {
100
+ "manifest": combined_manifest,
101
+ "concurrency": concurrency if concurrency is not None else _default_concurrency(),
102
+ "report_dir": report_dir,
103
+ "run_id": uuid4().hex,
104
+ }
105
+
106
+
107
+ @app.command()
108
+ def pull(
109
+ ctx: typer.Context,
110
+ project: str | None = typer.Option(None, "--project", "-p", help="Filter by project id."),
111
+ match: str | None = typer.Option(None, "--match", help="Wildcard match against repo name."),
112
+ tags: list[str] = typer.Option([], "--tag", help="Only include repos with tag."),
113
+ skip_tags: list[str] = typer.Option([], "--skip-tag", help="Exclude repos with tag."),
114
+ dry_run: bool = typer.Option(False, "--dry-run", help="List repositories without executing git."),
115
+ ) -> None:
116
+ """
117
+ Fetch and fast-forward repositories.
118
+ """
119
+
120
+ manifest: Manifest = ctx.obj["manifest"]
121
+ filters = _build_filters(project, match, tags, skip_tags)
122
+ concurrency: int = ctx.obj["concurrency"]
123
+ report_dir: Path = ctx.obj["report_dir"]
124
+ run_id: str = ctx.obj["run_id"]
125
+
126
+ start_time = time.perf_counter()
127
+ summary = asyncio.run(
128
+ orchestrate_repositories(
129
+ manifest=manifest,
130
+ operation=GitOperation.PULL,
131
+ filters=filters,
132
+ concurrency=concurrency,
133
+ dry_run=dry_run,
134
+ run_id=run_id,
135
+ )
136
+ )
137
+ duration = time.perf_counter() - start_time
138
+ print_summary(summary)
139
+ typer.echo(f"Serial retries: {summary.serial_retries}")
140
+ typer.echo(f"Duration: {duration:.2f}s")
141
+ report_path = write_json_report(
142
+ summary,
143
+ report_dir,
144
+ metadata={
145
+ "operation": GitOperation.PULL.value,
146
+ "dry_run": dry_run,
147
+ "run_id": run_id,
148
+ "filters": {
149
+ "project": filters.project,
150
+ "match": filters.name_match,
151
+ "include_tags": sorted(filters.include_tags),
152
+ "exclude_tags": sorted(filters.exclude_tags),
153
+ },
154
+ "serial_retries": summary.serial_retries,
155
+ "duration_seconds": round(duration, 3),
156
+ },
157
+ )
158
+ typer.echo(f"Report written to {report_path}")
159
+
160
+
161
+ @app.command()
162
+ def fetch(
163
+ ctx: typer.Context,
164
+ project: str | None = typer.Option(None, "--project", "-p"),
165
+ match: str | None = typer.Option(None, "--match"),
166
+ tags: list[str] = typer.Option([], "--tag"),
167
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
168
+ dry_run: bool = typer.Option(False, "--dry-run"),
169
+ ) -> None:
170
+ """
171
+ Fetch latest refs for repositories without fast-forward.
172
+ """
173
+
174
+ manifest: Manifest = ctx.obj["manifest"]
175
+ filters = _build_filters(project, match, tags, skip_tags)
176
+ concurrency: int = ctx.obj["concurrency"]
177
+ report_dir: Path = ctx.obj["report_dir"]
178
+ run_id: str = ctx.obj["run_id"]
179
+
180
+ start_time = time.perf_counter()
181
+ summary = asyncio.run(
182
+ orchestrate_repositories(
183
+ manifest=manifest,
184
+ operation=GitOperation.FETCH,
185
+ filters=filters,
186
+ concurrency=concurrency,
187
+ dry_run=dry_run,
188
+ run_id=run_id,
189
+ )
190
+ )
191
+ duration = time.perf_counter() - start_time
192
+ print_summary(summary)
193
+ typer.echo(f"Serial retries: {summary.serial_retries}")
194
+ typer.echo(f"Duration: {duration:.2f}s")
195
+ report_path = write_json_report(
196
+ summary,
197
+ report_dir,
198
+ metadata={
199
+ "operation": GitOperation.FETCH.value,
200
+ "dry_run": dry_run,
201
+ "run_id": run_id,
202
+ "filters": {
203
+ "project": filters.project,
204
+ "match": filters.name_match,
205
+ "include_tags": sorted(filters.include_tags),
206
+ "exclude_tags": sorted(filters.exclude_tags),
207
+ },
208
+ "serial_retries": summary.serial_retries,
209
+ "duration_seconds": round(duration, 3),
210
+ },
211
+ )
212
+ typer.echo(f"Report written to {report_path}")
213
+
214
+
215
+ @app.command()
216
+ def status(
217
+ ctx: typer.Context,
218
+ project: str | None = typer.Option(None, "--project", "-p"),
219
+ match: str | None = typer.Option(None, "--match"),
220
+ tags: list[str] = typer.Option([], "--tag"),
221
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
222
+ ) -> None:
223
+ """
224
+ Show git status for repositories.
225
+ """
226
+
227
+ manifest: Manifest = ctx.obj["manifest"]
228
+ filters = _build_filters(project, match, tags, skip_tags)
229
+ concurrency: int = ctx.obj["concurrency"]
230
+ report_dir: Path = ctx.obj["report_dir"]
231
+ run_id: str = ctx.obj["run_id"]
232
+
233
+ start_time = time.perf_counter()
234
+ summary = asyncio.run(
235
+ orchestrate_repositories(
236
+ manifest=manifest,
237
+ operation=GitOperation.STATUS,
238
+ filters=filters,
239
+ concurrency=concurrency,
240
+ dry_run=False,
241
+ run_id=run_id,
242
+ )
243
+ )
244
+ duration = time.perf_counter() - start_time
245
+ print_summary(summary)
246
+ typer.echo(f"Serial retries: {summary.serial_retries}")
247
+ typer.echo(f"Duration: {duration:.2f}s")
248
+ report_path = write_json_report(
249
+ summary,
250
+ report_dir,
251
+ metadata={
252
+ "operation": GitOperation.STATUS.value,
253
+ "dry_run": False,
254
+ "run_id": run_id,
255
+ "filters": {
256
+ "project": filters.project,
257
+ "match": filters.name_match,
258
+ "include_tags": sorted(filters.include_tags),
259
+ "exclude_tags": sorted(filters.exclude_tags),
260
+ },
261
+ "serial_retries": summary.serial_retries,
262
+ "duration_seconds": round(duration, 3),
263
+ },
264
+ )
265
+ typer.echo(f"Report written to {report_path}")
266
+
267
+
268
+ @app.command()
269
+ def clone(
270
+ ctx: typer.Context,
271
+ project: str | None = typer.Option(None, "--project", "-p"),
272
+ match: str | None = typer.Option(None, "--match"),
273
+ tags: list[str] = typer.Option([], "--tag"),
274
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
275
+ dry_run: bool = typer.Option(False, "--dry-run"),
276
+ ) -> None:
277
+ """
278
+ Clone repositories that are missing locally.
279
+ """
280
+
281
+ manifest: Manifest = ctx.obj["manifest"]
282
+ filters = _build_filters(project, match, tags, skip_tags)
283
+ concurrency: int = ctx.obj["concurrency"]
284
+ report_dir: Path = ctx.obj["report_dir"]
285
+ run_id: str = ctx.obj["run_id"]
286
+
287
+ start_time = time.perf_counter()
288
+ summary = asyncio.run(
289
+ orchestrate_repositories(
290
+ manifest=manifest,
291
+ operation=GitOperation.CLONE,
292
+ filters=filters,
293
+ concurrency=concurrency,
294
+ dry_run=dry_run,
295
+ run_id=run_id,
296
+ )
297
+ )
298
+ duration = time.perf_counter() - start_time
299
+ print_summary(summary)
300
+ typer.echo(f"Serial retries: {summary.serial_retries}")
301
+ typer.echo(f"Duration: {duration:.2f}s")
302
+ report_path = write_json_report(
303
+ summary,
304
+ report_dir,
305
+ metadata={
306
+ "operation": GitOperation.CLONE.value,
307
+ "dry_run": dry_run,
308
+ "run_id": run_id,
309
+ "filters": {
310
+ "project": filters.project,
311
+ "match": filters.name_match,
312
+ "include_tags": sorted(filters.include_tags),
313
+ "exclude_tags": sorted(filters.exclude_tags),
314
+ },
315
+ "serial_retries": summary.serial_retries,
316
+ "duration_seconds": round(duration, 3),
317
+ },
318
+ )
319
+ typer.echo(f"Report written to {report_path}")
320
+
321
+
322
+ @app.command(name="list")
323
+ def list_repositories(
324
+ ctx: typer.Context,
325
+ project: str | None = typer.Option(None, "--project", "-p"),
326
+ match: str | None = typer.Option(None, "--match"),
327
+ tags: list[str] = typer.Option([], "--tag"),
328
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
329
+ ) -> None:
330
+ """
331
+ List repositories from the manifest after applying filters.
332
+ """
333
+
334
+ manifest: Manifest = ctx.obj["manifest"]
335
+ filters = _build_filters(project, match, tags, skip_tags)
336
+ repos = manifest.filter(filters)
337
+
338
+ console = Console()
339
+ if not repos:
340
+ console.print("[yellow]No repositories matched the provided filters.[/yellow]")
341
+ return
342
+
343
+ table = Table("Name", "Project", "Branch", "Path", "Tags")
344
+ for repo in repos:
345
+ table.add_row(
346
+ repo.name,
347
+ repo.project,
348
+ repo.branch,
349
+ str(repo.path),
350
+ ", ".join(sorted(repo.tags)) or "-",
351
+ )
352
+
353
+ console.print(table)
354
+
355
+
356
+ @app.command()
357
+ def branch(
358
+ ctx: typer.Context,
359
+ action: str = typer.Argument(..., help="Action: list, create, delete"),
360
+ branch_name: str | None = typer.Option(None, "--name", help="Branch name (for create/delete)"),
361
+ project: str | None = typer.Option(None, "--project", "-p"),
362
+ match: str | None = typer.Option(None, "--match"),
363
+ tags: list[str] = typer.Option([], "--tag"),
364
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
365
+ dry_run: bool = typer.Option(False, "--dry-run"),
366
+ ) -> None:
367
+ """List, create, or delete branches."""
368
+ manifest: Manifest = ctx.obj["manifest"]
369
+ filters = _build_filters(project, match, tags, skip_tags)
370
+ concurrency: int = ctx.obj["concurrency"]
371
+ report_dir: Path = ctx.obj["report_dir"]
372
+ run_id: str = ctx.obj["run_id"]
373
+
374
+ start_time = time.perf_counter()
375
+ summary = asyncio.run(
376
+ orchestrate_repositories(
377
+ manifest=manifest,
378
+ operation=GitOperation.BRANCH,
379
+ filters=filters,
380
+ concurrency=concurrency,
381
+ dry_run=dry_run,
382
+ run_id=run_id,
383
+ action=action,
384
+ branch_name=branch_name,
385
+ )
386
+ )
387
+ duration = time.perf_counter() - start_time
388
+ print_summary(summary)
389
+ typer.echo(f"Duration: {duration:.2f}s")
390
+ report_path = write_json_report(
391
+ summary,
392
+ report_dir,
393
+ metadata={
394
+ "operation": f"branch-{action}",
395
+ "dry_run": dry_run,
396
+ "run_id": run_id,
397
+ "branch_name": branch_name,
398
+ "duration_seconds": round(duration, 3),
399
+ },
400
+ )
401
+ typer.echo(f"Report written to {report_path}")
402
+
403
+
404
+ @app.command()
405
+ def checkout(
406
+ ctx: typer.Context,
407
+ branch_or_path: str = typer.Argument(..., help="Branch name or path to checkout"),
408
+ create: bool = typer.Option(False, "--create", "-b", help="Create new branch"),
409
+ project: str | None = typer.Option(None, "--project", "-p"),
410
+ match: str | None = typer.Option(None, "--match"),
411
+ tags: list[str] = typer.Option([], "--tag"),
412
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
413
+ dry_run: bool = typer.Option(False, "--dry-run"),
414
+ ) -> None:
415
+ """Checkout a branch or path."""
416
+ manifest: Manifest = ctx.obj["manifest"]
417
+ filters = _build_filters(project, match, tags, skip_tags)
418
+ concurrency: int = ctx.obj["concurrency"]
419
+ report_dir: Path = ctx.obj["report_dir"]
420
+ run_id: str = ctx.obj["run_id"]
421
+
422
+ start_time = time.perf_counter()
423
+ summary = asyncio.run(
424
+ orchestrate_repositories(
425
+ manifest=manifest,
426
+ operation=GitOperation.CHECKOUT,
427
+ filters=filters,
428
+ concurrency=concurrency,
429
+ dry_run=dry_run,
430
+ run_id=run_id,
431
+ branch_or_path=branch_or_path,
432
+ create=create,
433
+ )
434
+ )
435
+ duration = time.perf_counter() - start_time
436
+ print_summary(summary)
437
+ typer.echo(f"Duration: {duration:.2f}s")
438
+ report_path = write_json_report(
439
+ summary,
440
+ report_dir,
441
+ metadata={
442
+ "operation": "checkout",
443
+ "branch_or_path": branch_or_path,
444
+ "create": create,
445
+ "dry_run": dry_run,
446
+ "run_id": run_id,
447
+ "duration_seconds": round(duration, 3),
448
+ },
449
+ )
450
+ typer.echo(f"Report written to {report_path}")
451
+
452
+
453
+ @app.command()
454
+ def add(
455
+ ctx: typer.Context,
456
+ paths: list[str] = typer.Argument(default=[], help="File paths to stage"),
457
+ all_files: bool = typer.Option(False, "--all", "-A", help="Stage all files"),
458
+ project: str | None = typer.Option(None, "--project", "-p"),
459
+ match: str | None = typer.Option(None, "--match"),
460
+ tags: list[str] = typer.Option([], "--tag"),
461
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
462
+ dry_run: bool = typer.Option(False, "--dry-run"),
463
+ ) -> None:
464
+ """Stage files for commit."""
465
+ if not all_files and not paths:
466
+ typer.echo("Error: Either specify paths or use --all flag", err=True)
467
+ raise typer.Exit(1) from None
468
+ manifest: Manifest = ctx.obj["manifest"]
469
+ filters = _build_filters(project, match, tags, skip_tags)
470
+ concurrency: int = ctx.obj["concurrency"]
471
+ report_dir: Path = ctx.obj["report_dir"]
472
+ run_id: str = ctx.obj["run_id"]
473
+
474
+ start_time = time.perf_counter()
475
+ summary = asyncio.run(
476
+ orchestrate_repositories(
477
+ manifest=manifest,
478
+ operation=GitOperation.ADD,
479
+ filters=filters,
480
+ concurrency=concurrency,
481
+ dry_run=dry_run,
482
+ run_id=run_id,
483
+ paths=paths,
484
+ all_files=all_files,
485
+ )
486
+ )
487
+ duration = time.perf_counter() - start_time
488
+ print_summary(summary)
489
+ typer.echo(f"Duration: {duration:.2f}s")
490
+ report_path = write_json_report(
491
+ summary,
492
+ report_dir,
493
+ metadata={
494
+ "operation": "add",
495
+ "paths": paths,
496
+ "all_files": all_files,
497
+ "dry_run": dry_run,
498
+ "run_id": run_id,
499
+ "duration_seconds": round(duration, 3),
500
+ },
501
+ )
502
+ typer.echo(f"Report written to {report_path}")
503
+
504
+
505
+ @app.command()
506
+ def commit(
507
+ ctx: typer.Context,
508
+ message: str = typer.Argument(..., help="Commit message"),
509
+ project: str | None = typer.Option(None, "--project", "-p"),
510
+ match: str | None = typer.Option(None, "--match"),
511
+ tags: list[str] = typer.Option([], "--tag"),
512
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
513
+ dry_run: bool = typer.Option(False, "--dry-run"),
514
+ ) -> None:
515
+ """Commit staged changes."""
516
+ manifest: Manifest = ctx.obj["manifest"]
517
+ filters = _build_filters(project, match, tags, skip_tags)
518
+ concurrency: int = ctx.obj["concurrency"]
519
+ report_dir: Path = ctx.obj["report_dir"]
520
+ run_id: str = ctx.obj["run_id"]
521
+
522
+ start_time = time.perf_counter()
523
+ summary = asyncio.run(
524
+ orchestrate_repositories(
525
+ manifest=manifest,
526
+ operation=GitOperation.COMMIT,
527
+ filters=filters,
528
+ concurrency=concurrency,
529
+ dry_run=dry_run,
530
+ run_id=run_id,
531
+ message=message,
532
+ )
533
+ )
534
+ duration = time.perf_counter() - start_time
535
+ print_summary(summary)
536
+ typer.echo(f"Duration: {duration:.2f}s")
537
+ report_path = write_json_report(
538
+ summary,
539
+ report_dir,
540
+ metadata={
541
+ "operation": "commit",
542
+ "message": message,
543
+ "dry_run": dry_run,
544
+ "run_id": run_id,
545
+ "duration_seconds": round(duration, 3),
546
+ },
547
+ )
548
+ typer.echo(f"Report written to {report_path}")
549
+
550
+
551
+ @app.command()
552
+ def push(
553
+ ctx: typer.Context,
554
+ branch: str | None = typer.Option(None, "--branch", help="Branch to push"),
555
+ remote: str | None = typer.Option(None, "--remote", help="Remote name (default: origin)"),
556
+ project: str | None = typer.Option(None, "--project", "-p"),
557
+ match: str | None = typer.Option(None, "--match"),
558
+ tags: list[str] = typer.Option([], "--tag"),
559
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
560
+ dry_run: bool = typer.Option(False, "--dry-run"),
561
+ ) -> None:
562
+ """Push commits to remote."""
563
+ manifest: Manifest = ctx.obj["manifest"]
564
+ filters = _build_filters(project, match, tags, skip_tags)
565
+ concurrency: int = ctx.obj["concurrency"]
566
+ report_dir: Path = ctx.obj["report_dir"]
567
+ run_id: str = ctx.obj["run_id"]
568
+
569
+ start_time = time.perf_counter()
570
+ summary = asyncio.run(
571
+ orchestrate_repositories(
572
+ manifest=manifest,
573
+ operation=GitOperation.PUSH,
574
+ filters=filters,
575
+ concurrency=concurrency,
576
+ dry_run=dry_run,
577
+ run_id=run_id,
578
+ branch=branch,
579
+ remote=remote,
580
+ )
581
+ )
582
+ duration = time.perf_counter() - start_time
583
+ print_summary(summary)
584
+ typer.echo(f"Duration: {duration:.2f}s")
585
+ report_path = write_json_report(
586
+ summary,
587
+ report_dir,
588
+ metadata={
589
+ "operation": "push",
590
+ "branch": branch,
591
+ "remote": remote,
592
+ "dry_run": dry_run,
593
+ "run_id": run_id,
594
+ "duration_seconds": round(duration, 3),
595
+ },
596
+ )
597
+ typer.echo(f"Report written to {report_path}")
598
+
599
+
600
+ @app.command()
601
+ def log(
602
+ ctx: typer.Context,
603
+ limit: int | None = typer.Option(None, "--limit", "-n", help="Number of commits to show"),
604
+ oneline: bool = typer.Option(False, "--oneline", help="Show one line per commit"),
605
+ project: str | None = typer.Option(None, "--project", "-p"),
606
+ match: str | None = typer.Option(None, "--match"),
607
+ tags: list[str] = typer.Option([], "--tag"),
608
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
609
+ ) -> None:
610
+ """Show commit history."""
611
+ manifest: Manifest = ctx.obj["manifest"]
612
+ filters = _build_filters(project, match, tags, skip_tags)
613
+ concurrency: int = ctx.obj["concurrency"]
614
+ report_dir: Path = ctx.obj["report_dir"]
615
+ run_id: str = ctx.obj["run_id"]
616
+
617
+ start_time = time.perf_counter()
618
+ summary = asyncio.run(
619
+ orchestrate_repositories(
620
+ manifest=manifest,
621
+ operation=GitOperation.LOG,
622
+ filters=filters,
623
+ concurrency=concurrency,
624
+ dry_run=False,
625
+ run_id=run_id,
626
+ limit=limit,
627
+ oneline=oneline,
628
+ )
629
+ )
630
+ duration = time.perf_counter() - start_time
631
+ print_summary(summary)
632
+ typer.echo(f"Duration: {duration:.2f}s")
633
+ report_path = write_json_report(
634
+ summary,
635
+ report_dir,
636
+ metadata={
637
+ "operation": "log",
638
+ "limit": limit,
639
+ "oneline": oneline,
640
+ "run_id": run_id,
641
+ "duration_seconds": round(duration, 3),
642
+ },
643
+ )
644
+ typer.echo(f"Report written to {report_path}")
645
+
646
+
647
+ @app.command()
648
+ def diff(
649
+ ctx: typer.Context,
650
+ paths: list[str] = typer.Argument(default=[], help="File paths to diff"),
651
+ staged: bool = typer.Option(False, "--staged", help="Show staged changes"),
652
+ project: str | None = typer.Option(None, "--project", "-p"),
653
+ match: str | None = typer.Option(None, "--match"),
654
+ tags: list[str] = typer.Option([], "--tag"),
655
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
656
+ ) -> None:
657
+ """Show changes between commits, commit and working tree, etc."""
658
+ manifest: Manifest = ctx.obj["manifest"]
659
+ filters = _build_filters(project, match, tags, skip_tags)
660
+ concurrency: int = ctx.obj["concurrency"]
661
+ report_dir: Path = ctx.obj["report_dir"]
662
+ run_id: str = ctx.obj["run_id"]
663
+
664
+ start_time = time.perf_counter()
665
+ summary = asyncio.run(
666
+ orchestrate_repositories(
667
+ manifest=manifest,
668
+ operation=GitOperation.DIFF,
669
+ filters=filters,
670
+ concurrency=concurrency,
671
+ dry_run=False,
672
+ run_id=run_id,
673
+ paths=paths,
674
+ staged=staged,
675
+ )
676
+ )
677
+ duration = time.perf_counter() - start_time
678
+ print_summary(summary)
679
+ typer.echo(f"Duration: {duration:.2f}s")
680
+ report_path = write_json_report(
681
+ summary,
682
+ report_dir,
683
+ metadata={
684
+ "operation": "diff",
685
+ "paths": paths,
686
+ "staged": staged,
687
+ "run_id": run_id,
688
+ "duration_seconds": round(duration, 3),
689
+ },
690
+ )
691
+ typer.echo(f"Report written to {report_path}")
692
+
693
+
694
+ @app.command()
695
+ def stash(
696
+ ctx: typer.Context,
697
+ action: str = typer.Argument(..., help="Action: list, save, pop, apply, drop"),
698
+ message: str | None = typer.Option(None, "--message", "-m", help="Stash message (for save)"),
699
+ project: str | None = typer.Option(None, "--project", "-p"),
700
+ match: str | None = typer.Option(None, "--match"),
701
+ tags: list[str] = typer.Option([], "--tag"),
702
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
703
+ dry_run: bool = typer.Option(False, "--dry-run"),
704
+ ) -> None:
705
+ """Stash or restore working directory changes."""
706
+ manifest: Manifest = ctx.obj["manifest"]
707
+ filters = _build_filters(project, match, tags, skip_tags)
708
+ concurrency: int = ctx.obj["concurrency"]
709
+ report_dir: Path = ctx.obj["report_dir"]
710
+ run_id: str = ctx.obj["run_id"]
711
+
712
+ start_time = time.perf_counter()
713
+ summary = asyncio.run(
714
+ orchestrate_repositories(
715
+ manifest=manifest,
716
+ operation=GitOperation.STASH,
717
+ filters=filters,
718
+ concurrency=concurrency,
719
+ dry_run=dry_run,
720
+ run_id=run_id,
721
+ action=action,
722
+ message=message,
723
+ )
724
+ )
725
+ duration = time.perf_counter() - start_time
726
+ print_summary(summary)
727
+ typer.echo(f"Duration: {duration:.2f}s")
728
+ report_path = write_json_report(
729
+ summary,
730
+ report_dir,
731
+ metadata={
732
+ "operation": f"stash-{action}",
733
+ "message": message,
734
+ "dry_run": dry_run,
735
+ "run_id": run_id,
736
+ "duration_seconds": round(duration, 3),
737
+ },
738
+ )
739
+ typer.echo(f"Report written to {report_path}")
740
+
741
+
742
+ @app.command()
743
+ def tag(
744
+ ctx: typer.Context,
745
+ action: str = typer.Argument(..., help="Action: list, create, delete"),
746
+ tag_name: str | None = typer.Option(None, "--name", help="Tag name (for create/delete)"),
747
+ message: str | None = typer.Option(None, "--message", "-m", help="Tag message (for create)"),
748
+ project: str | None = typer.Option(None, "--project", "-p"),
749
+ match: str | None = typer.Option(None, "--match"),
750
+ tags: list[str] = typer.Option([], "--tag"),
751
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
752
+ dry_run: bool = typer.Option(False, "--dry-run"),
753
+ ) -> None:
754
+ """List, create, or delete tags."""
755
+ manifest: Manifest = ctx.obj["manifest"]
756
+ filters = _build_filters(project, match, tags, skip_tags)
757
+ concurrency: int = ctx.obj["concurrency"]
758
+ report_dir: Path = ctx.obj["report_dir"]
759
+ run_id: str = ctx.obj["run_id"]
760
+
761
+ start_time = time.perf_counter()
762
+ summary = asyncio.run(
763
+ orchestrate_repositories(
764
+ manifest=manifest,
765
+ operation=GitOperation.TAG,
766
+ filters=filters,
767
+ concurrency=concurrency,
768
+ dry_run=dry_run,
769
+ run_id=run_id,
770
+ action=action,
771
+ tag_name=tag_name,
772
+ message=message,
773
+ )
774
+ )
775
+ duration = time.perf_counter() - start_time
776
+ print_summary(summary)
777
+ typer.echo(f"Duration: {duration:.2f}s")
778
+ report_path = write_json_report(
779
+ summary,
780
+ report_dir,
781
+ metadata={
782
+ "operation": f"tag-{action}",
783
+ "tag_name": tag_name,
784
+ "message": message,
785
+ "dry_run": dry_run,
786
+ "run_id": run_id,
787
+ "duration_seconds": round(duration, 3),
788
+ },
789
+ )
790
+ typer.echo(f"Report written to {report_path}")
791
+
792
+
793
+ @app.command()
794
+ def remote(
795
+ ctx: typer.Context,
796
+ action: str = typer.Argument(..., help="Action: list, add, remove"),
797
+ name: str | None = typer.Option(None, "--name", help="Remote name (for add/remove)"),
798
+ url: str | None = typer.Option(None, "--url", help="Remote URL (for add)"),
799
+ project: str | None = typer.Option(None, "--project", "-p"),
800
+ match: str | None = typer.Option(None, "--match"),
801
+ tags: list[str] = typer.Option([], "--tag"),
802
+ skip_tags: list[str] = typer.Option([], "--skip-tag"),
803
+ ) -> None:
804
+ """List, add, or remove remotes."""
805
+ manifest: Manifest = ctx.obj["manifest"]
806
+ filters = _build_filters(project, match, tags, skip_tags)
807
+ concurrency: int = ctx.obj["concurrency"]
808
+ report_dir: Path = ctx.obj["report_dir"]
809
+ run_id: str = ctx.obj["run_id"]
810
+
811
+ start_time = time.perf_counter()
812
+ summary = asyncio.run(
813
+ orchestrate_repositories(
814
+ manifest=manifest,
815
+ operation=GitOperation.REMOTE,
816
+ filters=filters,
817
+ concurrency=concurrency,
818
+ dry_run=False,
819
+ run_id=run_id,
820
+ action=action,
821
+ name=name,
822
+ url=url,
823
+ )
824
+ )
825
+ duration = time.perf_counter() - start_time
826
+ print_summary(summary)
827
+ typer.echo(f"Duration: {duration:.2f}s")
828
+ report_path = write_json_report(
829
+ summary,
830
+ report_dir,
831
+ metadata={
832
+ "operation": f"remote-{action}",
833
+ "name": name,
834
+ "url": url,
835
+ "run_id": run_id,
836
+ "duration_seconds": round(duration, 3),
837
+ },
838
+ )
839
+ typer.echo(f"Report written to {report_path}")
840
+
841
+
842
+ def main() -> None:
843
+ app()
844
+
845
+
846
+ if __name__ == "__main__":
847
+ main()