rosett-ai 1.3.3
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.
- checksums.yaml +7 -0
- data/.ai-provenance.yml +119 -0
- data/.debride_whitelist +186 -0
- data/.fasterer.yml +29 -0
- data/.mdl_style.rb +10 -0
- data/.mdlrc +3 -0
- data/.mutant.yml +49 -0
- data/.namespace-allowlist +42 -0
- data/.reek.yml +1040 -0
- data/.rosett-ai/config.yml +3 -0
- data/.rspec +5 -0
- data/.rubocop.yml +380 -0
- data/.ruby-version +1 -0
- data/.yamllint +51 -0
- data/.yardopts +12 -0
- data/AI-DISCLOSURE.md +48 -0
- data/CHANGELOG.md +519 -0
- data/CLAUDE.md +141 -0
- data/CONTRIBUTING.md +734 -0
- data/INSTALL.md +154 -0
- data/LICENSE +674 -0
- data/LICENSE.md +675 -0
- data/QUICKSTART.md +73 -0
- data/README.md +366 -0
- data/Rakefile +200 -0
- data/SECURITY.md +114 -0
- data/bin/rai +1 -0
- data/cliff.toml +52 -0
- data/conf/adopt_redactions.yml +8 -0
- data/conf/behaviour/.gitkeep +0 -0
- data/conf/compliance/cra_rules.yml +25 -0
- data/conf/compliance/license_rules.yml +20 -0
- data/conf/design/aaif_alignment.yml +181 -0
- data/conf/design/ab_testing.yml +172 -0
- data/conf/design/accessibility.yml +84 -0
- data/conf/design/ai_authorship.yml +210 -0
- data/conf/design/ai_provenance.yml +224 -0
- data/conf/design/ai_tool_configuration.yml +207 -0
- data/conf/design/architecture.yml +139 -0
- data/conf/design/autocompletion.yml +115 -0
- data/conf/design/backward_compatibility.yml +112 -0
- data/conf/design/behaviour_composition.yml +246 -0
- data/conf/design/build_rake_extraction.yml +57 -0
- data/conf/design/ci_pipeline.yml +100 -0
- data/conf/design/claude_code_configuration.yml +157 -0
- data/conf/design/compiler.yml +128 -0
- data/conf/design/comply.yml +153 -0
- data/conf/design/content_packs.yml +84 -0
- data/conf/design/desktop_integration.yml +289 -0
- data/conf/design/distribution.yml +216 -0
- data/conf/design/doctor.yml +184 -0
- data/conf/design/documentation.yml +152 -0
- data/conf/design/engine_architecture.yml +257 -0
- data/conf/design/error_handling.yml +103 -0
- data/conf/design/feature_flags.yml +142 -0
- data/conf/design/git_hooks.yml +165 -0
- data/conf/design/gui_plugins.yml +475 -0
- data/conf/design/i18n.yml +84 -0
- data/conf/design/integration_testing.yml +56 -0
- data/conf/design/licensing_system.yml +88 -0
- data/conf/design/lifecycle_management.yml +208 -0
- data/conf/design/mcp_integration.yml +207 -0
- data/conf/design/mcp_settings.yml +126 -0
- data/conf/design/migration.yml +56 -0
- data/conf/design/monitoring_observability.yml +194 -0
- data/conf/design/namespace_cleanup.yml +145 -0
- data/conf/design/plugin_test_segregation.yml +145 -0
- data/conf/design/policy_management.yml +229 -0
- data/conf/design/project_management.yml +183 -0
- data/conf/design/rai_mcp_asset_discovery.yml +164 -0
- data/conf/design/rai_mcp_server.yml +605 -0
- data/conf/design/release_management.yml +117 -0
- data/conf/design/retrofit.yml +199 -0
- data/conf/design/retrospective_analyzer.yml +79 -0
- data/conf/design/scope_hierarchy.yml +352 -0
- data/conf/design/security.yml +115 -0
- data/conf/design/session_retrospective.yml +85 -0
- data/conf/design/smart_ui_feedback.yml +89 -0
- data/conf/design/structured_logging.yml +148 -0
- data/conf/design/styles.yml +123 -0
- data/conf/design/test_peer_review.yml +89 -0
- data/conf/design/testing.yml +136 -0
- data/conf/design/threat_model.yml +108 -0
- data/conf/design/ui_framework.yml +111 -0
- data/conf/design/usage_optimization.yml +122 -0
- data/conf/design/version_management.yml +60 -0
- data/conf/design/workflow.yml +227 -0
- data/conf/mcp/server_defaults.yml +42 -0
- data/conf/mcp/trust.yml +21 -0
- data/conf/packaging/core.yml +12 -0
- data/conf/packaging/gtk4.yml +11 -0
- data/conf/packaging/qt6.yml +11 -0
- data/conf/policy/default_deny_list.yml +197 -0
- data/conf/review/cli-command-audit.yml +857 -0
- data/conf/review/design-docs.yml +1064 -0
- data/conf/review/design-questionnaire.yml +153 -0
- data/conf/review/questionnaire.yml +146 -0
- data/conf/review/rosett-ai-core.yml +2919 -0
- data/conf/schemas/ai_config_schema.json +73 -0
- data/conf/schemas/behaviour_schema.json +132 -0
- data/conf/schemas/compliance_rule_schema.json +63 -0
- data/conf/schemas/content_pack_manifest_schema.json +51 -0
- data/conf/schemas/design_schema.json +210 -0
- data/conf/schemas/engine_manifest_schema.json +144 -0
- data/conf/schemas/lockfile_schema.json +74 -0
- data/conf/schemas/mcp_server_schema.json +48 -0
- data/conf/schemas/packaging_schema.json +70 -0
- data/conf/schemas/policy_schema.json +85 -0
- data/conf/schemas/provenance_schema.json +84 -0
- data/conf/schemas/rai_config_schema.json +56 -0
- data/conf/schemas/rai_project_schema.json +20 -0
- data/conf/schemas/scope_hierarchy_schema.json +49 -0
- data/conf/schemas/target_schema.json +67 -0
- data/conf/schemas/tooling_schema.json +65 -0
- data/conf/schemas/workflow_schema.json +112 -0
- data/conf/targets/agents_md.yml +17 -0
- data/conf/targets/claude.yml +12 -0
- data/conf/tooling/tools.yml +58 -0
- data/dist/rosett-ai-mcp.service +48 -0
- data/dist/rosett-ai-mcp.yml.default +45 -0
- data/doc/AAIF_POSITIONING.md +58 -0
- data/doc/ADOPT.md +224 -0
- data/doc/AI_PROVENANCE.md +139 -0
- data/doc/ARCHITECTURE.md +920 -0
- data/doc/BEHAVIOUR.md +409 -0
- data/doc/BUILD.md +138 -0
- data/doc/CI_CD_RECIPES.md +171 -0
- data/doc/CLAUDE_SESSIONS_MOVED.md +16 -0
- data/doc/COMMAND_ANALYSIS.md +229 -0
- data/doc/CONFIGURATION.md +281 -0
- data/doc/DESIGN_AUDIT.md +235 -0
- data/doc/DESIGN_PEER_REVIEW.md +771 -0
- data/doc/DESKTOP.md +447 -0
- data/doc/ENGINES.md +567 -0
- data/doc/ENGINE_DEVELOPMENT_GUIDE.md +417 -0
- data/doc/FEATURE_AUDIT.md +218 -0
- data/doc/IMPLEMENTATION_PLAN.md +669 -0
- data/doc/INCIDENT_REPORT_2026-02-02.md +251 -0
- data/doc/MIGRATION_GUIDE.md +88 -0
- data/doc/PACKAGING.md +232 -0
- data/doc/PROJECT_DASHBOARD.md +153 -0
- data/doc/PULP_DEPLOYMENT.md +164 -0
- data/doc/QUALITY_FIX_SUMMARY.md +110 -0
- data/doc/QUICK_START.md +162 -0
- data/doc/REEK_CONFIGURATION.md +166 -0
- data/doc/REFERENCE.md +253 -0
- data/doc/REFERENCES.md +324 -0
- data/doc/SECURITY_REVIEW_CHECKLIST.md +72 -0
- data/doc/SESSION_2026-02-28_GTK4_HARDENING.md +359 -0
- data/doc/SETUP.md +202 -0
- data/doc/TEST_PEER_REVIEW.md +152 -0
- data/doc/THREAT_MODEL.md +230 -0
- data/doc/USAGE.md +545 -0
- data/doc/USER_MANUAL.md +585 -0
- data/doc/ai_test_review_checklist.md +110 -0
- data/doc/changes/2026-02-18-packaging-fpm.md +155 -0
- data/doc/changes/2026-02-19-testing-infrastructure.md +221 -0
- data/doc/changes/2026-02-20-security-implementation.md +281 -0
- data/doc/changes/2026-02-20-styles-implementation.md +220 -0
- data/doc/changes/2026-02-21-architecture-completion.md +95 -0
- data/doc/changes/2026-02-21-architecture-ui-layer.md +253 -0
- data/doc/changes/2026-02-21-cc-config-implementation.md +108 -0
- data/doc/changes/2026-02-21-ci-pipeline-implementation.md +214 -0
- data/doc/changes/2026-02-21-compiler-multi-target-pipeline.md +241 -0
- data/doc/changes/2026-02-21-config-design-show-commands.md +61 -0
- data/doc/changes/2026-02-21-design-implementation-overview.md +455 -0
- data/doc/changes/2026-02-21-lifecycle-management.md +196 -0
- data/doc/changes/2026-02-21-path-resolver.md +128 -0
- data/doc/changes/2026-02-24-ci-tmpdir-mutant-fetch.md +45 -0
- data/doc/changes/2026-03-01-ci-bundler-strategy.md +120 -0
- data/doc/changes/2026-03-20-security-hardening-phase2.md +163 -0
- data/doc/context/SESSION-HANDOFF.md +69 -0
- data/doc/context/ai-engine-usage-trends-2026.md +80 -0
- data/doc/context/plan-pluggable-engines.md +590 -0
- data/doc/decisions/001-flog-deferred.md +32 -0
- data/doc/decisions/002-path-resolution-strategy.md +158 -0
- data/doc/decisions/003-ui-adapter-selection.md +193 -0
- data/doc/decisions/004-design-document-validation.md +179 -0
- data/doc/decisions/005-package-splitting-strategy.md +200 -0
- data/doc/decisions/006-multi-engine-architecture.md +147 -0
- data/doc/decisions/007-engine-agnostic-pivot.md +219 -0
- data/doc/decisions/008-ci-bundler-strategy.md +129 -0
- data/doc/decisions/009-core-only-v1-release.md +60 -0
- data/doc/decisions/010-engine-debian-packaging.md +66 -0
- data/doc/decisions/011-context-aware-cli.md +71 -0
- data/doc/dependency_decisions.yml +247 -0
- data/doc/issues/001-wrapper-missing-environment-variables.md +197 -0
- data/doc/issues/002-embedded-ruby-wrong-prefix.md +217 -0
- data/doc/issues/003-smoke-test-false-positive.md +127 -0
- data/doc/issues/004-market-research-design-updates.md +109 -0
- data/doc/issues/005-compile-scope-coexistence.md +161 -0
- data/doc/locales/.gitkeep +0 -0
- data/doc/man/rai.1.ronn +505 -0
- data/doc/operations/packaging.md +133 -0
- data/doc/operations/rosett-ai-release.md +65 -0
- data/doc/reference/error-catalog.md +107 -0
- data/doc/reference/rosett-ai-technical-reference.pdf +0 -0
- data/doc/reference/src/Pictures/cover.jpg +0 -0
- data/doc/reference/src/Pictures/head1.jpg +0 -0
- data/doc/reference/src/Pictures/head2.jpg +0 -0
- data/doc/reference/src/Pictures/head3.jpg +0 -0
- data/doc/reference/src/Pictures/head4.jpg +0 -0
- data/doc/reference/src/Pictures/head5.jpg +0 -0
- data/doc/reference/src/Pictures/head6.jpg +0 -0
- data/doc/reference/src/Pictures/head7.jpg +0 -0
- data/doc/reference/src/Pictures/head8.jpg +0 -0
- data/doc/reference/src/StyleInd.ist +4 -0
- data/doc/reference/src/bibliography.bib +79 -0
- data/doc/reference/src/main.tex +1288 -0
- data/doc/reference/src/structure.tex +303 -0
- data/doc/rosett-ai-bookmarks.html +301 -0
- data/kitchen.yml +46 -0
- data/lib/rosett_ai/adopter/executor_resolver.rb +77 -0
- data/lib/rosett_ai/adopter/local_analysis_collector.rb +154 -0
- data/lib/rosett_ai/adopter/rule_adopter.rb +254 -0
- data/lib/rosett_ai/ai_config/config_compiler.rb +111 -0
- data/lib/rosett_ai/ai_config/context_window.rb +55 -0
- data/lib/rosett_ai/ai_config/cost_controls.rb +44 -0
- data/lib/rosett_ai/ai_config/fallback_chain.rb +64 -0
- data/lib/rosett_ai/ai_config/model_router.rb +121 -0
- data/lib/rosett_ai/ai_config/validator.rb +45 -0
- data/lib/rosett_ai/authorship/attribution_compiler.rb +99 -0
- data/lib/rosett_ai/authorship/disclosure_policy.rb +81 -0
- data/lib/rosett_ai/authorship/review_validator.rb +39 -0
- data/lib/rosett_ai/authorship/trailer_generator.rb +88 -0
- data/lib/rosett_ai/backup/compressor.rb +180 -0
- data/lib/rosett_ai/backup/destination.rb +91 -0
- data/lib/rosett_ai/behaviour/manager.rb +156 -0
- data/lib/rosett_ai/compiler/backend.rb +86 -0
- data/lib/rosett_ai/compiler/backends/agents_md_backend.rb +80 -0
- data/lib/rosett_ai/compiler/backends/claude_backend.rb +88 -0
- data/lib/rosett_ai/compiler/backends/generic_backend.rb +15 -0
- data/lib/rosett_ai/compiler/behaviour_compiler.rb +40 -0
- data/lib/rosett_ai/compiler/capability_checker.rb +104 -0
- data/lib/rosett_ai/compiler/compilation_pipeline.rb +361 -0
- data/lib/rosett_ai/compiler/compiled_output.rb +39 -0
- data/lib/rosett_ai/compiler/locale_compiler.rb +250 -0
- data/lib/rosett_ai/compiler/target_profile.rb +112 -0
- data/lib/rosett_ai/completion/generator.rb +101 -0
- data/lib/rosett_ai/completion/shells/bash_generator.rb +126 -0
- data/lib/rosett_ai/completion/shells/fish_generator.rb +78 -0
- data/lib/rosett_ai/completion/shells/zsh_generator.rb +126 -0
- data/lib/rosett_ai/comply/checkers/cra_checker.rb +102 -0
- data/lib/rosett_ai/comply/checkers/license_checker.rb +85 -0
- data/lib/rosett_ai/comply/checkers/spdx_header_checker.rb +98 -0
- data/lib/rosett_ai/comply/reporter.rb +113 -0
- data/lib/rosett_ai/comply/runner.rb +50 -0
- data/lib/rosett_ai/composition/circular_dependency_detector.rb +56 -0
- data/lib/rosett_ai/composition/composer.rb +158 -0
- data/lib/rosett_ai/composition/composition_result.rb +64 -0
- data/lib/rosett_ai/composition/conflict_detector.rb +53 -0
- data/lib/rosett_ai/composition/lockfile.rb +103 -0
- data/lib/rosett_ai/composition/merge_strategy.rb +131 -0
- data/lib/rosett_ai/composition/priority_sorter.rb +29 -0
- data/lib/rosett_ai/composition/scope_resolver.rb +55 -0
- data/lib/rosett_ai/config/compile_result.rb +37 -0
- data/lib/rosett_ai/config/compiler.rb +13 -0
- data/lib/rosett_ai/config/domain_transformer.rb +13 -0
- data/lib/rosett_ai/config/key_map.rb +13 -0
- data/lib/rosett_ai/config/masking_secret_resolver.rb +40 -0
- data/lib/rosett_ai/config/scope_router.rb +13 -0
- data/lib/rosett_ai/config/secret_resolver.rb +125 -0
- data/lib/rosett_ai/configuration.rb +119 -0
- data/lib/rosett_ai/content/content_client.rb +60 -0
- data/lib/rosett_ai/content/pack_installer.rb +117 -0
- data/lib/rosett_ai/content/pack_manifest.rb +50 -0
- data/lib/rosett_ai/content/pack_registry.rb +68 -0
- data/lib/rosett_ai/content_packs/manager.rb +50 -0
- data/lib/rosett_ai/dbus/compositor_detector.rb +77 -0
- data/lib/rosett_ai/dbus/focus_adapters/base.rb +59 -0
- data/lib/rosett_ai/dbus/focus_adapters/gnome_adapter.rb +172 -0
- data/lib/rosett_ai/dbus/focus_adapters/hyprland_adapter.rb +77 -0
- data/lib/rosett_ai/dbus/focus_adapters/i3_adapter.rb +65 -0
- data/lib/rosett_ai/dbus/focus_adapters/kwin_adapter.rb +103 -0
- data/lib/rosett_ai/dbus/focus_adapters/x11_adapter.rb +105 -0
- data/lib/rosett_ai/dbus/focus_monitor_interface.rb +103 -0
- data/lib/rosett_ai/dbus/manager_interface.rb +213 -0
- data/lib/rosett_ai/dbus/plugin_manager_interface.rb +169 -0
- data/lib/rosett_ai/dbus/rate_limiter.rb +89 -0
- data/lib/rosett_ai/dbus/service.rb +121 -0
- data/lib/rosett_ai/dbus/status_notifier_interface.rb +79 -0
- data/lib/rosett_ai/deprecation.rb +79 -0
- data/lib/rosett_ai/desktop/dbus_client.rb +259 -0
- data/lib/rosett_ai/desktop/gtk4_app.rb +371 -0
- data/lib/rosett_ai/desktop/gtk4_preferences.rb +331 -0
- data/lib/rosett_ai/desktop/gui_logger.rb +236 -0
- data/lib/rosett_ai/doctor/check.rb +92 -0
- data/lib/rosett_ai/doctor/checks/cache_health_check.rb +50 -0
- data/lib/rosett_ai/doctor/checks/dbus_availability_check.rb +39 -0
- data/lib/rosett_ai/doctor/checks/engine_detection_check.rb +46 -0
- data/lib/rosett_ai/doctor/checks/file_permission_check.rb +44 -0
- data/lib/rosett_ai/doctor/checks/gem_dependency_check.rb +55 -0
- data/lib/rosett_ai/doctor/checks/ruby_version_check.rb +50 -0
- data/lib/rosett_ai/doctor/checks/stale_config_nncc_check.rb +57 -0
- data/lib/rosett_ai/doctor/checks/stale_home_nncc_check.rb +59 -0
- data/lib/rosett_ai/doctor.rb +81 -0
- data/lib/rosett_ai/documentation/reference_compiler.rb +122 -0
- data/lib/rosett_ai/documentation/translator.rb +62 -0
- data/lib/rosett_ai/engines/base_config_compiler.rb +203 -0
- data/lib/rosett_ai/engines/detector.rb +63 -0
- data/lib/rosett_ai/engines/registry.rb +50 -0
- data/lib/rosett_ai/error_handler.rb +139 -0
- data/lib/rosett_ai/exit_codes.rb +76 -0
- data/lib/rosett_ai/feature_flags.rb +102 -0
- data/lib/rosett_ai/formatting.rb +33 -0
- data/lib/rosett_ai/gem_consistency_checker.rb +199 -0
- data/lib/rosett_ai/git_hooks/chain_detector.rb +86 -0
- data/lib/rosett_ai/git_hooks/installer.rb +175 -0
- data/lib/rosett_ai/git_hooks/script_generator.rb +125 -0
- data/lib/rosett_ai/gitlab/validators/supplementary_gitlab_ci_yaml_validator.rb +79 -0
- data/lib/rosett_ai/i18n/locale_resolver.rb +46 -0
- data/lib/rosett_ai/i18n/utf8_checker.rb +32 -0
- data/lib/rosett_ai/init/config_file_writer.rb +24 -0
- data/lib/rosett_ai/init/directory_builder.rb +38 -0
- data/lib/rosett_ai/init/file_copier.rb +95 -0
- data/lib/rosett_ai/init/global_initializer.rb +28 -0
- data/lib/rosett_ai/init/local_initializer.rb +27 -0
- data/lib/rosett_ai/init/mcp_registrar.rb +109 -0
- data/lib/rosett_ai/init/project_initializer.rb +38 -0
- data/lib/rosett_ai/licensing/license_key.rb +139 -0
- data/lib/rosett_ai/licensing/license_store.rb +64 -0
- data/lib/rosett_ai/licensing/license_validator.rb +60 -0
- data/lib/rosett_ai/licensing/tier.rb +42 -0
- data/lib/rosett_ai/mcp/admin/auditor.rb +88 -0
- data/lib/rosett_ai/mcp/admin/health_checker.rb +81 -0
- data/lib/rosett_ai/mcp/admin/registry.rb +100 -0
- data/lib/rosett_ai/mcp/admin/schema_validator.rb +63 -0
- data/lib/rosett_ai/mcp/enforcement/.gitkeep +0 -0
- data/lib/rosett_ai/mcp/enforcement/hook_generator.rb +197 -0
- data/lib/rosett_ai/mcp/enforcement/validator.rb +215 -0
- data/lib/rosett_ai/mcp/governance.rb +160 -0
- data/lib/rosett_ai/mcp/http_security_config.rb +158 -0
- data/lib/rosett_ai/mcp/instructions.rb +266 -0
- data/lib/rosett_ai/mcp/key_hasher.rb +66 -0
- data/lib/rosett_ai/mcp/keyfile.rb +221 -0
- data/lib/rosett_ai/mcp/middleware/authentication.rb +146 -0
- data/lib/rosett_ai/mcp/middleware/content_type.rb +56 -0
- data/lib/rosett_ai/mcp/middleware/cors.rb +83 -0
- data/lib/rosett_ai/mcp/middleware/origin_validation.rb +73 -0
- data/lib/rosett_ai/mcp/middleware/rate_limit.rb +106 -0
- data/lib/rosett_ai/mcp/middleware/request_size.rb +51 -0
- data/lib/rosett_ai/mcp/plugins.rb +143 -0
- data/lib/rosett_ai/mcp/prompts/compilation_prompt.rb +40 -0
- data/lib/rosett_ai/mcp/prompts/compliance_prompt.rb +41 -0
- data/lib/rosett_ai/mcp/prompts/diagnostics_prompt.rb +41 -0
- data/lib/rosett_ai/mcp/prompts/validation_prompt.rb +41 -0
- data/lib/rosett_ai/mcp/resources/behaviour_resource.rb +127 -0
- data/lib/rosett_ai/mcp/resources/config_resource.rb +72 -0
- data/lib/rosett_ai/mcp/resources/design_resource.rb +58 -0
- data/lib/rosett_ai/mcp/resources/hooks_resource.rb +74 -0
- data/lib/rosett_ai/mcp/resources/provenance_resource.rb +51 -0
- data/lib/rosett_ai/mcp/resources/rules_resource.rb +60 -0
- data/lib/rosett_ai/mcp/resources/schema_resource.rb +72 -0
- data/lib/rosett_ai/mcp/response_helper.rb +46 -0
- data/lib/rosett_ai/mcp/security_logger.rb +60 -0
- data/lib/rosett_ai/mcp/server.rb +212 -0
- data/lib/rosett_ai/mcp/settings/server_installer.rb +112 -0
- data/lib/rosett_ai/mcp/settings/trust_manager.rb +142 -0
- data/lib/rosett_ai/mcp/tools/adopt_tool.rb +70 -0
- data/lib/rosett_ai/mcp/tools/backup_tool.rb +64 -0
- data/lib/rosett_ai/mcp/tools/behaviour_display_tool.rb +72 -0
- data/lib/rosett_ai/mcp/tools/behaviour_list_tool.rb +56 -0
- data/lib/rosett_ai/mcp/tools/behaviour_manage_tool.rb +114 -0
- data/lib/rosett_ai/mcp/tools/behaviour_show_tool.rb +62 -0
- data/lib/rosett_ai/mcp/tools/compile_status_tool.rb +122 -0
- data/lib/rosett_ai/mcp/tools/compile_tool.rb +191 -0
- data/lib/rosett_ai/mcp/tools/comply_tool.rb +79 -0
- data/lib/rosett_ai/mcp/tools/config_compile_tool.rb +71 -0
- data/lib/rosett_ai/mcp/tools/config_status_tool.rb +79 -0
- data/lib/rosett_ai/mcp/tools/content_tool.rb +78 -0
- data/lib/rosett_ai/mcp/tools/context_query_tool.rb +156 -0
- data/lib/rosett_ai/mcp/tools/design_list_tool.rb +57 -0
- data/lib/rosett_ai/mcp/tools/design_show_tool.rb +69 -0
- data/lib/rosett_ai/mcp/tools/doctor_tool.rb +62 -0
- data/lib/rosett_ai/mcp/tools/documentation_status_tool.rb +45 -0
- data/lib/rosett_ai/mcp/tools/engines_tool.rb +84 -0
- data/lib/rosett_ai/mcp/tools/hook_install_tool.rb +190 -0
- data/lib/rosett_ai/mcp/tools/hook_preview_tool.rb +173 -0
- data/lib/rosett_ai/mcp/tools/hooks_status_tool.rb +84 -0
- data/lib/rosett_ai/mcp/tools/init_tool.rb +87 -0
- data/lib/rosett_ai/mcp/tools/license_status_tool.rb +44 -0
- data/lib/rosett_ai/mcp/tools/project_tool.rb +117 -0
- data/lib/rosett_ai/mcp/tools/provenance_tool.rb +97 -0
- data/lib/rosett_ai/mcp/tools/provenance_write_tool.rb +40 -0
- data/lib/rosett_ai/mcp/tools/retrofit_tool.rb +81 -0
- data/lib/rosett_ai/mcp/tools/rule_search_tool.rb +163 -0
- data/lib/rosett_ai/mcp/tools/schema_get_tool.rb +94 -0
- data/lib/rosett_ai/mcp/tools/tooling_tool.rb +86 -0
- data/lib/rosett_ai/mcp/tools/validate_tool.rb +105 -0
- data/lib/rosett_ai/mcp/tools/workflow_execute_tool.rb +74 -0
- data/lib/rosett_ai/mcp/tools/workflow_tool.rb +78 -0
- data/lib/rosett_ai/migration/detector.rb +117 -0
- data/lib/rosett_ai/migration/nncc_config_migrator.rb +94 -0
- data/lib/rosett_ai/migration/nncc_project_migrator.rb +90 -0
- data/lib/rosett_ai/migration/xdg_migrator.rb +123 -0
- data/lib/rosett_ai/package_manager/apt.rb +108 -0
- data/lib/rosett_ai/package_manager/base.rb +68 -0
- data/lib/rosett_ai/package_manager/gem_backend.rb +90 -0
- data/lib/rosett_ai/packaging/variant_config.rb +92 -0
- data/lib/rosett_ai/path_resolver.rb +115 -0
- data/lib/rosett_ai/plugins/contract.rb +43 -0
- data/lib/rosett_ai/plugins/engine_contract.rb +60 -0
- data/lib/rosett_ai/plugins/gui_contract.rb +74 -0
- data/lib/rosett_ai/plugins/mcp_contract.rb +48 -0
- data/lib/rosett_ai/plugins/registry.rb +150 -0
- data/lib/rosett_ai/policy/auditor.rb +41 -0
- data/lib/rosett_ai/policy/deny_list.rb +71 -0
- data/lib/rosett_ai/policy/opt_out_scanner.rb +37 -0
- data/lib/rosett_ai/policy/policy_compiler.rb +84 -0
- data/lib/rosett_ai/policy/protected_files.rb +47 -0
- data/lib/rosett_ai/policy/tier_hierarchy.rb +48 -0
- data/lib/rosett_ai/policy/validator.rb +35 -0
- data/lib/rosett_ai/profiler.rb +79 -0
- data/lib/rosett_ai/project/drift_detector.rb +126 -0
- data/lib/rosett_ai/project/manager.rb +115 -0
- data/lib/rosett_ai/project/sync_manager.rb +138 -0
- data/lib/rosett_ai/project/template_applier.rb +105 -0
- data/lib/rosett_ai/project_context.rb +82 -0
- data/lib/rosett_ai/provenance/entry.rb +63 -0
- data/lib/rosett_ai/provenance/file_source.rb +32 -0
- data/lib/rosett_ai/provenance/source.rb +62 -0
- data/lib/rosett_ai/provenance/store.rb +153 -0
- data/lib/rosett_ai/provenance/tracker.rb +62 -0
- data/lib/rosett_ai/provenance/trailer_generator.rb +43 -0
- data/lib/rosett_ai/provenance/validator.rb +45 -0
- data/lib/rosett_ai/quorum/collector.rb +59 -0
- data/lib/rosett_ai/quorum/comparator.rb +81 -0
- data/lib/rosett_ai/quorum/dispatcher.rb +57 -0
- data/lib/rosett_ai/quorum/strategies/adopt.rb +56 -0
- data/lib/rosett_ai/rai_config.rb +107 -0
- data/lib/rosett_ai/retrofit/base_parser.rb +66 -0
- data/lib/rosett_ai/retrofit/engine.rb +171 -0
- data/lib/rosett_ai/retrofit/parsers/agents_md_parser.rb +50 -0
- data/lib/rosett_ai/retrofit/parsers/claude_parser.rb +69 -0
- data/lib/rosett_ai/retrofit/parsers/cursor_parser.rb +82 -0
- data/lib/rosett_ai/retrofit/round_trip_validator.rb +65 -0
- data/lib/rosett_ai/retrofit/scanner.rb +47 -0
- data/lib/rosett_ai/retrofit/secret_detector.rb +87 -0
- data/lib/rosett_ai/secrets_resolver.rb +71 -0
- data/lib/rosett_ai/smart_feedback/suggester.rb +83 -0
- data/lib/rosett_ai/smart_feedback/thor_middleware.rb +84 -0
- data/lib/rosett_ai/structured_logger.rb +110 -0
- data/lib/rosett_ai/telemetry/json_lines_writer.rb +50 -0
- data/lib/rosett_ai/telemetry/log_rotator.rb +67 -0
- data/lib/rosett_ai/telemetry/provider.rb +26 -0
- data/lib/rosett_ai/telemetry/reporter.rb +144 -0
- data/lib/rosett_ai/telemetry.rb +47 -0
- data/lib/rosett_ai/text_sanitizer.rb +62 -0
- data/lib/rosett_ai/thor/cli.rb +269 -0
- data/lib/rosett_ai/thor/tasks/adopt.rb +250 -0
- data/lib/rosett_ai/thor/tasks/backup.rb +420 -0
- data/lib/rosett_ai/thor/tasks/behaviour.rb +474 -0
- data/lib/rosett_ai/thor/tasks/build.rb +1162 -0
- data/lib/rosett_ai/thor/tasks/compile.rb +415 -0
- data/lib/rosett_ai/thor/tasks/completion.rb +123 -0
- data/lib/rosett_ai/thor/tasks/comply.rb +82 -0
- data/lib/rosett_ai/thor/tasks/config.rb +265 -0
- data/lib/rosett_ai/thor/tasks/content.rb +193 -0
- data/lib/rosett_ai/thor/tasks/dbus.rb +321 -0
- data/lib/rosett_ai/thor/tasks/design.rb +258 -0
- data/lib/rosett_ai/thor/tasks/desktop.rb +129 -0
- data/lib/rosett_ai/thor/tasks/doctor.rb +127 -0
- data/lib/rosett_ai/thor/tasks/documentation.rb +321 -0
- data/lib/rosett_ai/thor/tasks/engines.rb +167 -0
- data/lib/rosett_ai/thor/tasks/hooks.rb +219 -0
- data/lib/rosett_ai/thor/tasks/init.rb +259 -0
- data/lib/rosett_ai/thor/tasks/license.rb +120 -0
- data/lib/rosett_ai/thor/tasks/mcp.rb +535 -0
- data/lib/rosett_ai/thor/tasks/migrate.rb +121 -0
- data/lib/rosett_ai/thor/tasks/plugins.rb +157 -0
- data/lib/rosett_ai/thor/tasks/project.rb +260 -0
- data/lib/rosett_ai/thor/tasks/provenance.rb +195 -0
- data/lib/rosett_ai/thor/tasks/release.rb +314 -0
- data/lib/rosett_ai/thor/tasks/retrofit.rb +90 -0
- data/lib/rosett_ai/thor/tasks/tooling.rb +308 -0
- data/lib/rosett_ai/thor/tasks/validate.rb +108 -0
- data/lib/rosett_ai/thor/tasks/workflow.rb +196 -0
- data/lib/rosett_ai/tooling/ci_yaml_validator.rb +37 -0
- data/lib/rosett_ai/tooling/version_checker.rb +35 -0
- data/lib/rosett_ai/ui/accessible_tui.rb +61 -0
- data/lib/rosett_ai/ui/base.rb +46 -0
- data/lib/rosett_ai/ui/gtk4.rb +98 -0
- data/lib/rosett_ai/ui/kde.rb +40 -0
- data/lib/rosett_ai/ui/qt6.rb +40 -0
- data/lib/rosett_ai/ui/registry.rb +60 -0
- data/lib/rosett_ai/ui/tty_helper.rb +74 -0
- data/lib/rosett_ai/ui/tui.rb +59 -0
- data/lib/rosett_ai/validators/behaviour_validator.rb +20 -0
- data/lib/rosett_ai/validators/design_validator.rb +17 -0
- data/lib/rosett_ai/validators/schema_validator.rb +84 -0
- data/lib/rosett_ai/validators/tooling_validator.rb +17 -0
- data/lib/rosett_ai/version.rb +8 -0
- data/lib/rosett_ai/version_consistency_checker.rb +129 -0
- data/lib/rosett_ai/workflow/audit_log.rb +86 -0
- data/lib/rosett_ai/workflow/engine.rb +142 -0
- data/lib/rosett_ai/workflow/manager.rb +82 -0
- data/lib/rosett_ai/workflow/schema_validator.rb +71 -0
- data/lib/rosett_ai/workflow/step_runner.rb +61 -0
- data/lib/rosett_ai/workflow/steps/prompt_step.rb +62 -0
- data/lib/rosett_ai/workflow/steps/rai_step.rb +74 -0
- data/lib/rosett_ai/workflow/steps/shell_step.rb +53 -0
- data/lib/rosett_ai/yaml_loader.rb +78 -0
- data/lib/rosett_ai.rb +221 -0
- data/lib/rubocop/cop/rosett_ai/shell_interpolation.rb +54 -0
- data/lib/rubocop/cop/rosett_ai/unsafe_const_get.rb +60 -0
- data/lib/rubocop/cop/rosett_ai/unsafe_send.rb +50 -0
- data/lib/rubocop/cop/rosett_ai/unsafe_yaml_load.rb +40 -0
- data/lib/rubocop/rosett_ai.rb +9 -0
- data/lib/scripts/generated/docker_hub_tags.rb +126 -0
- data/locales/.gitkeep +0 -0
- data/locales/ar.yml +579 -0
- data/locales/en.yml +571 -0
- data/locales/fr.yml +567 -0
- data/packaging/build-engine-deb.sh +81 -0
- data/packaging/scripts/postinst +17 -0
- data/packaging/scripts/postrm +19 -0
- data/packaging/scripts/prerm +10 -0
- data/packaging/wrapper.sh.template +38 -0
- data/rosett-ai.gemspec +63 -0
- data/rules/.gitkeep +0 -0
- data/scripts/publish/pulp_upload.sh +123 -0
- data/settings.json +29 -0
- data/share/applications/be.neatnerds.rosettai.desktop +29 -0
- data/share/dbus-1/interfaces/be.neatnerds.rosettai.xml +103 -0
- data/share/dbus-1/services/be.neatnerds.rosettai.service +3 -0
- data/share/templates/behaviour/criticalthinking.yml +69 -0
- metadata +810 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'thor'
|
|
7
|
+
require 'rainbow'
|
|
8
|
+
require 'terminal-table'
|
|
9
|
+
|
|
10
|
+
module RosettAi
|
|
11
|
+
module Thor
|
|
12
|
+
module Tasks
|
|
13
|
+
# Thor tasks for CI/CD tooling validation
|
|
14
|
+
class Tooling < ::Thor
|
|
15
|
+
desc 'validate-ci-yaml', 'Validate GitLab CI YAML files for syntax errors'
|
|
16
|
+
long_desc <<~LONGDESC
|
|
17
|
+
Validates all GitLab CI YAML files (.gitlab-ci.yml and includes) for syntax errors.
|
|
18
|
+
Reports line and column numbers for any detected issues.
|
|
19
|
+
|
|
20
|
+
Flags:
|
|
21
|
+
--verbose: Show all validated files, not just errors
|
|
22
|
+
--project-dir: Project directory (defaults to current directory)
|
|
23
|
+
|
|
24
|
+
Exit codes:
|
|
25
|
+
0 - All files valid
|
|
26
|
+
1 - Validation failures
|
|
27
|
+
|
|
28
|
+
EXAMPLES
|
|
29
|
+
|
|
30
|
+
raictl tooling validate-ci-yaml
|
|
31
|
+
raictl tooling validate-ci-yaml --verbose
|
|
32
|
+
raictl tooling validate-ci-yaml --project-dir /path/to/project
|
|
33
|
+
|
|
34
|
+
Related commands: validate, check-versions
|
|
35
|
+
LONGDESC
|
|
36
|
+
method_option :verbose,
|
|
37
|
+
type: :boolean,
|
|
38
|
+
default: false,
|
|
39
|
+
desc: 'Show all validated files, not just errors'
|
|
40
|
+
method_option :project_dir,
|
|
41
|
+
type: :string,
|
|
42
|
+
default: nil,
|
|
43
|
+
desc: 'Project directory (defaults to current directory)'
|
|
44
|
+
|
|
45
|
+
def validate_ci_yaml
|
|
46
|
+
validator = RosettAi::Gitlab::Validators::SupplementaryGitlabCiYamlValidator.new(
|
|
47
|
+
project_dir: options[:project_dir] || ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
results = validator.validate
|
|
51
|
+
|
|
52
|
+
if results[:valid].empty? && results[:invalid].empty?
|
|
53
|
+
puts Rainbow(t('no_ci_files')).yellow
|
|
54
|
+
return
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
print_ci_results(results)
|
|
58
|
+
raise ::Thor::Error, t('ci_validation_failed') unless results[:invalid].empty?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
desc 'validate [NAME]', 'Validate tooling configuration file(s) against the tooling schema'
|
|
62
|
+
long_desc <<~LONGDESC
|
|
63
|
+
Validates tooling configuration files in conf/tooling/ against the tooling schema.
|
|
64
|
+
If NAME is provided, validates only that file; otherwise validates all tooling files.
|
|
65
|
+
|
|
66
|
+
Exit codes:
|
|
67
|
+
0 - All files valid
|
|
68
|
+
1 - Validation failures
|
|
69
|
+
|
|
70
|
+
EXAMPLES
|
|
71
|
+
|
|
72
|
+
raictl tooling validate
|
|
73
|
+
raictl tooling validate ci_pipeline
|
|
74
|
+
|
|
75
|
+
Related commands: validate-ci-yaml, check-versions
|
|
76
|
+
LONGDESC
|
|
77
|
+
def validate(name = nil)
|
|
78
|
+
name = RosettAi::TextSanitizer.normalize_nfc(name) if name
|
|
79
|
+
files = files_to_validate(name)
|
|
80
|
+
return puts Rainbow(t('no_tooling_files')).yellow if files.empty?
|
|
81
|
+
|
|
82
|
+
all_valid = validate_tooling_files(files)
|
|
83
|
+
raise ::Thor::Error, t('validation_failed') unless all_valid
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
desc 'check-versions', 'Check Ruby and gem version consistency across the codebase'
|
|
87
|
+
long_desc <<~LONGDESC
|
|
88
|
+
Checks that Ruby version references are consistent across all configuration files.
|
|
89
|
+
Optionally also checks gem version consistency when --gems flag is used.
|
|
90
|
+
|
|
91
|
+
Flags:
|
|
92
|
+
--verbose: Show all version references, not just mismatches
|
|
93
|
+
--gems: Also check gem version consistency
|
|
94
|
+
--project-dir: Project directory (defaults to current directory)
|
|
95
|
+
|
|
96
|
+
Exit codes:
|
|
97
|
+
0 - All versions consistent
|
|
98
|
+
1 - Version mismatches detected
|
|
99
|
+
|
|
100
|
+
EXAMPLES
|
|
101
|
+
|
|
102
|
+
raictl tooling check-versions
|
|
103
|
+
raictl tooling check-versions --verbose --gems
|
|
104
|
+
|
|
105
|
+
Related commands: validate-ci-yaml, validate
|
|
106
|
+
LONGDESC
|
|
107
|
+
method_option :verbose,
|
|
108
|
+
type: :boolean,
|
|
109
|
+
default: false,
|
|
110
|
+
desc: 'Show all version references, not just mismatches'
|
|
111
|
+
method_option :gems,
|
|
112
|
+
type: :boolean,
|
|
113
|
+
default: false,
|
|
114
|
+
desc: 'Also check gem version consistency'
|
|
115
|
+
method_option :project_dir,
|
|
116
|
+
type: :string,
|
|
117
|
+
default: nil,
|
|
118
|
+
desc: 'Project directory (defaults to current directory)'
|
|
119
|
+
|
|
120
|
+
def check_versions
|
|
121
|
+
project_dir = options[:project_dir] || ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd)
|
|
122
|
+
all_consistent = check_ruby_versions(project_dir)
|
|
123
|
+
|
|
124
|
+
if options[:gems]
|
|
125
|
+
gem_consistent = check_gem_versions(project_dir)
|
|
126
|
+
all_consistent &&= gem_consistent
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
raise ::Thor::Error, t('version_check_failed') unless all_consistent
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def t(key, **)
|
|
135
|
+
::I18n.t("rosett_ai.cli.tooling.#{key}", **)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def tooling_path
|
|
139
|
+
RosettAi.conf_root.join('conf', 'tooling')
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def tooling_files
|
|
143
|
+
Dir.glob(tooling_path.join('*.yml'))
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def find_tooling_file(name)
|
|
147
|
+
path = tooling_path.join("#{name}.yml")
|
|
148
|
+
path.exist? ? path.to_s : nil
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def files_to_validate(name)
|
|
152
|
+
return tooling_files unless name
|
|
153
|
+
|
|
154
|
+
file = find_tooling_file(name)
|
|
155
|
+
unless file
|
|
156
|
+
puts Rainbow(t('not_found', name: name)).red
|
|
157
|
+
return []
|
|
158
|
+
end
|
|
159
|
+
[file]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def validate_tooling_files(files)
|
|
163
|
+
validator = RosettAi::Validators::ToolingValidator.new
|
|
164
|
+
files.all? { |file| tooling_file_valid?(validator, file) }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def tooling_file_valid?(validator, file)
|
|
168
|
+
basename = File.basename(file)
|
|
169
|
+
if validator.valid?(file)
|
|
170
|
+
puts "#{Rainbow(t('valid')).green} #{basename}"
|
|
171
|
+
true
|
|
172
|
+
else
|
|
173
|
+
display_tooling_errors(basename, validator.errors)
|
|
174
|
+
false
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def display_tooling_errors(basename, errors)
|
|
179
|
+
puts "#{Rainbow(t('invalid')).red} #{basename}"
|
|
180
|
+
errors.each { |err| puts " #{err}" }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def print_ci_results(results)
|
|
184
|
+
if options[:verbose]
|
|
185
|
+
results[:valid].each do |entry|
|
|
186
|
+
puts " #{Rainbow(t('ok')).green} #{entry[:file]}"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
results[:invalid].each do |entry|
|
|
191
|
+
puts " #{Rainbow(t('fail')).red} #{entry[:file]}"
|
|
192
|
+
puts " line #{entry[:line]}, column #{entry[:column]}: #{entry[:message]}"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
puts
|
|
196
|
+
print_ci_summary(results)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def print_ci_summary(results)
|
|
200
|
+
rows = [
|
|
201
|
+
[t('ci_valid'), results[:valid].size],
|
|
202
|
+
[t('ci_invalid'), results[:invalid].size]
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
table = ::Terminal::Table.new(
|
|
206
|
+
title: Rainbow(t('ci_title')).bright,
|
|
207
|
+
headings: ['Status', 'Count'],
|
|
208
|
+
rows: rows,
|
|
209
|
+
style: { border: :unicode_round }
|
|
210
|
+
)
|
|
211
|
+
puts table
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def check_ruby_versions(project_dir)
|
|
215
|
+
checker = RosettAi::VersionConsistencyChecker.new(project_dir: project_dir)
|
|
216
|
+
results = checker.check
|
|
217
|
+
print_version_results(results)
|
|
218
|
+
results[:consistent]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def check_gem_versions(project_dir)
|
|
222
|
+
checker = RosettAi::GemConsistencyChecker.new(project_dir: project_dir)
|
|
223
|
+
results = checker.check
|
|
224
|
+
print_gem_results(results)
|
|
225
|
+
results[:consistent]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def print_gem_results(results)
|
|
229
|
+
if options[:verbose]
|
|
230
|
+
results[:gems].each do |entry|
|
|
231
|
+
status = entry[:status] == :ok ? Rainbow(t('ok')).green : Rainbow(t(entry[:status].to_s)).red
|
|
232
|
+
resolved = entry[:resolved] || '-'
|
|
233
|
+
puts " #{status} #{entry[:name]} #{entry[:constraint]} #{resolved}"
|
|
234
|
+
end
|
|
235
|
+
puts
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
results[:stale_references].each do |ref|
|
|
239
|
+
puts " #{Rainbow(t('gem_stale_reference')).red} #{ref[:file]}:#{ref[:line]} " \
|
|
240
|
+
"#{ref[:gem]} found #{ref[:found]}, expected #{ref[:expected]}"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
results[:gems].reject { |entry| entry[:status] == :ok }.each do |entry|
|
|
244
|
+
label = t("gem_status_#{entry[:status]}")
|
|
245
|
+
puts " #{Rainbow(label).red} #{entry[:name]} #{entry[:constraint]} #{entry[:resolved] || '-'}"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
puts
|
|
249
|
+
print_gem_summary(results)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def print_gem_summary(results)
|
|
253
|
+
ok_count = results[:gems].count { |entry| entry[:status] == :ok }
|
|
254
|
+
problem_count = results[:gems].size - ok_count
|
|
255
|
+
|
|
256
|
+
rows = [
|
|
257
|
+
[t('gem_total'), results[:gems].size],
|
|
258
|
+
[t('gem_ok'), ok_count],
|
|
259
|
+
[t('gem_problems'), problem_count],
|
|
260
|
+
[t('gem_stale_refs'), results[:stale_references].size]
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
table = ::Terminal::Table.new(
|
|
264
|
+
title: Rainbow(t('gem_title')).bright,
|
|
265
|
+
headings: ['Check', 'Result'],
|
|
266
|
+
rows: rows,
|
|
267
|
+
style: { border: :unicode_round }
|
|
268
|
+
)
|
|
269
|
+
puts table
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def print_version_results(results)
|
|
273
|
+
if options[:verbose]
|
|
274
|
+
results[:references].each do |ref|
|
|
275
|
+
status = ref[:found] == results[:expected_version] ? Rainbow(t('ok')).green : Rainbow(t('mismatch')).red
|
|
276
|
+
puts " #{status} #{ref[:file]}:#{ref[:line]} #{ref[:found]}"
|
|
277
|
+
end
|
|
278
|
+
puts
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
results[:mismatches].each do |ref|
|
|
282
|
+
puts " #{Rainbow(t('mismatch')).red} #{ref[:file]}:#{ref[:line]} " \
|
|
283
|
+
"found #{ref[:found]}, expected #{results[:expected_version]}"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
puts
|
|
287
|
+
print_version_summary(results)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def print_version_summary(results)
|
|
291
|
+
rows = [
|
|
292
|
+
[t('expected_version'), results[:expected_version]],
|
|
293
|
+
[t('total_references'), results[:references].size],
|
|
294
|
+
[t('mismatches'), results[:mismatches].size]
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
table = ::Terminal::Table.new(
|
|
298
|
+
title: Rainbow(t('version_title')).bright,
|
|
299
|
+
headings: ['Check', 'Result'],
|
|
300
|
+
rows: rows,
|
|
301
|
+
style: { border: :unicode_round }
|
|
302
|
+
)
|
|
303
|
+
puts table
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'thor'
|
|
7
|
+
require 'rainbow'
|
|
8
|
+
require 'terminal-table'
|
|
9
|
+
|
|
10
|
+
module RosettAi
|
|
11
|
+
module Thor
|
|
12
|
+
module Tasks
|
|
13
|
+
# Unified validation command — runs all schema validators sequentially
|
|
14
|
+
# and reports aggregated results.
|
|
15
|
+
class Validate < ::Thor
|
|
16
|
+
default_task :all
|
|
17
|
+
|
|
18
|
+
CATEGORIES = [
|
|
19
|
+
{ name: 'behaviour', dir: 'conf/behaviour', validator: 'BehaviourValidator' },
|
|
20
|
+
{ name: 'design', dir: 'conf/design', validator: 'DesignValidator' },
|
|
21
|
+
{ name: 'tooling', dir: 'conf/tooling', validator: 'ToolingValidator' }
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
desc 'all', 'Validate all configuration files across all categories'
|
|
25
|
+
long_desc <<~LONGDESC
|
|
26
|
+
Validates all configuration files across behaviour, design, and tooling categories.
|
|
27
|
+
Runs schema validation for each category and reports aggregated results in a summary table.
|
|
28
|
+
|
|
29
|
+
Exit codes:
|
|
30
|
+
0 - All files valid
|
|
31
|
+
1 - Validation failures detected
|
|
32
|
+
|
|
33
|
+
EXAMPLES
|
|
34
|
+
|
|
35
|
+
raictl validate
|
|
36
|
+
|
|
37
|
+
Related commands: behaviour validate, design validate, tooling validate
|
|
38
|
+
LONGDESC
|
|
39
|
+
def all
|
|
40
|
+
results = CATEGORIES.map { |cat| validate_category(cat) }
|
|
41
|
+
print_summary(results)
|
|
42
|
+
|
|
43
|
+
failed = results.any? { |r| r[:failed].positive? }
|
|
44
|
+
raise ::Thor::Error, t('validation_failed') if failed
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def t(key, **)
|
|
50
|
+
::I18n.t("rosett_ai.cli.validate.#{key}", **)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_category(category)
|
|
54
|
+
dir = RosettAi.conf_root.join(category[:dir])
|
|
55
|
+
files = Dir.glob(dir.join('*.yml'))
|
|
56
|
+
|
|
57
|
+
result = { category: category[:name], total: files.size, passed: 0, failed: 0, errors: [] }
|
|
58
|
+
return result if files.empty?
|
|
59
|
+
|
|
60
|
+
validator = RosettAi::Validators.const_get(category[:validator]).new
|
|
61
|
+
files.each { |file| validate_file(validator, file, result) }
|
|
62
|
+
result
|
|
63
|
+
rescue RosettAi::ConfigurationError => e
|
|
64
|
+
puts " #{Rainbow(t('invalid')).red} #{category[:name]}: #{e.message}"
|
|
65
|
+
{ category: category[:name], total: 0, passed: 0, failed: 1, errors: [e.message] }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def validate_file(validator, file, result)
|
|
69
|
+
basename = File.basename(file)
|
|
70
|
+
if validator.valid?(file)
|
|
71
|
+
result[:passed] += 1
|
|
72
|
+
puts " #{Rainbow(t('valid')).green} #{result[:category]}/#{basename}"
|
|
73
|
+
else
|
|
74
|
+
result[:failed] += 1
|
|
75
|
+
result[:errors] << { file: basename, messages: validator.errors.dup }
|
|
76
|
+
puts " #{Rainbow(t('invalid')).red} #{result[:category]}/#{basename}"
|
|
77
|
+
validator.errors.each { |err| puts " #{err}" }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def print_summary(results)
|
|
82
|
+
puts
|
|
83
|
+
|
|
84
|
+
rows = results.map do |r|
|
|
85
|
+
status = r[:failed].positive? ? Rainbow(t('fail')).red : Rainbow(t('ok')).green
|
|
86
|
+
[r[:category], r[:total], r[:passed], r[:failed], status]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
total_files = results.sum { |r| r[:total] }
|
|
90
|
+
total_passed = results.sum { |r| r[:passed] }
|
|
91
|
+
total_failed = results.sum { |r| r[:failed] }
|
|
92
|
+
total_status = total_failed.positive? ? Rainbow(t('fail')).red : Rainbow(t('ok')).green
|
|
93
|
+
|
|
94
|
+
rows << :separator
|
|
95
|
+
rows << [Rainbow(t('total')).bright, total_files, total_passed, total_failed, total_status]
|
|
96
|
+
|
|
97
|
+
table = ::Terminal::Table.new(
|
|
98
|
+
title: Rainbow(t('summary_title')).bright,
|
|
99
|
+
headings: [t('category'), t('files'), t('passed'), t('failed_label'), t('status')],
|
|
100
|
+
rows: rows,
|
|
101
|
+
style: { border: :unicode_round }
|
|
102
|
+
)
|
|
103
|
+
puts table
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'terminal-table'
|
|
7
|
+
require 'rainbow'
|
|
8
|
+
require 'json'
|
|
9
|
+
|
|
10
|
+
module RosettAi
|
|
11
|
+
module Thor
|
|
12
|
+
module Tasks
|
|
13
|
+
# CLI task for `rai workflow` — declarative workflow execution.
|
|
14
|
+
#
|
|
15
|
+
# Provides commands to list, validate, simulate, and run workflows.
|
|
16
|
+
# Workflows are YAML files defining multi-step automation sequences.
|
|
17
|
+
#
|
|
18
|
+
# @author hugo
|
|
19
|
+
# @author claude
|
|
20
|
+
class Workflow < ::Thor
|
|
21
|
+
desc 'list', 'List available workflow files'
|
|
22
|
+
long_desc <<~LONGDESC
|
|
23
|
+
Lists all workflow YAML files found in conf/workflows/ and
|
|
24
|
+
the project .rosett-ai/workflows/ directory.
|
|
25
|
+
|
|
26
|
+
EXAMPLES
|
|
27
|
+
|
|
28
|
+
raictl workflow list
|
|
29
|
+
LONGDESC
|
|
30
|
+
def list
|
|
31
|
+
files = discover_workflows
|
|
32
|
+
if files.empty?
|
|
33
|
+
warn ::I18n.t('rosett_ai.workflow.no_files')
|
|
34
|
+
return
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
rows = files.filter_map do |path|
|
|
38
|
+
data = load_workflow_safe(path)
|
|
39
|
+
next unless data
|
|
40
|
+
|
|
41
|
+
[data['name'] || File.basename(path, '.yml'), data['version'] || '-',
|
|
42
|
+
data.fetch('steps', []).size, path.to_s]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
table = ::Terminal::Table.new(
|
|
46
|
+
title: ::I18n.t('rosett_ai.workflow.list_title'),
|
|
47
|
+
headings: [::I18n.t('rosett_ai.workflow.name'), ::I18n.t('rosett_ai.workflow.version'),
|
|
48
|
+
::I18n.t('rosett_ai.workflow.steps_count'), ::I18n.t('rosett_ai.workflow.path')],
|
|
49
|
+
rows: rows
|
|
50
|
+
)
|
|
51
|
+
puts table
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
desc 'validate NAME', 'Validate a workflow definition'
|
|
55
|
+
long_desc <<~LONGDESC
|
|
56
|
+
Validates the workflow YAML file against the workflow schema
|
|
57
|
+
and runs security checks (array-form commands, max step count).
|
|
58
|
+
|
|
59
|
+
EXAMPLES
|
|
60
|
+
|
|
61
|
+
raictl workflow validate release
|
|
62
|
+
LONGDESC
|
|
63
|
+
def validate(name)
|
|
64
|
+
path = resolve_workflow(name)
|
|
65
|
+
engine = RosettAi::Workflow::Engine.new(workflow_path: path)
|
|
66
|
+
errors = engine.validate
|
|
67
|
+
|
|
68
|
+
if errors.empty?
|
|
69
|
+
puts Rainbow(::I18n.t('rosett_ai.workflow.valid', name: name)).green
|
|
70
|
+
else
|
|
71
|
+
warn Rainbow(::I18n.t('rosett_ai.workflow.invalid', name: name)).red
|
|
72
|
+
errors.each { |err| warn " - #{err}" }
|
|
73
|
+
exit 2
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
desc 'simulate NAME', 'Dry-run a workflow (show steps without executing)'
|
|
78
|
+
long_desc <<~LONGDESC
|
|
79
|
+
Shows what each step would do without actually executing.
|
|
80
|
+
Useful for reviewing a workflow before running it.
|
|
81
|
+
|
|
82
|
+
EXAMPLES
|
|
83
|
+
|
|
84
|
+
raictl workflow simulate release
|
|
85
|
+
LONGDESC
|
|
86
|
+
def simulate(name)
|
|
87
|
+
path = resolve_workflow(name)
|
|
88
|
+
engine = RosettAi::Workflow::Engine.new(workflow_path: path)
|
|
89
|
+
descriptions = engine.simulate
|
|
90
|
+
|
|
91
|
+
puts ::I18n.t('rosett_ai.workflow.simulate_header', name: name)
|
|
92
|
+
descriptions.each_with_index do |desc, idx|
|
|
93
|
+
puts " #{idx + 1}. #{desc}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
desc 'execute NAME', 'Execute a workflow'
|
|
98
|
+
long_desc <<~LONGDESC
|
|
99
|
+
Executes all steps in the named workflow sequentially.
|
|
100
|
+
|
|
101
|
+
Use --resume to skip steps already completed in a previous run
|
|
102
|
+
(detected via the audit log). Use --format json for CI output.
|
|
103
|
+
|
|
104
|
+
Exit codes: 0 = all pass, 1 = any failure.
|
|
105
|
+
|
|
106
|
+
EXAMPLES
|
|
107
|
+
|
|
108
|
+
raictl workflow execute release
|
|
109
|
+
raictl workflow execute deploy --resume
|
|
110
|
+
raictl workflow execute ci-check --format json
|
|
111
|
+
LONGDESC
|
|
112
|
+
method_option :resume, type: :boolean, default: false,
|
|
113
|
+
desc: 'Resume from last completed step'
|
|
114
|
+
method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
|
|
115
|
+
desc: 'Output format'
|
|
116
|
+
def execute(name)
|
|
117
|
+
path = resolve_workflow(name)
|
|
118
|
+
audit_path = resolve_audit_log_path(name)
|
|
119
|
+
engine = RosettAi::Workflow::Engine.new(workflow_path: path, audit_log_path: audit_path)
|
|
120
|
+
summary = engine.execute(resume: options[:resume])
|
|
121
|
+
|
|
122
|
+
if options[:format] == 'json'
|
|
123
|
+
puts JSON.pretty_generate(summary)
|
|
124
|
+
else
|
|
125
|
+
render_table(summary)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
exit 1 if summary[:status] == 'fail'
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def discover_workflows
|
|
134
|
+
paths = []
|
|
135
|
+
conf_dir = RosettAi.root.join('conf', 'workflows')
|
|
136
|
+
paths += Dir.glob(conf_dir.join('*.yml').to_s).map { |path| Pathname.new(path) } if conf_dir.directory?
|
|
137
|
+
|
|
138
|
+
project_dir = Pathname.new(ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd)).join('.rosett-ai', 'workflows')
|
|
139
|
+
paths += Dir.glob(project_dir.join('*.yml').to_s).map { |path| Pathname.new(path) } if project_dir.directory?
|
|
140
|
+
|
|
141
|
+
paths
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def resolve_workflow(name)
|
|
145
|
+
candidates = discover_workflows
|
|
146
|
+
match = candidates.find { |path| File.basename(path, '.yml') == name }
|
|
147
|
+
raise RosettAi::WorkflowError, ::I18n.t('rosett_ai.workflow.not_found', name: name) unless match
|
|
148
|
+
|
|
149
|
+
match
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def resolve_audit_log_path(name)
|
|
153
|
+
RosettAi.root.join('tmp', 'workflow_audit', "#{name}.jsonl")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def load_workflow_safe(path)
|
|
157
|
+
YAML.safe_load(path.read, permitted_classes: [Date])
|
|
158
|
+
rescue StandardError
|
|
159
|
+
nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def render_table(summary)
|
|
163
|
+
rows = summary[:results].map do |result|
|
|
164
|
+
status_text = colorize_status(result[:status])
|
|
165
|
+
duration = result[:duration_ms] ? "#{result[:duration_ms]}ms" : '-'
|
|
166
|
+
[result[:step], status_text, result[:message], duration]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
table = ::Terminal::Table.new(
|
|
170
|
+
title: ::I18n.t('rosett_ai.workflow.run_title', name: summary[:workflow]),
|
|
171
|
+
headings: [::I18n.t('rosett_ai.workflow.step_label'), ::I18n.t('rosett_ai.workflow.status_label'),
|
|
172
|
+
::I18n.t('rosett_ai.workflow.message_label'), ::I18n.t('rosett_ai.workflow.duration_label')],
|
|
173
|
+
rows: rows
|
|
174
|
+
)
|
|
175
|
+
puts table
|
|
176
|
+
puts summary_line(summary)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def colorize_status(status)
|
|
180
|
+
case status
|
|
181
|
+
when 'pass' then Rainbow(status).green
|
|
182
|
+
when 'fail' then Rainbow(status).red
|
|
183
|
+
when 'skip' then Rainbow(status).yellow
|
|
184
|
+
else status
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def summary_line(summary)
|
|
189
|
+
::I18n.t('rosett_ai.workflow.run_summary',
|
|
190
|
+
total: summary[:total], pass: summary[:pass],
|
|
191
|
+
fail: summary[:fail], skip: summary[:skip])
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Tooling
|
|
8
|
+
# MCP-facing facade for CI YAML validation.
|
|
9
|
+
#
|
|
10
|
+
# Delegates to the GitLab CI YAML validators, providing the
|
|
11
|
+
# simplified interface expected by {Mcp::Tools::ToolingTool}.
|
|
12
|
+
#
|
|
13
|
+
# @author hugo
|
|
14
|
+
# @author claude
|
|
15
|
+
class CiYamlValidator
|
|
16
|
+
def initialize(project_dir: Dir.pwd)
|
|
17
|
+
@project_dir = project_dir
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Validates all CI YAML files in the project.
|
|
21
|
+
#
|
|
22
|
+
# @return [Hash] with :errors and :files keys
|
|
23
|
+
def validate
|
|
24
|
+
validator = RosettAi::Gitlab::Validators::SupplementaryGitlabCiYamlValidator.new(
|
|
25
|
+
project_dir: @project_dir
|
|
26
|
+
)
|
|
27
|
+
result = validator.validate
|
|
28
|
+
valid_files = result.fetch(:valid, []).map { |entry| entry[:file] }
|
|
29
|
+
invalid_entries = result.fetch(:invalid, [])
|
|
30
|
+
{
|
|
31
|
+
errors: invalid_entries.map { |entry| entry[:message] },
|
|
32
|
+
files: valid_files + invalid_entries.map { |entry| entry[:file] }
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Tooling
|
|
8
|
+
# MCP-facing facade for tool version checking.
|
|
9
|
+
#
|
|
10
|
+
# Delegates to {VersionConsistencyChecker} and {GemConsistencyChecker}
|
|
11
|
+
# for the actual version checks, providing the simplified interface
|
|
12
|
+
# expected by {Mcp::Tools::ToolingTool}.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class VersionChecker
|
|
17
|
+
def initialize(project_dir: Dir.pwd)
|
|
18
|
+
@project_dir = project_dir
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Checks tool and gem versions for consistency.
|
|
22
|
+
#
|
|
23
|
+
# @return [Hash] with :outdated and :tools keys
|
|
24
|
+
def check
|
|
25
|
+
checker = RosettAi::VersionConsistencyChecker.new(project_dir: @project_dir)
|
|
26
|
+
result = checker.check
|
|
27
|
+
outdated = result[:mismatches] || []
|
|
28
|
+
{
|
|
29
|
+
outdated: outdated,
|
|
30
|
+
tools: result[:references] || []
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|