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,81 @@
|
|
|
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 Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: retrofit native configurations.
|
|
10
|
+
#
|
|
11
|
+
# Scan and convert native AI tool configs to Rosett-AI YAML.
|
|
12
|
+
# Write operation for convert action.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class RetrofitTool
|
|
17
|
+
TOOL_NAME = 'rai_retrofit'
|
|
18
|
+
DESCRIPTION = 'Scan and convert native AI configs to rosett-ai format'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => false,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
VALID_ACTIONS = ['engines', 'scan', 'convert'].freeze
|
|
28
|
+
|
|
29
|
+
INPUT_SCHEMA = {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
action: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
enum: ['engines', 'scan', 'convert'],
|
|
35
|
+
description: 'Retrofit action (default: scan)'
|
|
36
|
+
},
|
|
37
|
+
engine: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: 'Engine to retrofit from'
|
|
40
|
+
},
|
|
41
|
+
simulate: {
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
description: 'Dry-run mode (default: true)'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
# Executes the retrofit operation.
|
|
49
|
+
#
|
|
50
|
+
# @param action [String] one of 'engines', 'scan', 'convert'
|
|
51
|
+
# @param engine [String, nil] specific engine for conversion
|
|
52
|
+
# @param simulate [Boolean] dry-run mode for convert
|
|
53
|
+
# @return [Hash] retrofit results
|
|
54
|
+
def call(action: 'scan', engine: nil, simulate: true)
|
|
55
|
+
return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
|
|
56
|
+
|
|
57
|
+
retrofitter = RosettAi::Retrofit::Scanner.new
|
|
58
|
+
case action
|
|
59
|
+
when 'engines' then { parsers: retrofitter.available_parsers }
|
|
60
|
+
when 'scan' then { findings: retrofitter.scan }
|
|
61
|
+
when 'convert' then action_convert(retrofitter, engine, simulate)
|
|
62
|
+
end
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
ResponseHelper.error("Retrofit #{action} failed: #{e.message}")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def action_convert(retrofitter, engine, simulate)
|
|
70
|
+
result = retrofitter.convert(engine: engine, options: { dry_run: simulate })
|
|
71
|
+
{
|
|
72
|
+
simulate: simulate,
|
|
73
|
+
engine: engine,
|
|
74
|
+
converted: result[:converted],
|
|
75
|
+
files: result[:files]
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
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 'yaml'
|
|
7
|
+
require 'pathname'
|
|
8
|
+
|
|
9
|
+
module RosettAi
|
|
10
|
+
module Mcp
|
|
11
|
+
module Tools
|
|
12
|
+
# MCP tool: search compiled rules by keyword.
|
|
13
|
+
#
|
|
14
|
+
# Searches behaviour YAML source files for rules matching a keyword,
|
|
15
|
+
# with optional filters for minimum priority, scope, and enabled status.
|
|
16
|
+
# Read-only operation.
|
|
17
|
+
#
|
|
18
|
+
# @author hugo
|
|
19
|
+
# @author claude
|
|
20
|
+
class RuleSearchTool
|
|
21
|
+
TOOL_NAME = 'rai_rule_search'
|
|
22
|
+
DESCRIPTION = 'Search rules by keyword with optional priority/scope/enabled filters'
|
|
23
|
+
|
|
24
|
+
ANNOTATIONS = {
|
|
25
|
+
'readOnlyHint' => true,
|
|
26
|
+
'destructiveHint' => false,
|
|
27
|
+
'idempotentHint' => true,
|
|
28
|
+
'openWorldHint' => false
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
INPUT_SCHEMA = {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
keyword: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'Search keyword to match against rule descriptions and IDs'
|
|
37
|
+
},
|
|
38
|
+
priority_min: {
|
|
39
|
+
type: 'integer',
|
|
40
|
+
minimum: 0,
|
|
41
|
+
maximum: 100,
|
|
42
|
+
description: 'Minimum priority threshold (0-100)'
|
|
43
|
+
},
|
|
44
|
+
scope: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Behaviour scope to search within'
|
|
47
|
+
},
|
|
48
|
+
enabled_only: {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
description: 'Only return enabled rules (default: true)'
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
required: ['keyword']
|
|
54
|
+
}.freeze
|
|
55
|
+
|
|
56
|
+
# Searches rules across all behaviour files.
|
|
57
|
+
#
|
|
58
|
+
# @param keyword [String] search term (matched case-insensitively)
|
|
59
|
+
# @param priority_min [Integer, nil] minimum priority filter
|
|
60
|
+
# @param scope [String, nil] scope filter ('global', 'project', 'personal')
|
|
61
|
+
# @param enabled_only [Boolean] filter to enabled rules only (default true)
|
|
62
|
+
# @return [Hash] matching rules with metadata
|
|
63
|
+
def call(keyword:, priority_min: nil, scope: nil, enabled_only: true)
|
|
64
|
+
matches = []
|
|
65
|
+
each_behaviour_file do |path, data|
|
|
66
|
+
search_behaviour(data, keyword, path, matches)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
matches = apply_filters(matches, priority_min, scope, enabled_only)
|
|
70
|
+
matches.sort_by! { |m| -(m[:priority] || 0) }
|
|
71
|
+
|
|
72
|
+
{ keyword: keyword, matches: matches, total: matches.size }
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
ResponseHelper.error("Rule search failed: #{e.message}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# @yield [Pathname, Hash] path and parsed YAML data
|
|
80
|
+
def each_behaviour_file
|
|
81
|
+
behaviour_dirs.each do |dir|
|
|
82
|
+
next unless dir.directory?
|
|
83
|
+
|
|
84
|
+
dir.glob('*.yml').each do |path|
|
|
85
|
+
data = YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
86
|
+
yield path, data if data.is_a?(Hash)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @return [Array<Pathname>] directories to search for behaviours
|
|
92
|
+
def behaviour_dirs
|
|
93
|
+
dirs = [RosettAi.root.join('conf', 'behaviour')]
|
|
94
|
+
xdg = RosettAi.paths.rai_conf_dir.join('behaviour')
|
|
95
|
+
dirs << xdg if xdg.directory?
|
|
96
|
+
dirs
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @param data [Hash] parsed behaviour YAML
|
|
100
|
+
# @param keyword [String] search term
|
|
101
|
+
# @param path [Pathname] source file path
|
|
102
|
+
# @param matches [Array<Hash>] accumulator
|
|
103
|
+
def search_behaviour(data, keyword, path, matches)
|
|
104
|
+
rules = data['rules']
|
|
105
|
+
return unless rules.is_a?(Array)
|
|
106
|
+
|
|
107
|
+
pattern = Regexp.new(Regexp.escape(keyword), Regexp::IGNORECASE)
|
|
108
|
+
behaviour_name = data['name'] || File.basename(path, '.yml')
|
|
109
|
+
|
|
110
|
+
rules.each do |rule|
|
|
111
|
+
next unless rule.is_a?(Hash)
|
|
112
|
+
|
|
113
|
+
description = rule['description'].to_s
|
|
114
|
+
next unless description.match?(pattern)
|
|
115
|
+
|
|
116
|
+
matches << build_match(behaviour_name, rule, description, data, pattern)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @param behaviour_name [String]
|
|
121
|
+
# @param rule [Hash]
|
|
122
|
+
# @param description [String]
|
|
123
|
+
# @param data [Hash]
|
|
124
|
+
# @param pattern [Regexp]
|
|
125
|
+
# @return [Hash]
|
|
126
|
+
def build_match(behaviour_name, rule, description, data, pattern)
|
|
127
|
+
snippet = extract_snippet(description, pattern)
|
|
128
|
+
{
|
|
129
|
+
behaviour: behaviour_name,
|
|
130
|
+
rule_id: rule['id'],
|
|
131
|
+
priority: rule['priority'] || 0,
|
|
132
|
+
enabled: rule.fetch('enabled', true),
|
|
133
|
+
scope: data['scope'],
|
|
134
|
+
snippet: snippet
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# @param text [String]
|
|
139
|
+
# @param pattern [Regexp]
|
|
140
|
+
# @return [String] snippet around the first match (max 120 chars)
|
|
141
|
+
def extract_snippet(text, pattern)
|
|
142
|
+
match = pattern.match(text)
|
|
143
|
+
return text[0, 120] unless match
|
|
144
|
+
|
|
145
|
+
start = [match.begin(0) - 40, 0].max
|
|
146
|
+
text[start, 120].strip
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @param matches [Array<Hash>]
|
|
150
|
+
# @param priority_min [Integer, nil]
|
|
151
|
+
# @param scope [String, nil]
|
|
152
|
+
# @param enabled_only [Boolean]
|
|
153
|
+
# @return [Array<Hash>]
|
|
154
|
+
def apply_filters(matches, priority_min, scope, enabled_only)
|
|
155
|
+
matches = matches.select { |m| m[:priority] >= priority_min } if priority_min
|
|
156
|
+
matches = matches.select { |m| m[:scope] == scope } if scope
|
|
157
|
+
matches = matches.select { |m| m[:enabled] } if enabled_only
|
|
158
|
+
matches
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
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 'json'
|
|
7
|
+
require 'pathname'
|
|
8
|
+
|
|
9
|
+
module RosettAi
|
|
10
|
+
module Mcp
|
|
11
|
+
module Tools
|
|
12
|
+
# MCP tool: retrieve JSON Schema content for validation.
|
|
13
|
+
#
|
|
14
|
+
# Returns the raw JSON Schema used for validating behaviour,
|
|
15
|
+
# design, or tooling configuration files. Read-only.
|
|
16
|
+
#
|
|
17
|
+
# @author hugo
|
|
18
|
+
# @author claude
|
|
19
|
+
class SchemaGetTool
|
|
20
|
+
TOOL_NAME = 'rai_schema_get'
|
|
21
|
+
DESCRIPTION = 'Get JSON Schema content for behaviour/design/tooling validation'
|
|
22
|
+
|
|
23
|
+
ANNOTATIONS = {
|
|
24
|
+
'readOnlyHint' => true,
|
|
25
|
+
'destructiveHint' => false,
|
|
26
|
+
'idempotentHint' => true,
|
|
27
|
+
'openWorldHint' => false
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
VALID_SCHEMAS = {
|
|
31
|
+
'behaviour' => 'behaviour_schema.json',
|
|
32
|
+
'design' => 'design_schema.json',
|
|
33
|
+
'tooling' => 'tooling_schema.json',
|
|
34
|
+
'workflow' => 'workflow_schema.json',
|
|
35
|
+
'provenance' => 'provenance_schema.json',
|
|
36
|
+
'policy' => 'policy_schema.json'
|
|
37
|
+
}.freeze
|
|
38
|
+
|
|
39
|
+
INPUT_SCHEMA = {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
schema_name: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
enum: ['behaviour', 'design', 'tooling', 'workflow', 'provenance', 'policy'],
|
|
45
|
+
description: 'Schema name to retrieve'
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
required: ['schema_name']
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
51
|
+
# Retrieves the JSON Schema content.
|
|
52
|
+
#
|
|
53
|
+
# @param schema_name [String] one of: behaviour, design, tooling, workflow, provenance, policy
|
|
54
|
+
# @return [Hash] schema content and metadata
|
|
55
|
+
def call(schema_name:)
|
|
56
|
+
filename = VALID_SCHEMAS[schema_name]
|
|
57
|
+
return invalid_schema_response(schema_name) unless filename
|
|
58
|
+
|
|
59
|
+
schema_path = RosettAi.root.join('conf', 'schemas', filename)
|
|
60
|
+
return missing_schema_response(schema_path) unless schema_path.exist?
|
|
61
|
+
|
|
62
|
+
content = JSON.parse(schema_path.read)
|
|
63
|
+
{
|
|
64
|
+
schema_name: schema_name,
|
|
65
|
+
filename: filename,
|
|
66
|
+
content: content,
|
|
67
|
+
path: schema_path.to_s
|
|
68
|
+
}
|
|
69
|
+
rescue JSON::ParserError => e
|
|
70
|
+
ResponseHelper.error("Schema parse error: #{e.message}")
|
|
71
|
+
rescue StandardError => e
|
|
72
|
+
ResponseHelper.error("Schema get failed: #{e.message}")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
# @param schema_name [String]
|
|
78
|
+
# @return [Hash]
|
|
79
|
+
def invalid_schema_response(schema_name)
|
|
80
|
+
ResponseHelper.error(
|
|
81
|
+
"Unknown schema: #{schema_name}. " \
|
|
82
|
+
"Available: #{VALID_SCHEMAS.keys.join(', ')}"
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @param schema_path [Pathname]
|
|
87
|
+
# @return [Hash]
|
|
88
|
+
def missing_schema_response(schema_path)
|
|
89
|
+
ResponseHelper.error("Schema file not found: #{schema_path}")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
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 Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: run tooling validation.
|
|
10
|
+
#
|
|
11
|
+
# Validates CI YAML syntax and checks tool versions.
|
|
12
|
+
# Read-only operation.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class ToolingTool
|
|
17
|
+
TOOL_NAME = 'rai_tooling'
|
|
18
|
+
DESCRIPTION = 'Run tooling validation (CI YAML, version checks)'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => true,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
VALID_ACTIONS = ['validate-ci-yaml', 'check-versions'].freeze
|
|
28
|
+
|
|
29
|
+
INPUT_SCHEMA = {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
action: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
enum: ['validate-ci-yaml', 'check-versions'],
|
|
35
|
+
description: 'Tooling action (default: validate-ci-yaml)'
|
|
36
|
+
},
|
|
37
|
+
verbose: {
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
description: 'Show detailed output (default: false)'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
# Executes tooling validation.
|
|
45
|
+
#
|
|
46
|
+
# @param action [String] one of 'validate-ci-yaml', 'check-versions'
|
|
47
|
+
# @param verbose [Boolean] show detailed output
|
|
48
|
+
# @return [Hash] validation results
|
|
49
|
+
def call(action: 'validate-ci-yaml', verbose: false)
|
|
50
|
+
return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
|
|
51
|
+
|
|
52
|
+
case action
|
|
53
|
+
when 'validate-ci-yaml' then validate_ci_yaml(verbose)
|
|
54
|
+
when 'check-versions' then check_versions(verbose)
|
|
55
|
+
end
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
ResponseHelper.error("Tooling #{action} failed: #{e.message}")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def validate_ci_yaml(verbose)
|
|
63
|
+
validator = RosettAi::Tooling::CiYamlValidator.new
|
|
64
|
+
results = validator.validate
|
|
65
|
+
{
|
|
66
|
+
valid: results[:errors].empty?,
|
|
67
|
+
files_checked: results[:files],
|
|
68
|
+
errors: results[:errors],
|
|
69
|
+
verbose: verbose
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def check_versions(verbose)
|
|
74
|
+
checker = RosettAi::Tooling::VersionChecker.new
|
|
75
|
+
results = checker.check
|
|
76
|
+
{
|
|
77
|
+
up_to_date: results[:outdated].empty?,
|
|
78
|
+
tools: results[:tools],
|
|
79
|
+
outdated: results[:outdated],
|
|
80
|
+
verbose: verbose
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
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 'yaml'
|
|
7
|
+
|
|
8
|
+
module RosettAi
|
|
9
|
+
module Mcp
|
|
10
|
+
module Tools
|
|
11
|
+
# MCP tool: validate rai configuration files.
|
|
12
|
+
#
|
|
13
|
+
# Performs both YAML syntax and JSON Schema validation using the
|
|
14
|
+
# project's {RosettAi::Validators::SchemaValidator} hierarchy.
|
|
15
|
+
# Read-only operation.
|
|
16
|
+
#
|
|
17
|
+
# @author hugo
|
|
18
|
+
# @author claude
|
|
19
|
+
class ValidateTool
|
|
20
|
+
TOOL_NAME = 'rai_validate'
|
|
21
|
+
DESCRIPTION = 'Validate rai configuration files (behaviours, designs, tooling)'
|
|
22
|
+
|
|
23
|
+
ANNOTATIONS = {
|
|
24
|
+
'readOnlyHint' => true,
|
|
25
|
+
'destructiveHint' => false,
|
|
26
|
+
'idempotentHint' => true,
|
|
27
|
+
'openWorldHint' => false
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
INPUT_SCHEMA = {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
scope: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
enum: ['behaviour', 'design', 'tooling'],
|
|
36
|
+
description: 'Validation scope (default: all scopes)'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
# Maps scope names to their validator classes.
|
|
42
|
+
SCOPE_VALIDATORS = {
|
|
43
|
+
'behaviour' => -> { RosettAi::Validators::BehaviourValidator.new },
|
|
44
|
+
'design' => -> { RosettAi::Validators::DesignValidator.new },
|
|
45
|
+
'tooling' => -> { RosettAi::Validators::ToolingValidator.new }
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
# Executes the validation with JSON Schema checking.
|
|
49
|
+
#
|
|
50
|
+
# @param scope [String, nil] optional scope filter ('behaviour', 'design', 'tooling')
|
|
51
|
+
# @return [Hash] result with :valid, :errors, :warnings
|
|
52
|
+
def call(scope: nil)
|
|
53
|
+
results = { valid: true, errors: [], warnings: [] }
|
|
54
|
+
scopes = scope ? [scope] : ['behaviour', 'design']
|
|
55
|
+
|
|
56
|
+
scopes.each { |s| validate_scope(s, results) }
|
|
57
|
+
results[:valid] = results[:errors].empty?
|
|
58
|
+
results
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# @param scope [String]
|
|
64
|
+
# @param results [Hash]
|
|
65
|
+
# @return [void]
|
|
66
|
+
def validate_scope(scope, results)
|
|
67
|
+
dir = RosettAi.root.join('conf', scope)
|
|
68
|
+
return unless dir.directory?
|
|
69
|
+
|
|
70
|
+
validator = build_validator(scope)
|
|
71
|
+
dir.glob('*.yml').each do |path|
|
|
72
|
+
validate_file(path, scope, validator, results)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @param scope [String]
|
|
77
|
+
# @return [RosettAi::Validators::SchemaValidator, nil]
|
|
78
|
+
def build_validator(scope)
|
|
79
|
+
factory = SCOPE_VALIDATORS[scope]
|
|
80
|
+
factory&.call
|
|
81
|
+
rescue RosettAi::ConfigurationError
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param path [Pathname]
|
|
86
|
+
# @param scope [String]
|
|
87
|
+
# @param validator [RosettAi::Validators::SchemaValidator, nil]
|
|
88
|
+
# @param results [Hash]
|
|
89
|
+
# @return [void]
|
|
90
|
+
def validate_file(path, scope, validator, results)
|
|
91
|
+
file_str = path.to_s
|
|
92
|
+
YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
93
|
+
|
|
94
|
+
return unless validator
|
|
95
|
+
|
|
96
|
+
validator.validate(file_str).each do |error|
|
|
97
|
+
results[:errors] << { file: file_str, scope: scope, message: error }
|
|
98
|
+
end
|
|
99
|
+
rescue Psych::SyntaxError => e
|
|
100
|
+
results[:errors] << { file: file_str, scope: scope, message: e.message }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
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 Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: execute a named workflow.
|
|
10
|
+
#
|
|
11
|
+
# Runs a declarative workflow by name.
|
|
12
|
+
# Write operation — executes workflow steps.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class WorkflowExecuteTool
|
|
17
|
+
TOOL_NAME = 'rai_workflow_execute'
|
|
18
|
+
DESCRIPTION = 'Execute a named rai workflow'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => false,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => false,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
INPUT_SCHEMA = {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
name: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Workflow name to execute'
|
|
33
|
+
},
|
|
34
|
+
resume: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
description: 'Resume a previously interrupted workflow (default: false)'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
required: ['name']
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
# Executes the named workflow.
|
|
43
|
+
#
|
|
44
|
+
# @param name [String] workflow name
|
|
45
|
+
# @param resume [Boolean] resume a previously interrupted workflow
|
|
46
|
+
# @return [Hash] execution results
|
|
47
|
+
def call(name:, resume: false)
|
|
48
|
+
return ResponseHelper.error('Workflow name is required') if name.nil? || name.empty?
|
|
49
|
+
|
|
50
|
+
path = resolve_workflow(name)
|
|
51
|
+
return ResponseHelper.error("Workflow '#{name}' not found") unless path
|
|
52
|
+
|
|
53
|
+
engine = RosettAi::Workflow::Engine.new(workflow_path: path)
|
|
54
|
+
build_result(name, engine.execute(resume: resume))
|
|
55
|
+
rescue StandardError => e
|
|
56
|
+
ResponseHelper.error("Workflow execute failed: #{e.message}")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def build_result(name, summary)
|
|
62
|
+
{ name: name, status: summary[:status],
|
|
63
|
+
steps_completed: summary[:steps_completed], output: summary[:output] }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def resolve_workflow(name)
|
|
67
|
+
dir = RosettAi.root.join('conf', 'workflows')
|
|
68
|
+
path = dir.join("#{name}.yml")
|
|
69
|
+
path.exist? ? path : nil
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
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 Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: query workflow definitions.
|
|
10
|
+
#
|
|
11
|
+
# Lists, validates, and simulates declarative workflows.
|
|
12
|
+
# Read-only operation.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class WorkflowTool
|
|
17
|
+
TOOL_NAME = 'rai_workflow'
|
|
18
|
+
DESCRIPTION = 'List, validate, or simulate rai workflows'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => true,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
VALID_ACTIONS = ['list', 'validate', 'simulate'].freeze
|
|
28
|
+
|
|
29
|
+
INPUT_SCHEMA = {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
action: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
enum: ['list', 'validate', 'simulate'],
|
|
35
|
+
description: 'Workflow action (default: list)'
|
|
36
|
+
},
|
|
37
|
+
name: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: 'Workflow name (required for validate/simulate)'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
# Executes the workflow query.
|
|
45
|
+
#
|
|
46
|
+
# @param action [String] one of 'list', 'validate', 'simulate'
|
|
47
|
+
# @param name [String, nil] workflow name (for validate/simulate)
|
|
48
|
+
# @return [Hash] workflow information
|
|
49
|
+
def call(action: 'list', name: nil)
|
|
50
|
+
return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
|
|
51
|
+
|
|
52
|
+
manager = RosettAi::Workflow::Manager.new
|
|
53
|
+
case action
|
|
54
|
+
when 'list' then { workflows: manager.list }
|
|
55
|
+
when 'validate' then action_validate(manager, name)
|
|
56
|
+
when 'simulate' then action_simulate(manager, name)
|
|
57
|
+
end
|
|
58
|
+
rescue StandardError => e
|
|
59
|
+
ResponseHelper.error("Workflow #{action} failed: #{e.message}")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def action_validate(manager, name)
|
|
65
|
+
results = name ? manager.validate(name) : manager.validate_all
|
|
66
|
+
{ valid: results[:errors].empty?, errors: results[:errors] }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def action_simulate(manager, name)
|
|
70
|
+
return ResponseHelper.error('Workflow name required for simulate') unless name
|
|
71
|
+
|
|
72
|
+
result = manager.simulate(name)
|
|
73
|
+
{ name: name, steps: result[:steps], estimated_duration: result[:duration] }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|