@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,240 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Folder Structure Consistency Validation Script
4
+ Validates that all services follow identical folder and file structure.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Dict, List, Set
10
+ import json
11
+
12
+ # Expected project structure
13
+ EXPECTED_PROJECTS = ["INSURANCE", "LEP", "EKYC", "SAVING", "PLATFORM", "SECURITY"]
14
+
15
+ # Expected folder structure for each project
16
+ EXPECTED_FOLDERS = [
17
+ "analysis",
18
+ "business",
19
+ "integrations",
20
+ "operations",
21
+ "overview",
22
+ "security"
23
+ ]
24
+
25
+ # Expected files in each project root
26
+ EXPECTED_FILES = [
27
+ "README.md"
28
+ ]
29
+
30
+ def analyze_project_structure(project_path: Path) -> Dict:
31
+ """Analyze the folder structure of a project."""
32
+
33
+ if not project_path.exists():
34
+ return {"error": f"Project path does not exist: {project_path}"}
35
+
36
+ structure = {
37
+ "folders": [],
38
+ "files": [],
39
+ "subfolders": {}
40
+ }
41
+
42
+ try:
43
+ # Get immediate folders and files
44
+ for item in project_path.iterdir():
45
+ if item.is_dir():
46
+ structure["folders"].append(item.name)
47
+ # Analyze subfolders
48
+ subfolders = []
49
+ try:
50
+ for subitem in item.iterdir():
51
+ if subitem.is_dir():
52
+ subfolders.append(subitem.name)
53
+ except PermissionError:
54
+ pass
55
+ structure["subfolders"][item.name] = sorted(subfolders)
56
+ elif item.is_file():
57
+ structure["files"].append(item.name)
58
+
59
+ structure["folders"] = sorted(structure["folders"])
60
+ structure["files"] = sorted(structure["files"])
61
+
62
+ except Exception as e:
63
+ structure["error"] = str(e)
64
+
65
+ return structure
66
+
67
+ def validate_folder_consistency() -> Dict:
68
+ """Validate folder structure consistency across all projects."""
69
+
70
+ # Get the documentation root
71
+ script_dir = Path(__file__).parent
72
+ docs_root = script_dir.parent / "insurance-document"
73
+
74
+ if not docs_root.exists():
75
+ docs_root = script_dir.parent.parent / "insurance-document"
76
+
77
+ if not docs_root.exists():
78
+ return {"error": "Documentation root not found"}
79
+
80
+ projects_root = docs_root / "projects"
81
+ if not projects_root.exists():
82
+ return {"error": "Projects directory not found"}
83
+
84
+ print(f"🔍 Analyzing project structures at: {projects_root}")
85
+
86
+ # Analyze each project
87
+ project_structures = {}
88
+ for project_name in EXPECTED_PROJECTS:
89
+ project_path = projects_root / project_name
90
+ if project_path.exists():
91
+ structure = analyze_project_structure(project_path)
92
+ project_structures[project_name] = structure
93
+ else:
94
+ project_structures[project_name] = {"error": "Project directory not found"}
95
+
96
+ # Find common structure patterns
97
+ all_folders = set()
98
+ all_files = set()
99
+
100
+ for project, structure in project_structures.items():
101
+ if "error" not in structure:
102
+ all_folders.update(structure.get("folders", []))
103
+ all_files.update(structure.get("files", []))
104
+
105
+ # Check consistency
106
+ consistency_report = {
107
+ "total_projects": len(EXPECTED_PROJECTS),
108
+ "analyzed_projects": len([p for p in project_structures.values() if "error" not in p]),
109
+ "common_folders": sorted(list(all_folders)),
110
+ "common_files": sorted(list(all_files)),
111
+ "project_structures": project_structures,
112
+ "consistency_issues": [],
113
+ "missing_projects": []
114
+ }
115
+
116
+ # Check for missing projects
117
+ for project_name in EXPECTED_PROJECTS:
118
+ if "error" in project_structures.get(project_name, {}):
119
+ consistency_report["missing_projects"].append(project_name)
120
+
121
+ # Check folder consistency
122
+ reference_structure = None
123
+ reference_project = None
124
+
125
+ for project, structure in project_structures.items():
126
+ if "error" not in structure:
127
+ if reference_structure is None:
128
+ reference_structure = structure
129
+ reference_project = project
130
+ else:
131
+ # Compare with reference
132
+ if set(structure["folders"]) != set(reference_structure["folders"]):
133
+ consistency_report["consistency_issues"].append({
134
+ "type": "folder_mismatch",
135
+ "project": project,
136
+ "reference": reference_project,
137
+ "missing_folders": list(set(reference_structure["folders"]) - set(structure["folders"])),
138
+ "extra_folders": list(set(structure["folders"]) - set(reference_structure["folders"]))
139
+ })
140
+
141
+ if set(structure["files"]) != set(reference_structure["files"]):
142
+ consistency_report["consistency_issues"].append({
143
+ "type": "file_mismatch",
144
+ "project": project,
145
+ "reference": reference_project,
146
+ "missing_files": list(set(reference_structure["files"]) - set(structure["files"])),
147
+ "extra_files": list(set(structure["files"]) - set(reference_structure["files"]))
148
+ })
149
+
150
+ # Calculate consistency score
151
+ total_checks = len(EXPECTED_PROJECTS) * 2 # folders + files
152
+ failed_checks = len(consistency_report["consistency_issues"]) + len(consistency_report["missing_projects"])
153
+ consistency_score = max(0, (total_checks - failed_checks) / total_checks * 100)
154
+
155
+ consistency_report["consistency_score"] = consistency_score
156
+
157
+ return consistency_report
158
+
159
+ def print_consistency_report(report: Dict):
160
+ """Print a formatted consistency report."""
161
+
162
+ if "error" in report:
163
+ print(f"❌ {report['error']}")
164
+ return
165
+
166
+ print("\n" + "="*60)
167
+ print("📁 FOLDER STRUCTURE CONSISTENCY REPORT")
168
+ print("="*60)
169
+
170
+ print(f"\n📊 Summary:")
171
+ print(f" Total Projects: {report['total_projects']}")
172
+ print(f" Analyzed Projects: {report['analyzed_projects']}")
173
+ print(f" Consistency Score: {report['consistency_score']:.1f}%")
174
+
175
+ if report['consistency_score'] >= 95:
176
+ print(" ✅ Excellent consistency!")
177
+ elif report['consistency_score'] >= 80:
178
+ print(" ⚠️ Good consistency with minor issues")
179
+ else:
180
+ print(" ❌ Poor consistency - significant issues found")
181
+
182
+ if report['missing_projects']:
183
+ print(f"\n❌ Missing Projects ({len(report['missing_projects'])}):")
184
+ for project in report['missing_projects']:
185
+ print(f" • {project}")
186
+
187
+ print(f"\n📂 Common Folder Structure:")
188
+ for folder in report['common_folders']:
189
+ print(f" • {folder}/")
190
+
191
+ print(f"\n📄 Common Files:")
192
+ for file in report['common_files']:
193
+ print(f" • {file}")
194
+
195
+ if report['consistency_issues']:
196
+ print(f"\n⚠️ Consistency Issues ({len(report['consistency_issues'])}):")
197
+ for issue in report['consistency_issues']:
198
+ if issue['type'] == 'folder_mismatch':
199
+ print(f" • {issue['project']}: Folder mismatch with {issue['reference']}")
200
+ if issue['missing_folders']:
201
+ print(f" Missing: {', '.join(issue['missing_folders'])}")
202
+ if issue['extra_folders']:
203
+ print(f" Extra: {', '.join(issue['extra_folders'])}")
204
+ elif issue['type'] == 'file_mismatch':
205
+ print(f" • {issue['project']}: File mismatch with {issue['reference']}")
206
+ if issue['missing_files']:
207
+ print(f" Missing: {', '.join(issue['missing_files'])}")
208
+ if issue['extra_files']:
209
+ print(f" Extra: {', '.join(issue['extra_files'])}")
210
+
211
+ print(f"\n📋 Detailed Project Structures:")
212
+ for project, structure in report['project_structures'].items():
213
+ if "error" in structure:
214
+ print(f" • {project}: ❌ {structure['error']}")
215
+ else:
216
+ print(f" • {project}: ✅ {len(structure['folders'])} folders, {len(structure['files'])} files")
217
+
218
+ if __name__ == "__main__":
219
+ print("🚀 Starting Folder Structure Consistency Validation...")
220
+ report = validate_folder_consistency()
221
+ print_consistency_report(report)
222
+
223
+ # Save report to file
224
+ script_dir = Path(__file__).parent
225
+ report_file = script_dir / "folder_structure_report.json"
226
+
227
+ with open(report_file, 'w') as f:
228
+ json.dump(report, f, indent=2)
229
+
230
+ print(f"\n💾 Report saved to: {report_file}")
231
+
232
+ # Exit with appropriate code
233
+ if "error" in report:
234
+ exit(1)
235
+ elif report['consistency_score'] < 80:
236
+ print(f"\n⚠️ Folder structure consistency below threshold: {report['consistency_score']:.1f}%")
237
+ exit(1)
238
+ else:
239
+ print(f"\n✅ Folder structure consistency validation passed: {report['consistency_score']:.1f}%")
240
+ exit(0)
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Link Integrity Validation Script (Internal Links Only)
4
+ Validates that all internal links in documentation resolve correctly.
5
+ """
6
+
7
+ import os
8
+ import re
9
+ from pathlib import Path
10
+ from typing import Dict, List, Set, Tuple
11
+ import json
12
+ import urllib.parse
13
+
14
+ # Link patterns to match
15
+ MARKDOWN_LINK_PATTERN = r'\[([^\]]*)\]\(([^)]+)\)'
16
+ REFERENCE_LINK_PATTERN = r'\[([^\]]*)\]:\s*([^\s]+)'
17
+
18
+ def extract_links_from_file(file_path: Path) -> List[Tuple[str, str, int]]:
19
+ """Extract all links from a markdown file."""
20
+ links = []
21
+
22
+ try:
23
+ with open(file_path, 'r', encoding='utf-8') as f:
24
+ content = f.read()
25
+
26
+ lines = content.split('\n')
27
+
28
+ for line_num, line in enumerate(lines, 1):
29
+ # Find markdown links [text](url)
30
+ for match in re.finditer(MARKDOWN_LINK_PATTERN, line):
31
+ text, url = match.groups()
32
+ links.append((text, url, line_num))
33
+
34
+ # Find reference links [text]: url
35
+ for match in re.finditer(REFERENCE_LINK_PATTERN, line):
36
+ text, url = match.groups()
37
+ links.append((text, url, line_num))
38
+
39
+ except Exception as e:
40
+ print(f"Error reading {file_path}: {e}")
41
+
42
+ return links
43
+
44
+ def categorize_link(url: str, base_path: Path) -> str:
45
+ """Categorize a link as internal, external, or anchor."""
46
+
47
+ # Remove fragments (anchors)
48
+ clean_url = url.split('#')[0]
49
+
50
+ if not clean_url:
51
+ return "anchor"
52
+ elif clean_url.startswith(('http://', 'https://')):
53
+ return "external"
54
+ elif clean_url.startswith('mailto:'):
55
+ return "email"
56
+ else:
57
+ return "internal"
58
+
59
+ def validate_internal_link(url: str, source_file: Path, docs_root: Path) -> Dict:
60
+ """Validate an internal link."""
61
+
62
+ # Remove anchor fragment
63
+ clean_url = url.split('#')[0]
64
+
65
+ if not clean_url:
66
+ return {"valid": True, "type": "anchor", "message": "Anchor link"}
67
+
68
+ # Resolve relative path
69
+ if clean_url.startswith('/'):
70
+ # Absolute path from docs root
71
+ target_path = docs_root / clean_url.lstrip('/')
72
+ else:
73
+ # Relative path from source file
74
+ target_path = source_file.parent / clean_url
75
+
76
+ # Normalize path
77
+ try:
78
+ target_path = target_path.resolve()
79
+ except Exception as e:
80
+ return {"valid": False, "type": "internal", "message": f"Path resolution error: {e}"}
81
+
82
+ # Check if target exists
83
+ if target_path.exists():
84
+ return {"valid": True, "type": "internal", "message": "File exists"}
85
+ else:
86
+ return {"valid": False, "type": "internal", "message": f"File not found: {target_path}"}
87
+
88
+ def validate_links_in_file(file_path: Path, docs_root: Path) -> Dict:
89
+ """Validate all links in a single file."""
90
+
91
+ links = extract_links_from_file(file_path)
92
+
93
+ results = {
94
+ "file": str(file_path.relative_to(docs_root)),
95
+ "total_links": len(links),
96
+ "internal_links": 0,
97
+ "external_links": 0,
98
+ "valid_internal": 0,
99
+ "broken_internal": 0,
100
+ "link_results": []
101
+ }
102
+
103
+ for text, url, line_num in links:
104
+ link_type = categorize_link(url, file_path)
105
+
106
+ if link_type == "internal" or link_type == "anchor":
107
+ results["internal_links"] += 1
108
+ validation = validate_internal_link(url, file_path, docs_root)
109
+
110
+ if validation["valid"]:
111
+ results["valid_internal"] += 1
112
+ else:
113
+ results["broken_internal"] += 1
114
+
115
+ elif link_type == "external":
116
+ results["external_links"] += 1
117
+ validation = {"valid": True, "type": "external", "message": "External link (not validated)"}
118
+ elif link_type == "email":
119
+ validation = {"valid": True, "type": "email", "message": "Email link"}
120
+ else:
121
+ validation = {"valid": False, "type": "unknown", "message": "Unknown link type"}
122
+
123
+ link_result = {
124
+ "text": text,
125
+ "url": url,
126
+ "line": line_num,
127
+ "type": link_type,
128
+ "valid": validation["valid"],
129
+ "message": validation["message"]
130
+ }
131
+
132
+ results["link_results"].append(link_result)
133
+
134
+ return results
135
+
136
+ def validate_link_integrity(max_files: int = 50) -> Dict:
137
+ """Validate link integrity across documentation."""
138
+
139
+ # Get the documentation root
140
+ script_dir = Path(__file__).parent
141
+ docs_root = script_dir.parent / "insurance-document"
142
+
143
+ if not docs_root.exists():
144
+ docs_root = script_dir.parent.parent / "insurance-document"
145
+
146
+ if not docs_root.exists():
147
+ return {"error": "Documentation root not found"}
148
+
149
+ print(f"🔍 Scanning links in documentation at: {docs_root}")
150
+
151
+ # Find markdown files (limit to avoid overwhelming)
152
+ md_files = list(docs_root.rglob("*.md"))[:max_files]
153
+
154
+ print(f"📄 Found {len(md_files)} markdown files (limited to {max_files})")
155
+
156
+ # Validate links in each file
157
+ all_results = []
158
+ total_links = 0
159
+ total_internal = 0
160
+ total_external = 0
161
+ valid_internal = 0
162
+ broken_internal = 0
163
+
164
+ for i, md_file in enumerate(md_files, 1):
165
+ print(f" Processing {i}/{len(md_files)}: {md_file.name}")
166
+
167
+ file_results = validate_links_in_file(md_file, docs_root)
168
+ all_results.append(file_results)
169
+
170
+ total_links += file_results["total_links"]
171
+ total_internal += file_results["internal_links"]
172
+ total_external += file_results["external_links"]
173
+ valid_internal += file_results["valid_internal"]
174
+ broken_internal += file_results["broken_internal"]
175
+
176
+ # Calculate success rate for internal links only
177
+ internal_success_rate = (valid_internal / total_internal * 100) if total_internal > 0 else 100
178
+
179
+ # Generate summary report
180
+ report = {
181
+ "total_files": len(md_files),
182
+ "total_links": total_links,
183
+ "internal_links": total_internal,
184
+ "external_links": total_external,
185
+ "valid_internal": valid_internal,
186
+ "broken_internal": broken_internal,
187
+ "internal_success_rate": internal_success_rate,
188
+ "file_results": all_results,
189
+ "broken_internal_summary": []
190
+ }
191
+
192
+ # Collect broken internal links summary
193
+ for file_result in all_results:
194
+ for link in file_result["link_results"]:
195
+ if link["type"] in ["internal", "anchor"] and not link["valid"]:
196
+ report["broken_internal_summary"].append({
197
+ "file": file_result["file"],
198
+ "line": link["line"],
199
+ "url": link["url"],
200
+ "message": link["message"]
201
+ })
202
+
203
+ return report
204
+
205
+ def print_link_integrity_report(report: Dict):
206
+ """Print a formatted link integrity report."""
207
+
208
+ if "error" in report:
209
+ print(f"❌ {report['error']}")
210
+ return
211
+
212
+ print("\n" + "="*60)
213
+ print("🔗 LINK INTEGRITY VALIDATION REPORT (Internal Links)")
214
+ print("="*60)
215
+
216
+ print(f"\n📊 Summary:")
217
+ print(f" Files Analyzed: {report['total_files']}")
218
+ print(f" Total Links: {report['total_links']}")
219
+ print(f" Internal Links: {report['internal_links']}")
220
+ print(f" External Links: {report['external_links']} (not validated)")
221
+ print(f" Valid Internal Links: {report['valid_internal']}")
222
+ print(f" Broken Internal Links: {report['broken_internal']}")
223
+ print(f" Internal Success Rate: {report['internal_success_rate']:.1f}%")
224
+
225
+ if report['internal_success_rate'] >= 95:
226
+ print(" ✅ Excellent internal link integrity!")
227
+ elif report['internal_success_rate'] >= 80:
228
+ print(" ⚠️ Good internal link integrity with some issues")
229
+ else:
230
+ print(" ❌ Poor internal link integrity - many broken links")
231
+
232
+ if report['broken_internal'] > 0:
233
+ print(f"\n❌ Broken Internal Links ({len(report['broken_internal_summary'])}):")
234
+ for i, broken in enumerate(report['broken_internal_summary'][:10], 1): # Show first 10
235
+ print(f" {i}. {broken['file']}:{broken['line']}")
236
+ print(f" URL: {broken['url']}")
237
+ print(f" Issue: {broken['message']}")
238
+
239
+ if len(report['broken_internal_summary']) > 10:
240
+ print(f" ... and {len(report['broken_internal_summary']) - 10} more broken links")
241
+
242
+ print(f"\n📁 Files with Most Links:")
243
+ sorted_files = sorted(report['file_results'], key=lambda x: x['total_links'], reverse=True)
244
+ for file_result in sorted_files[:5]:
245
+ status = "✅" if file_result['broken_internal'] == 0 else f"⚠️ {file_result['broken_internal']} broken"
246
+ print(f" • {file_result['file']}: {file_result['total_links']} links ({status})")
247
+
248
+ if __name__ == "__main__":
249
+ print("🚀 Starting Link Integrity Validation (Internal Links Only)...")
250
+
251
+ # Run validation with limited scope for performance
252
+ report = validate_link_integrity(max_files=20) # Limit to 20 files for demo
253
+ print_link_integrity_report(report)
254
+
255
+ # Save report to file
256
+ script_dir = Path(__file__).parent
257
+ report_file = script_dir / "link_integrity_report.json"
258
+
259
+ with open(report_file, 'w') as f:
260
+ json.dump(report, f, indent=2)
261
+
262
+ print(f"\n💾 Report saved to: {report_file}")
263
+
264
+ # Exit with appropriate code
265
+ if "error" in report:
266
+ exit(1)
267
+ elif report['internal_success_rate'] < 80:
268
+ print(f"\n⚠️ Internal link integrity below threshold: {report['internal_success_rate']:.1f}%")
269
+ exit(1)
270
+ else:
271
+ print(f"\n✅ Internal link integrity validation passed: {report['internal_success_rate']:.1f}%")
272
+ exit(0)
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env bash
2
+ # vds_sh_helpers.sh — General-purpose helpers for VDS shell scripts.
3
+ #
4
+ # Provides workspace root resolution plus a canonical uv invocation contract for
5
+ # maintained shell scripts under WHO-project/vds-scripts.
6
+ #
7
+ # Canonical contract:
8
+ # - resolve the vds-scripts workspace root
9
+ # - invoke scripts/worktree_uv.sh
10
+ # - bind the workspace explicitly with --directory <root> for non-root callers
11
+ # - target uv packages by their [project].name value when using --package
12
+ #
13
+ # Callers may continue to pass workspace member directory names (e.g. vds_cli,
14
+ # markdown_orchestrator); this helper resolves those to uv project names.
15
+
16
+ set -euo pipefail
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # vds_resolve_scripts_root
20
+ #
21
+ # Walk upward from this helper location until a pyproject.toml containing the
22
+ # vds-scripts workspace marker is found.
23
+ #
24
+ # Outputs the absolute path to the vds-scripts workspace root (no trailing /).
25
+ # ---------------------------------------------------------------------------
26
+ vds_resolve_scripts_root() {
27
+ local dir candidate depth
28
+ dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+ candidate="$dir"
30
+ depth=0
31
+
32
+ while [[ "$candidate" != "/" && $depth -lt 20 ]]; do
33
+ if [[ -f "$candidate/pyproject.toml" ]] && grep -q "vds-scripts-workspace" "$candidate/pyproject.toml" 2>/dev/null; then
34
+ printf '%s\n' "$candidate"
35
+ return 0
36
+ fi
37
+ candidate="$(dirname "$candidate")"
38
+ (( depth++ )) || true
39
+ done
40
+
41
+ local fallback
42
+ fallback="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
43
+ if [[ -f "$fallback/scripts/worktree_uv.sh" ]]; then
44
+ printf '%s\n' "$fallback"
45
+ return 0
46
+ fi
47
+
48
+ echo "vds_sh_helpers: cannot locate vds-scripts workspace root" >&2
49
+ return 1
50
+ }
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # resolve_insurance_docs_root [base]
54
+ #
55
+ # Locate the insurance-document directory, optionally relative to [base].
56
+ # Preserved for backward compatibility with scripts that already call it.
57
+ # ---------------------------------------------------------------------------
58
+ resolve_insurance_docs_root() {
59
+ local base="${1:-$(vds_resolve_scripts_root)}"
60
+
61
+ if [[ -n "${VDS_INSURANCE_DOCS_ROOT:-}" ]]; then
62
+ printf '%s\n' "${VDS_INSURANCE_DOCS_ROOT}"
63
+ return 0
64
+ fi
65
+
66
+ if [[ -d "$base/WHO-project/insurance-document" ]]; then
67
+ printf '%s\n' "$base/WHO-project/insurance-document"
68
+ return 0
69
+ fi
70
+
71
+ local match=""
72
+ match="$(find "$base" -maxdepth 2 -type d -path "$base/*-project/insurance-document" 2>/dev/null | sort | head -n 1)"
73
+ if [[ -n "$match" ]]; then
74
+ printf '%s\n' "$match"
75
+ return 0
76
+ fi
77
+
78
+ printf '%s\n' "$base/WHO-project/insurance-document"
79
+ }
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # vds_uv_project_name <member-or-package>
83
+ #
84
+ # Resolve a workspace member directory name (e.g. vds_cli) to the uv package
85
+ # name declared in that member's pyproject.toml (e.g. vds-cli). If the input is
86
+ # already not a member directory, return it unchanged to allow explicit package
87
+ # names from callers.
88
+ #
89
+ # Intentionally avoids a host-Python/tomllib dependency so scripts remain
90
+ # portable across shells where `python3` may be older than 3.11.
91
+ # ---------------------------------------------------------------------------
92
+ vds_uv_project_name() {
93
+ local member_or_package="${1:?vds_uv_project_name: member or package is required}"
94
+ local root member_pyproject line
95
+ root="$(vds_resolve_scripts_root)"
96
+ member_pyproject="$root/$member_or_package/pyproject.toml"
97
+
98
+ if [[ ! -f "$member_pyproject" ]]; then
99
+ printf '%s\n' "$member_or_package"
100
+ return 0
101
+ fi
102
+
103
+ line="$(awk '
104
+ BEGIN { in_project = 0 }
105
+ /^\[project\]/ { in_project = 1; next }
106
+ /^\[/ && in_project { exit }
107
+ in_project && $0 ~ /^[[:space:]]*name[[:space:]]*=/ { print; exit }
108
+ ' "$member_pyproject")"
109
+
110
+ if [[ -z "$line" ]]; then
111
+ echo "vds_sh_helpers: missing [project].name in $member_pyproject" >&2
112
+ return 1
113
+ fi
114
+
115
+ line="${line#*=}"
116
+ line="$(printf '%s' "$line" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
117
+ line="${line#\"}"
118
+ line="${line%\"}"
119
+ printf '%s\n' "$line"
120
+ }
121
+
122
+ # ---------------------------------------------------------------------------
123
+ # vds_worktree_uv <uv args...>
124
+ #
125
+ # Invoke scripts/worktree_uv.sh from the resolved workspace root.
126
+ # ---------------------------------------------------------------------------
127
+ vds_worktree_uv() {
128
+ local root wrapper
129
+ root="$(vds_resolve_scripts_root)"
130
+ wrapper="$root/scripts/worktree_uv.sh"
131
+
132
+ if [[ ! -x "$wrapper" ]]; then
133
+ echo "vds_sh_helpers: worktree_uv.sh not found or not executable at $wrapper" >&2
134
+ return 1
135
+ fi
136
+
137
+ "$wrapper" "$@"
138
+ }
139
+
140
+ # ---------------------------------------------------------------------------
141
+ # vds_uv_run_package <member-or-package> [command args...]
142
+ #
143
+ # Run a command from a workspace package through the worktree wrapper, binding
144
+ # the workspace explicitly with --directory for callers outside the root.
145
+ # ---------------------------------------------------------------------------
146
+ vds_uv_run_package() {
147
+ local member_or_package="${1:?vds_uv_run_package: member or package is required}"
148
+ shift
149
+ local root package_name
150
+ root="$(vds_resolve_scripts_root)"
151
+ package_name="$(vds_uv_project_name "$member_or_package")"
152
+
153
+ vds_worktree_uv run --directory "$root" --package "$package_name" "$@"
154
+ }
155
+
156
+ # ---------------------------------------------------------------------------
157
+ # vds_uv_sync_package <member-or-package> [extra uv sync args...]
158
+ #
159
+ # Sync a single workspace package.
160
+ # ---------------------------------------------------------------------------
161
+ vds_uv_sync_package() {
162
+ local member_or_package="${1:?vds_uv_sync_package: member or package is required}"
163
+ shift
164
+ local root package_name
165
+ root="$(vds_resolve_scripts_root)"
166
+ package_name="$(vds_uv_project_name "$member_or_package")"
167
+
168
+ vds_worktree_uv sync --directory "$root" --package "$package_name" "$@"
169
+ }
170
+
171
+ # ---------------------------------------------------------------------------
172
+ # vds_uv_sync_all [extra uv sync args...]
173
+ #
174
+ # Full workspace sync — syncs all members defined in the root pyproject.toml.
175
+ # ---------------------------------------------------------------------------
176
+ vds_uv_sync_all() {
177
+ local root
178
+ root="$(vds_resolve_scripts_root)"
179
+ vds_worktree_uv sync --directory "$root" --all-packages "$@"
180
+ }