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,51 @@
|
|
|
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 Resources
|
|
9
|
+
# MCP resource provider for AI provenance log.
|
|
10
|
+
#
|
|
11
|
+
# Exposes the full provenance log as an MCP resource.
|
|
12
|
+
#
|
|
13
|
+
# @author hugo
|
|
14
|
+
# @author claude
|
|
15
|
+
class ProvenanceResource
|
|
16
|
+
URI_PREFIX = 'rosett-ai://provenance/'
|
|
17
|
+
PROVENANCE_FILE = '.ai-provenance.yml'
|
|
18
|
+
|
|
19
|
+
# Lists available provenance resources.
|
|
20
|
+
#
|
|
21
|
+
# @return [Array<Hash>] resource entries
|
|
22
|
+
def list
|
|
23
|
+
path = RosettAi.root.join(PROVENANCE_FILE)
|
|
24
|
+
return [] unless path.exist?
|
|
25
|
+
|
|
26
|
+
[{
|
|
27
|
+
uri: "#{URI_PREFIX}log",
|
|
28
|
+
name: 'provenance-log',
|
|
29
|
+
description: 'Full AI provenance log',
|
|
30
|
+
mime_type: 'application/x-yaml'
|
|
31
|
+
}]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Reads the provenance log.
|
|
35
|
+
#
|
|
36
|
+
# @param _name [String] resource name (only 'log' supported)
|
|
37
|
+
# @return [Hash, nil] resource content
|
|
38
|
+
def read(_name = 'log')
|
|
39
|
+
path = RosettAi.root.join(PROVENANCE_FILE)
|
|
40
|
+
return nil unless path.exist?
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
uri: "#{URI_PREFIX}log",
|
|
44
|
+
content: File.read(path),
|
|
45
|
+
mime_type: 'application/x-yaml'
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
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 Resources
|
|
9
|
+
# MCP resource provider for compiled rule markdown files.
|
|
10
|
+
#
|
|
11
|
+
# Exposes compiled rules (output of raictl compile) as MCP
|
|
12
|
+
# resources with URIs in the format rosett-ai://rules/{name}.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class RulesResource
|
|
17
|
+
URI_PREFIX = 'rosett-ai://rules/'
|
|
18
|
+
|
|
19
|
+
# Lists all available compiled rule resources.
|
|
20
|
+
#
|
|
21
|
+
# @return [Array<Hash>] resource entries
|
|
22
|
+
def list
|
|
23
|
+
dir = RosettAi.paths.rules_dir
|
|
24
|
+
return [] unless dir.directory?
|
|
25
|
+
|
|
26
|
+
dir.glob('*.md').map { |path| resource_entry(path) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Reads a specific compiled rule.
|
|
30
|
+
#
|
|
31
|
+
# @param name [String] rule filename (without .md)
|
|
32
|
+
# @return [Hash, nil] resource content
|
|
33
|
+
def read(name)
|
|
34
|
+
path = RosettAi.paths.rules_dir.join("#{name}.md")
|
|
35
|
+
return nil unless path.exist?
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
uri: "#{URI_PREFIX}#{name}",
|
|
39
|
+
content: File.read(path),
|
|
40
|
+
mime_type: 'text/markdown'
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# @param path [Pathname]
|
|
47
|
+
# @return [Hash]
|
|
48
|
+
def resource_entry(path)
|
|
49
|
+
name = path.basename('.md').to_s
|
|
50
|
+
{
|
|
51
|
+
uri: "#{URI_PREFIX}#{name}",
|
|
52
|
+
name: name,
|
|
53
|
+
description: "Compiled rule: #{name}",
|
|
54
|
+
mime_type: 'text/markdown'
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
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 Resources
|
|
9
|
+
# MCP resource provider for JSON Schema files.
|
|
10
|
+
#
|
|
11
|
+
# Exposes validation schemas as MCP resources with
|
|
12
|
+
# URIs in the format rosett-ai://schema/{name}.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class SchemaResource
|
|
17
|
+
URI_PREFIX = 'rosett-ai://schema/'
|
|
18
|
+
|
|
19
|
+
# Lists all available schema resources.
|
|
20
|
+
#
|
|
21
|
+
# @return [Array<Hash>] resource entries
|
|
22
|
+
def list
|
|
23
|
+
dir = RosettAi.root.join('conf', 'schemas')
|
|
24
|
+
return [] unless dir.directory?
|
|
25
|
+
|
|
26
|
+
dir.glob('*.json').map { |path| resource_entry(path) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Reads a specific schema resource.
|
|
30
|
+
#
|
|
31
|
+
# @param name [String] schema name (without _schema.json)
|
|
32
|
+
# @return [Hash, nil] resource content
|
|
33
|
+
def read(name)
|
|
34
|
+
path = resolve_path(name)
|
|
35
|
+
return nil unless path&.exist?
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
uri: "#{URI_PREFIX}#{name}",
|
|
39
|
+
content: File.read(path),
|
|
40
|
+
mime_type: 'application/json'
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# @param name [String]
|
|
47
|
+
# @return [Pathname, nil]
|
|
48
|
+
def resolve_path(name)
|
|
49
|
+
dir = RosettAi.root.join('conf', 'schemas')
|
|
50
|
+
# Try exact filename first, then with _schema.json suffix
|
|
51
|
+
exact = dir.join("#{name}.json")
|
|
52
|
+
return exact if exact.exist?
|
|
53
|
+
|
|
54
|
+
suffixed = dir.join("#{name}_schema.json")
|
|
55
|
+
suffixed.exist? ? suffixed : nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @param path [Pathname]
|
|
59
|
+
# @return [Hash]
|
|
60
|
+
def resource_entry(path)
|
|
61
|
+
name = path.basename('.json').to_s
|
|
62
|
+
{
|
|
63
|
+
uri: "#{URI_PREFIX}#{name}",
|
|
64
|
+
name: name,
|
|
65
|
+
description: "JSON Schema: #{name}",
|
|
66
|
+
mime_type: 'application/json'
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
# Standardized MCP tool response formatting.
|
|
9
|
+
#
|
|
10
|
+
# Provides consistent response structure for all MCP tools
|
|
11
|
+
# with success, finding, and error states.
|
|
12
|
+
#
|
|
13
|
+
# @author hugo
|
|
14
|
+
# @author claude
|
|
15
|
+
module ResponseHelper
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# Build a success response hash.
|
|
19
|
+
#
|
|
20
|
+
# @param message [String] human-readable message
|
|
21
|
+
# @param data [Hash] additional response data
|
|
22
|
+
# @return [Hash] response with success: true
|
|
23
|
+
def success(message, data = {})
|
|
24
|
+
{ success: true, message: message }.merge(data)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Build a finding response hash (not an error, but notable).
|
|
28
|
+
#
|
|
29
|
+
# @param message [String] human-readable message
|
|
30
|
+
# @param data [Hash] additional response data
|
|
31
|
+
# @return [Hash] response with finding: true
|
|
32
|
+
def finding(message, data = {})
|
|
33
|
+
{ finding: true, message: message }.merge(data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Build an error response hash.
|
|
37
|
+
#
|
|
38
|
+
# @param message [String] human-readable error message
|
|
39
|
+
# @param data [Hash] additional response data
|
|
40
|
+
# @return [Hash] response with error: true
|
|
41
|
+
def error(message, data = {})
|
|
42
|
+
{ error: true, message: message }.merge(data)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
# Security event logger for MCP server.
|
|
9
|
+
#
|
|
10
|
+
# Logs authentication, rate limiting, and origin validation
|
|
11
|
+
# events to stderr in structured format.
|
|
12
|
+
#
|
|
13
|
+
# @author hugo
|
|
14
|
+
# @author claude
|
|
15
|
+
module SecurityLogger
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# Log a successful authentication event.
|
|
19
|
+
#
|
|
20
|
+
# @param client_id [String] authenticated client identifier
|
|
21
|
+
# @return [void]
|
|
22
|
+
def auth_success(client_id)
|
|
23
|
+
log(:info, 'auth_success', client_id: client_id)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Log a failed authentication attempt.
|
|
27
|
+
#
|
|
28
|
+
# @param reason [String] failure reason
|
|
29
|
+
# @return [void]
|
|
30
|
+
def auth_failure(reason)
|
|
31
|
+
log(:warn, 'auth_failure', reason: reason)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Log a rate-limited request.
|
|
35
|
+
#
|
|
36
|
+
# @param key [String] bucket key that was limited
|
|
37
|
+
# @return [void]
|
|
38
|
+
def rate_limited(key)
|
|
39
|
+
log(:warn, 'rate_limited', bucket_key: key)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Log a rejected origin.
|
|
43
|
+
#
|
|
44
|
+
# @param origin [String] the rejected origin
|
|
45
|
+
# @return [void]
|
|
46
|
+
def origin_rejected(origin)
|
|
47
|
+
log(:warn, 'origin_rejected', origin: origin)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @param level [Symbol] log level
|
|
51
|
+
# @param event [String] event name
|
|
52
|
+
# @param data [Hash] event data
|
|
53
|
+
# @return [void]
|
|
54
|
+
def log(level, event, **data)
|
|
55
|
+
timestamp = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
56
|
+
warn "[rai-mcp] #{timestamp} #{level} #{event} #{data.map { |k, v| "#{k}=#{v}" }.join(' ')}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,212 @@
|
|
|
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
|
+
# MCP server for Rosett-AI — exposes validation, compilation, and
|
|
9
|
+
# configuration management as MCP tools, resources, and prompts.
|
|
10
|
+
#
|
|
11
|
+
# Supports stdio (default) and HTTP (Puma) transports with a
|
|
12
|
+
# 6-layer Rack middleware stack for security.
|
|
13
|
+
#
|
|
14
|
+
# Requires the `mcp` gem (optional dependency, :mcp group).
|
|
15
|
+
#
|
|
16
|
+
# @author hugo
|
|
17
|
+
# @author claude
|
|
18
|
+
class Server
|
|
19
|
+
MCP_SPEC_VERSION = '2025-03-26'
|
|
20
|
+
DEFAULT_PORT = 8484
|
|
21
|
+
DEFAULT_HOST = 'localhost'
|
|
22
|
+
|
|
23
|
+
attr_reader :security_config
|
|
24
|
+
|
|
25
|
+
# @param config_path [String, nil] path to server config YAML
|
|
26
|
+
# @param plugins [Array<String>] explicit plugin names to load
|
|
27
|
+
def initialize(config_path: nil, plugins: [])
|
|
28
|
+
@config_path = config_path
|
|
29
|
+
@plugin_names = plugins
|
|
30
|
+
@security_config = load_security_config(config_path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Boolean] true if the mcp gem is available
|
|
34
|
+
def self.available?
|
|
35
|
+
require 'mcp'
|
|
36
|
+
true
|
|
37
|
+
rescue LoadError
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Starts the MCP server on stdio transport.
|
|
42
|
+
#
|
|
43
|
+
# @return [void]
|
|
44
|
+
# @raise [RosettAi::McpError] if mcp gem is not available
|
|
45
|
+
def serve
|
|
46
|
+
ensure_mcp_available!
|
|
47
|
+
server = build_server
|
|
48
|
+
transport = ::MCP::Server::Transports::StdioTransport.new(server)
|
|
49
|
+
transport.open
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Starts the MCP server on HTTP transport via Puma.
|
|
53
|
+
#
|
|
54
|
+
# @param options [Hash] HTTP transport options
|
|
55
|
+
# @option options [Integer] :port HTTP port (default 8484)
|
|
56
|
+
# @option options [String] :host bind address (default localhost)
|
|
57
|
+
# @option options [String, nil] :tls_cert path to TLS certificate
|
|
58
|
+
# @option options [String, nil] :tls_key path to TLS private key
|
|
59
|
+
# @option options [Boolean] :allow_remote allow non-localhost binding
|
|
60
|
+
# @return [void]
|
|
61
|
+
# @raise [RosettAi::McpError] if puma/rackup gems are not available
|
|
62
|
+
def serve_http(**options)
|
|
63
|
+
ensure_mcp_available!
|
|
64
|
+
ensure_http_available!
|
|
65
|
+
|
|
66
|
+
port = options.fetch(:port, DEFAULT_PORT)
|
|
67
|
+
host = options.fetch(:host, DEFAULT_HOST)
|
|
68
|
+
tls_cert = options[:tls_cert]
|
|
69
|
+
tls_key = options[:tls_key]
|
|
70
|
+
|
|
71
|
+
validate_host_binding(host, options.fetch(:allow_remote, false))
|
|
72
|
+
|
|
73
|
+
rack_app = build_rack_app
|
|
74
|
+
start_puma(rack_app, port: port, host: host,
|
|
75
|
+
tls_cert: tls_cert, tls_key: tls_key)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Build the Rack application for HTTP transport.
|
|
79
|
+
#
|
|
80
|
+
# @return [Proc] Rack application
|
|
81
|
+
def http_app
|
|
82
|
+
ensure_mcp_available!
|
|
83
|
+
ensure_http_available!
|
|
84
|
+
build_rack_app
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns the tool registry for listing available tools.
|
|
88
|
+
#
|
|
89
|
+
# @return [Array<Hash>] tool descriptors
|
|
90
|
+
def tools
|
|
91
|
+
Governance::TOOL_CLASSES.map do |klass|
|
|
92
|
+
{
|
|
93
|
+
name: klass::TOOL_NAME,
|
|
94
|
+
description: klass::DESCRIPTION,
|
|
95
|
+
annotations: klass::ANNOTATIONS
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def ensure_mcp_available!
|
|
103
|
+
return if self.class.available?
|
|
104
|
+
|
|
105
|
+
raise RosettAi::McpError,
|
|
106
|
+
'MCP gem not available. Install with: bundle install --with mcp'
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def ensure_http_available!
|
|
110
|
+
require 'puma'
|
|
111
|
+
require 'rackup'
|
|
112
|
+
rescue LoadError => e
|
|
113
|
+
raise RosettAi::McpError,
|
|
114
|
+
"HTTP transport requires puma and rackup gems: #{e.message}"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def build_server
|
|
118
|
+
server = ::MCP::Server.new(
|
|
119
|
+
name: 'rosett-ai',
|
|
120
|
+
version: RosettAi::VERSION,
|
|
121
|
+
instructions: Instructions.generate
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
Governance.register(server)
|
|
125
|
+
Plugins.load_all(server, @plugin_names)
|
|
126
|
+
server
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def build_rack_app
|
|
130
|
+
server = build_server
|
|
131
|
+
transport = ::MCP::Server::Transports::StreamableHTTPTransport.new(server)
|
|
132
|
+
app = transport.method(:call)
|
|
133
|
+
security = @security_config
|
|
134
|
+
|
|
135
|
+
Rack::Builder.new do
|
|
136
|
+
use Middleware::RequestSize, max_size: security.max_request_size
|
|
137
|
+
use Middleware::ContentType if security.content_type_enforcement
|
|
138
|
+
use Middleware::OriginValidation, config: security.origin
|
|
139
|
+
use Middleware::Cors, config: security.cors if security.cors.enabled
|
|
140
|
+
use Middleware::Authentication, config: security.authentication
|
|
141
|
+
use Middleware::RateLimit, config: security.rate_limiting if security.rate_limiting.enabled
|
|
142
|
+
run app
|
|
143
|
+
end.to_app
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def start_puma(rack_app, port:, host:, tls_cert:, tls_key:)
|
|
147
|
+
puma_config = Puma::Configuration.new do |conf|
|
|
148
|
+
conf.app rack_app
|
|
149
|
+
if tls_cert && tls_key
|
|
150
|
+
conf.bind "ssl://#{host}:#{port}?cert=#{tls_cert}&key=#{tls_key}"
|
|
151
|
+
else
|
|
152
|
+
conf.bind "tcp://#{host}:#{port}"
|
|
153
|
+
end
|
|
154
|
+
conf.quiet
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
launcher = Puma::Launcher.new(puma_config)
|
|
158
|
+
launcher.run
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def validate_host_binding(host, allow_remote)
|
|
162
|
+
return if allow_remote
|
|
163
|
+
return if ['localhost', '127.0.0.1', '::1'].include?(host)
|
|
164
|
+
|
|
165
|
+
raise RosettAi::McpError,
|
|
166
|
+
"Binding to #{host} requires --allow-remote flag for security"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def load_security_config(config_path)
|
|
170
|
+
config = HttpSecurityConfig.new
|
|
171
|
+
yaml = load_config_yaml(config_path)
|
|
172
|
+
config.apply(yaml['http']) if yaml&.dig('http')
|
|
173
|
+
config
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def load_config_yaml(config_path)
|
|
177
|
+
path = config_path || find_config_file
|
|
178
|
+
return nil unless path && File.exist?(path)
|
|
179
|
+
|
|
180
|
+
YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
181
|
+
rescue Psych::SyntaxError
|
|
182
|
+
nil
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def find_config_file
|
|
186
|
+
candidates = [
|
|
187
|
+
ENV.fetch('RAI_MCP_CONFIG', nil),
|
|
188
|
+
find_upward('.rosett-ai-mcp.yml'),
|
|
189
|
+
File.expand_path('~/.config/rosett-ai/mcp/server.yml'),
|
|
190
|
+
'/etc/rosett-ai/mcp/server.yml',
|
|
191
|
+
RosettAi.root.join('conf', 'mcp', 'server_defaults.yml').to_s
|
|
192
|
+
].compact
|
|
193
|
+
|
|
194
|
+
candidates.find { |path| File.exist?(path) }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def find_upward(filename)
|
|
198
|
+
dir = Dir.pwd
|
|
199
|
+
loop do
|
|
200
|
+
candidate = File.join(dir, filename)
|
|
201
|
+
return candidate if File.exist?(candidate)
|
|
202
|
+
|
|
203
|
+
parent = File.dirname(dir)
|
|
204
|
+
break if parent == dir
|
|
205
|
+
|
|
206
|
+
dir = parent
|
|
207
|
+
end
|
|
208
|
+
nil
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
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 Settings
|
|
9
|
+
# Installs MCP servers with trust-first validation.
|
|
10
|
+
#
|
|
11
|
+
# All server installations must originate from a trusted source.
|
|
12
|
+
# Untrusted sources are rejected with actionable error messages.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class ServerInstaller
|
|
17
|
+
# @param trust_manager [TrustManager] trust validation
|
|
18
|
+
# @param registry [Admin::Registry] server registry
|
|
19
|
+
# @param schema_validator [Admin::SchemaValidator] config validator
|
|
20
|
+
def initialize(trust_manager:, registry:, schema_validator:)
|
|
21
|
+
@trust_manager = trust_manager
|
|
22
|
+
@registry = registry
|
|
23
|
+
@schema_validator = schema_validator
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Installs an MCP server from a URI with trust validation.
|
|
27
|
+
#
|
|
28
|
+
# @param name [String] server name
|
|
29
|
+
# @param uri [String] server URI or command
|
|
30
|
+
# @param transport [String] transport type (stdio, http)
|
|
31
|
+
# @return [Hash] result with :success, :message, :server
|
|
32
|
+
def install(name:, uri:, transport: 'stdio')
|
|
33
|
+
trust_result = validate_trust(uri, transport)
|
|
34
|
+
return trust_result unless trust_result[:trusted]
|
|
35
|
+
|
|
36
|
+
config = build_config(name, uri, transport)
|
|
37
|
+
validation = @schema_validator.validate(config)
|
|
38
|
+
return validation_failure(validation) unless validation[:valid]
|
|
39
|
+
|
|
40
|
+
path = @registry.add(config)
|
|
41
|
+
{
|
|
42
|
+
success: true,
|
|
43
|
+
message: "Server '#{name}' installed from #{trust_result[:source_type]} source",
|
|
44
|
+
server: config,
|
|
45
|
+
path: path.to_s
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Removes an MCP server by name.
|
|
50
|
+
#
|
|
51
|
+
# @param name [String] server name
|
|
52
|
+
# @param force [Boolean] skip confirmation for enterprise-managed servers
|
|
53
|
+
# @return [Hash] result with :success, :message
|
|
54
|
+
def remove(name:, force: false)
|
|
55
|
+
server = @registry.find(name)
|
|
56
|
+
return { success: false, message: "Server '#{name}' not found" } unless server
|
|
57
|
+
|
|
58
|
+
if managed_server?(server) && !force
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
message: "Server '#{name}' is enterprise-managed. Use --force to override."
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
@registry.remove(name)
|
|
66
|
+
{ success: true, message: "Server '#{name}' removed" }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def validate_trust(uri, transport)
|
|
72
|
+
return { trusted: true, source_type: 'local' } if local_transport?(transport)
|
|
73
|
+
|
|
74
|
+
result = @trust_manager.validate_uri(uri)
|
|
75
|
+
return result if result[:trusted]
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
trusted: false,
|
|
79
|
+
success: false,
|
|
80
|
+
message: "Untrusted source: #{result[:domain]}. " \
|
|
81
|
+
'Add trust with: rai mcp trust-sources add DOMAIN'
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def local_transport?(transport)
|
|
86
|
+
transport == 'stdio'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def build_config(name, uri, transport)
|
|
90
|
+
config = { name: name, transport: transport }
|
|
91
|
+
if transport == 'stdio'
|
|
92
|
+
config[:command] = uri
|
|
93
|
+
else
|
|
94
|
+
config[:url] = uri
|
|
95
|
+
end
|
|
96
|
+
config
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def validation_failure(validation)
|
|
100
|
+
{
|
|
101
|
+
success: false,
|
|
102
|
+
message: "Validation failed: #{validation[:errors].join(', ')}"
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def managed_server?(server)
|
|
107
|
+
server[:source_file].to_s.include?('managed')
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|