@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.
- package/package.json +2 -2
- package/src/global.js +70 -10
- package/tools/skills/vds-scripts-skill/.openskills.json +6 -0
- package/tools/skills/vds-scripts-skill/QUALITY.md +44 -0
- package/tools/skills/vds-scripts-skill/SKILL.md +135 -0
- package/tools/skills/vds-scripts-skill/references/audit-commands.md +171 -0
- package/tools/skills/vds-scripts-skill/references/capability-index.md +34 -0
- package/tools/skills/vds-scripts-skill/references/development-commands.md +12 -0
- package/tools/skills/vds-scripts-skill/references/google-sheets.md +73 -0
- package/tools/skills/vds-scripts-skill/references/integration-commands.md +17 -0
- package/tools/skills/vds-scripts-skill/references/platform-bootstrap.md +31 -0
- package/tools/skills/vds-scripts-skill/references/specialist-routing.md +14 -0
- package/tools/skills/vds-scripts-skill/references/validation-commands.md +15 -0
- package/tools/skills/vsaf-build/SKILL.md +32 -2
- package/tools/skills/vsaf-ship/SKILL.md +41 -10
- package/tools/skills/vsaf-test/SKILL.md +8 -0
- package/tools/vds-scripts/.mcp.json +11 -0
- package/tools/vds-scripts/.secrets.baseline +133 -0
- package/tools/vds-scripts/AGENTS.md +152 -0
- package/tools/vds-scripts/CLAUDE.md +101 -0
- package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
- package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
- package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
- package/tools/vds-scripts/README.md +97 -0
- package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
- package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
- package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
- package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
- package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
- package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
- package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
- package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
- package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
- package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
- package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
- package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
- package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
- package/tools/vds-scripts/git_orchestrator/README.md +280 -0
- package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
- package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
- package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
- package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
- package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
- package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
- package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
- package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
- package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
- package/tools/vds-scripts/mcp_server/README.md +140 -0
- package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
- package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
- package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
- package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
- package/tools/vds-scripts/mypy.ini +5 -0
- package/tools/vds-scripts/pyproject.toml +29 -0
- package/tools/vds-scripts/repo-manifest.yaml +273 -0
- package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
- package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
- package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
- package/tools/vds-scripts/scripts/README.md +162 -0
- package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
- package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
- package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
- package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
- package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
- package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
- package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
- package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
- package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
- package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
- package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
- package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
- package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
- package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
- package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
- package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
- package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
- package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
- package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
- package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
- package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
- package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
- package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
- package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
- package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
- package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
- package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
- package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
- package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
- package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
- package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
- package/tools/vds-scripts/uv.lock +8 -0
- package/tools/vds-scripts/vds_cli/README.md +126 -0
- package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
- package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
- package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
- package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
- package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
- package/tools/vds-scripts/vds_cli/uv.lock +524 -0
- package/tools/vds-scripts/vds_cli_common/README.md +190 -0
- package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
- package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
- 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
|
+
}
|