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,535 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'terminal-table'
|
|
7
|
+
|
|
8
|
+
module RosettAi
|
|
9
|
+
module Thor
|
|
10
|
+
module Tasks
|
|
11
|
+
# Thor CLI for MCP server and administration.
|
|
12
|
+
#
|
|
13
|
+
# Provides serve, list, validate, status, audit, key management,
|
|
14
|
+
# and trust source subcommands for MCP server management.
|
|
15
|
+
#
|
|
16
|
+
# @author hugo
|
|
17
|
+
# @author claude
|
|
18
|
+
class Mcp < ::Thor
|
|
19
|
+
namespace 'mcp'
|
|
20
|
+
|
|
21
|
+
desc 'serve', 'Start Rosett-AI as an MCP server'
|
|
22
|
+
long_desc <<~LONGDESC
|
|
23
|
+
Start Rosett-AI as a Model Context Protocol server.
|
|
24
|
+
|
|
25
|
+
By default, uses stdio transport. With --http, starts an HTTP server
|
|
26
|
+
on the specified port (default 8484) with the full security middleware
|
|
27
|
+
stack (request size, content type, origin validation, CORS,
|
|
28
|
+
authentication, rate limiting).
|
|
29
|
+
|
|
30
|
+
Exit codes: 0 success, 5 MCP error.
|
|
31
|
+
|
|
32
|
+
EXAMPLES
|
|
33
|
+
|
|
34
|
+
raictl mcp serve
|
|
35
|
+
raictl mcp serve --http
|
|
36
|
+
raictl mcp serve --http --port 9090
|
|
37
|
+
raictl mcp serve --http --tls-cert cert.pem --tls-key key.pem
|
|
38
|
+
LONGDESC
|
|
39
|
+
method_option :http, type: :boolean, default: false, desc: 'Use HTTP transport instead of stdio'
|
|
40
|
+
method_option :port, type: :numeric, default: 8484, desc: 'HTTP port'
|
|
41
|
+
method_option :host, type: :string, default: 'localhost', desc: 'HTTP bind address'
|
|
42
|
+
method_option :tls_cert, type: :string, desc: 'Path to TLS certificate'
|
|
43
|
+
method_option :tls_key, type: :string, desc: 'Path to TLS private key'
|
|
44
|
+
method_option :allow_remote, type: :boolean, default: false, desc: 'Allow non-localhost binding'
|
|
45
|
+
method_option :config, type: :string, desc: 'Path to server config YAML'
|
|
46
|
+
method_option :plugin, type: :array, default: [], desc: 'Plugin names to load'
|
|
47
|
+
def serve
|
|
48
|
+
server = RosettAi::Mcp::Server.new(
|
|
49
|
+
config_path: options[:config],
|
|
50
|
+
plugins: options[:plugin]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if options[:http]
|
|
54
|
+
unless ENV.fetch('RAI_MCP_API_KEY', nil)
|
|
55
|
+
warn Rainbow('[rosett-ai-mcp] WARNING: Starting HTTP server with no API key configured. ' \
|
|
56
|
+
'Set RAI_MCP_API_KEY or use a keyfile for authentication.').yellow
|
|
57
|
+
end
|
|
58
|
+
server.serve_http(
|
|
59
|
+
port: options[:port],
|
|
60
|
+
host: options[:host],
|
|
61
|
+
tls_cert: options[:tls_cert],
|
|
62
|
+
tls_key: options[:tls_key],
|
|
63
|
+
allow_remote: options[:allow_remote]
|
|
64
|
+
)
|
|
65
|
+
else
|
|
66
|
+
server.serve
|
|
67
|
+
end
|
|
68
|
+
rescue RosettAi::McpError => e
|
|
69
|
+
warn Rainbow(e.message).red
|
|
70
|
+
exit(5)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
desc 'list', 'List configured MCP servers'
|
|
74
|
+
long_desc <<~LONGDESC
|
|
75
|
+
List all MCP servers registered in the rai configuration.
|
|
76
|
+
|
|
77
|
+
Displays name, transport type, and command/URL for each server.
|
|
78
|
+
Use --format json for machine-readable output.
|
|
79
|
+
|
|
80
|
+
EXAMPLES
|
|
81
|
+
|
|
82
|
+
raictl mcp list
|
|
83
|
+
raictl mcp list --format json
|
|
84
|
+
|
|
85
|
+
Related: mcp status, mcp validate
|
|
86
|
+
LONGDESC
|
|
87
|
+
method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
|
|
88
|
+
def list
|
|
89
|
+
registry = build_registry
|
|
90
|
+
servers = registry.list
|
|
91
|
+
|
|
92
|
+
if servers.empty?
|
|
93
|
+
say(t('no_servers'))
|
|
94
|
+
return
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
render_server_list(servers)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
desc 'validate', 'Validate MCP server configurations'
|
|
101
|
+
long_desc <<~LONGDESC
|
|
102
|
+
Validate all MCP server configurations against the MCP schema.
|
|
103
|
+
|
|
104
|
+
Checks transport settings, command paths, and required fields.
|
|
105
|
+
Exit codes: 0 all valid, 2 validation errors found.
|
|
106
|
+
|
|
107
|
+
EXAMPLES
|
|
108
|
+
|
|
109
|
+
raictl mcp validate
|
|
110
|
+
raictl mcp validate --format json
|
|
111
|
+
|
|
112
|
+
Related: mcp list, mcp status
|
|
113
|
+
LONGDESC
|
|
114
|
+
method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
|
|
115
|
+
def validate
|
|
116
|
+
registry = build_registry
|
|
117
|
+
validator = RosettAi::Mcp::Admin::SchemaValidator.new
|
|
118
|
+
results = validator.validate_all(registry)
|
|
119
|
+
|
|
120
|
+
render_validation_results(results)
|
|
121
|
+
all_valid = results.all? { |r| r[:valid] }
|
|
122
|
+
exit(all_valid ? 0 : 2)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
desc 'status', 'Show MCP server health status'
|
|
126
|
+
long_desc <<~LONGDESC
|
|
127
|
+
Check the health of all configured MCP servers.
|
|
128
|
+
|
|
129
|
+
Probes each server's transport endpoint and reports availability.
|
|
130
|
+
Use --format json for machine-readable output.
|
|
131
|
+
|
|
132
|
+
EXAMPLES
|
|
133
|
+
|
|
134
|
+
raictl mcp status
|
|
135
|
+
raictl mcp status --format json
|
|
136
|
+
|
|
137
|
+
Related: mcp list, mcp validate
|
|
138
|
+
LONGDESC
|
|
139
|
+
method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
|
|
140
|
+
def status
|
|
141
|
+
registry = build_registry
|
|
142
|
+
checker = RosettAi::Mcp::Admin::HealthChecker.new
|
|
143
|
+
results = checker.check_all(registry)
|
|
144
|
+
|
|
145
|
+
if results.empty?
|
|
146
|
+
say(t('no_servers'))
|
|
147
|
+
return
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
render_health_results(results)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
desc 'audit', 'Generate MCP compliance audit report'
|
|
154
|
+
long_desc <<~LONGDESC
|
|
155
|
+
Generate a compliance audit report for all configured MCP servers.
|
|
156
|
+
|
|
157
|
+
Combines health checks and schema validation into a single report
|
|
158
|
+
with summary statistics. Default output is JSON for integration
|
|
159
|
+
with CI/CD pipelines.
|
|
160
|
+
|
|
161
|
+
EXAMPLES
|
|
162
|
+
|
|
163
|
+
raictl mcp audit
|
|
164
|
+
raictl mcp audit --format table
|
|
165
|
+
|
|
166
|
+
Related: mcp validate, mcp status
|
|
167
|
+
LONGDESC
|
|
168
|
+
method_option :format, type: :string, default: 'json', enum: ['table', 'json'], desc: 'Output format'
|
|
169
|
+
def audit
|
|
170
|
+
registry = build_registry
|
|
171
|
+
checker = RosettAi::Mcp::Admin::HealthChecker.new
|
|
172
|
+
validator = RosettAi::Mcp::Admin::SchemaValidator.new
|
|
173
|
+
auditor = RosettAi::Mcp::Admin::Auditor.new(
|
|
174
|
+
registry: registry, health_checker: checker, schema_validator: validator
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
report = auditor.audit
|
|
178
|
+
render_audit_report(report)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
desc 'add NAME URI', 'Install an MCP server from a trusted source'
|
|
182
|
+
long_desc <<~LONGDESC
|
|
183
|
+
Install an MCP server from a URI. The domain must be in the trust
|
|
184
|
+
sources list. Use `mcp trust-add` to add domains first.
|
|
185
|
+
|
|
186
|
+
Example: raictl mcp add my-server npx:@example/mcp-server
|
|
187
|
+
|
|
188
|
+
Exit codes: 0 success, 1 untrusted source, 2 validation error.
|
|
189
|
+
|
|
190
|
+
Related: mcp remove, mcp trust-sources
|
|
191
|
+
LONGDESC
|
|
192
|
+
method_option :transport, type: :string, default: 'stdio',
|
|
193
|
+
enum: ['stdio', 'http', 'streamable-http'],
|
|
194
|
+
desc: 'Transport type'
|
|
195
|
+
def add(name, uri)
|
|
196
|
+
installer = build_installer
|
|
197
|
+
result = installer.install(name: name, uri: uri, transport: options[:transport])
|
|
198
|
+
|
|
199
|
+
if result[:success]
|
|
200
|
+
say(Rainbow(result[:message]).green)
|
|
201
|
+
else
|
|
202
|
+
warn(Rainbow(result[:message]).red)
|
|
203
|
+
exit(result[:trusted] == false ? 1 : 2)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
desc 'remove NAME', 'Remove a configured MCP server'
|
|
208
|
+
long_desc <<~LONGDESC
|
|
209
|
+
Remove a configured MCP server by name.
|
|
210
|
+
|
|
211
|
+
User-installed servers are removed directly. Managed servers
|
|
212
|
+
(from content packs) require --force.
|
|
213
|
+
|
|
214
|
+
EXAMPLES
|
|
215
|
+
|
|
216
|
+
raictl mcp remove my-server
|
|
217
|
+
raictl mcp remove managed-server --force
|
|
218
|
+
|
|
219
|
+
Related: mcp add, mcp list
|
|
220
|
+
LONGDESC
|
|
221
|
+
method_option :force, type: :boolean, default: false, desc: 'Force removal of managed servers'
|
|
222
|
+
def remove(name)
|
|
223
|
+
installer = build_installer
|
|
224
|
+
result = installer.remove(name: name, force: options[:force])
|
|
225
|
+
|
|
226
|
+
if result[:success]
|
|
227
|
+
say(Rainbow(result[:message]).green)
|
|
228
|
+
else
|
|
229
|
+
warn(Rainbow(result[:message]).red)
|
|
230
|
+
exit(1)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
desc 'trust-sources', 'List configured trust sources'
|
|
235
|
+
long_desc <<~LONGDESC
|
|
236
|
+
List all configured trust sources for MCP server installation.
|
|
237
|
+
|
|
238
|
+
Shows built-in domains (e.g., modelcontextprotocol.io) and
|
|
239
|
+
user-added domains. Servers can only be installed from trusted sources.
|
|
240
|
+
|
|
241
|
+
EXAMPLES
|
|
242
|
+
|
|
243
|
+
raictl mcp trust-sources
|
|
244
|
+
raictl mcp trust-sources --format json
|
|
245
|
+
|
|
246
|
+
Related: mcp trust-add, mcp trust-remove
|
|
247
|
+
LONGDESC
|
|
248
|
+
method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
|
|
249
|
+
map 'trust-sources' => :trust_sources
|
|
250
|
+
def trust_sources
|
|
251
|
+
manager = build_trust_manager
|
|
252
|
+
sources = manager.list
|
|
253
|
+
|
|
254
|
+
render_trust_sources(sources)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
desc 'trust-add DOMAIN', 'Add a trusted domain for MCP server installation'
|
|
258
|
+
long_desc <<~LONGDESC
|
|
259
|
+
Add a domain to the user trust sources list.
|
|
260
|
+
|
|
261
|
+
Once trusted, MCP servers from this domain can be installed with
|
|
262
|
+
`mcp add`. Use --description to document why the domain is trusted.
|
|
263
|
+
|
|
264
|
+
Example: raictl mcp trust-add example.com --description "Internal MCP server"
|
|
265
|
+
|
|
266
|
+
Related: mcp trust-remove, mcp trust-sources
|
|
267
|
+
LONGDESC
|
|
268
|
+
method_option :description, type: :string, default: 'User-trusted domain', desc: 'Trust source description'
|
|
269
|
+
map 'trust-add' => :trust_add
|
|
270
|
+
def trust_add(domain)
|
|
271
|
+
manager = build_trust_manager
|
|
272
|
+
manager.add_trust(domain, description: options[:description])
|
|
273
|
+
say(Rainbow("Trust added for: #{domain}").green)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
desc 'trust-remove DOMAIN', 'Remove a trusted domain'
|
|
277
|
+
long_desc <<~LONGDESC
|
|
278
|
+
Remove a domain from the user trust sources list.
|
|
279
|
+
|
|
280
|
+
Built-in trust sources cannot be removed. Only user-added domains
|
|
281
|
+
can be removed.
|
|
282
|
+
|
|
283
|
+
EXAMPLES
|
|
284
|
+
|
|
285
|
+
raictl mcp trust-remove example.com
|
|
286
|
+
|
|
287
|
+
Related: mcp trust-add, mcp trust-sources
|
|
288
|
+
LONGDESC
|
|
289
|
+
map 'trust-remove' => :trust_remove
|
|
290
|
+
def trust_remove(domain)
|
|
291
|
+
manager = build_trust_manager
|
|
292
|
+
if manager.remove_trust(domain)
|
|
293
|
+
say(Rainbow("Trust removed for: #{domain}").green)
|
|
294
|
+
else
|
|
295
|
+
warn(Rainbow("Domain not found in user trust sources: #{domain}").red)
|
|
296
|
+
exit(1)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
desc 'tools', 'List available MCP tools'
|
|
301
|
+
long_desc <<~LONGDESC
|
|
302
|
+
List all tools exposed by the Rosett-AI MCP server.
|
|
303
|
+
|
|
304
|
+
Shows tool name, description, and read-only hint for each tool.
|
|
305
|
+
These are the tools available to AI engines via the MCP protocol.
|
|
306
|
+
|
|
307
|
+
EXAMPLES
|
|
308
|
+
|
|
309
|
+
raictl mcp tools
|
|
310
|
+
LONGDESC
|
|
311
|
+
def tools
|
|
312
|
+
server = RosettAi::Mcp::Server.new
|
|
313
|
+
tool_list = server.tools
|
|
314
|
+
|
|
315
|
+
table = ::Terminal::Table.new(
|
|
316
|
+
headings: ['Tool', 'Description', 'Read-Only'],
|
|
317
|
+
rows: tool_list.map { |t| [t[:name], t[:description], t[:annotations]['readOnlyHint']] }
|
|
318
|
+
)
|
|
319
|
+
say(table)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
desc 'generate-key', 'Generate a new MCP API key'
|
|
323
|
+
long_desc <<~LONGDESC
|
|
324
|
+
Generate a new API key for MCP server authentication.
|
|
325
|
+
|
|
326
|
+
The key is printed to stdout. The salted hash is stored in the
|
|
327
|
+
keyfile. Use --name to identify the key.
|
|
328
|
+
|
|
329
|
+
EXAMPLES
|
|
330
|
+
|
|
331
|
+
raictl mcp generate-key --name claude-code
|
|
332
|
+
raictl mcp generate-key --name ci-runner --keyfile /etc/rosett-ai/mcp/keys.yml
|
|
333
|
+
LONGDESC
|
|
334
|
+
method_option :name, type: :string, required: true, desc: 'Key name'
|
|
335
|
+
method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
|
|
336
|
+
map 'generate-key' => :generate_key
|
|
337
|
+
def generate_key
|
|
338
|
+
path = RosettAi::Mcp::Keyfile.create_if_missing(options[:keyfile])
|
|
339
|
+
plaintext = RosettAi::Mcp::KeyHasher.generate_key
|
|
340
|
+
key_hash = RosettAi::Mcp::KeyHasher.hash_key(plaintext)
|
|
341
|
+
|
|
342
|
+
keyfile = RosettAi::Mcp::Keyfile.new(path)
|
|
343
|
+
keyfile.load!
|
|
344
|
+
keyfile.add_key(name: options[:name], key_hash: key_hash)
|
|
345
|
+
|
|
346
|
+
say(Rainbow("API key generated for '#{options[:name]}':").green)
|
|
347
|
+
say(plaintext)
|
|
348
|
+
say(Rainbow('Store this key securely — it cannot be recovered.').yellow)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
desc 'list-keys', 'List all MCP API keys'
|
|
352
|
+
long_desc <<~LONGDESC
|
|
353
|
+
List all API keys in the keyfile.
|
|
354
|
+
|
|
355
|
+
Shows key name, creation date, enabled status, and expiry.
|
|
356
|
+
Key hashes are never displayed.
|
|
357
|
+
|
|
358
|
+
EXAMPLES
|
|
359
|
+
|
|
360
|
+
raictl mcp list-keys
|
|
361
|
+
raictl mcp list-keys --keyfile /etc/rosett-ai/mcp/keys.yml
|
|
362
|
+
LONGDESC
|
|
363
|
+
method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
|
|
364
|
+
map 'list-keys' => :list_keys
|
|
365
|
+
def list_keys
|
|
366
|
+
path = File.expand_path(options[:keyfile])
|
|
367
|
+
unless File.exist?(path)
|
|
368
|
+
say(Rainbow('No keyfile found.').yellow)
|
|
369
|
+
return
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
keyfile = RosettAi::Mcp::Keyfile.new(path)
|
|
373
|
+
keyfile.load!
|
|
374
|
+
keys = keyfile.enabled_keys
|
|
375
|
+
|
|
376
|
+
if keys.empty?
|
|
377
|
+
say('No keys configured.')
|
|
378
|
+
return
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
table = ::Terminal::Table.new(
|
|
382
|
+
headings: ['Name', 'Created', 'Enabled', 'Expires'],
|
|
383
|
+
rows: keys.map { |k| [k[:name], k[:created_at], k[:enabled], k[:expires_at] || 'never'] }
|
|
384
|
+
)
|
|
385
|
+
say(table)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
desc 'revoke-key', 'Revoke an MCP API key'
|
|
389
|
+
long_desc <<~LONGDESC
|
|
390
|
+
Disable an API key by name.
|
|
391
|
+
|
|
392
|
+
The key remains in the keyfile but is marked as disabled.
|
|
393
|
+
Revoked keys cannot be used for authentication.
|
|
394
|
+
|
|
395
|
+
EXAMPLES
|
|
396
|
+
|
|
397
|
+
raictl mcp revoke-key --name old-key
|
|
398
|
+
LONGDESC
|
|
399
|
+
method_option :name, type: :string, required: true, desc: 'Key name to revoke'
|
|
400
|
+
method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
|
|
401
|
+
map 'revoke-key' => :revoke_key
|
|
402
|
+
def revoke_key
|
|
403
|
+
keyfile = RosettAi::Mcp::Keyfile.new(File.expand_path(options[:keyfile]))
|
|
404
|
+
keyfile.load!
|
|
405
|
+
|
|
406
|
+
if keyfile.revoke_key(options[:name])
|
|
407
|
+
say(Rainbow("Key '#{options[:name]}' revoked.").green)
|
|
408
|
+
else
|
|
409
|
+
warn(Rainbow("Key '#{options[:name]}' not found.").red)
|
|
410
|
+
exit(1)
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
desc 'rotate-key', 'Rotate an MCP API key'
|
|
415
|
+
long_desc <<~LONGDESC
|
|
416
|
+
Revoke an existing key and generate a replacement.
|
|
417
|
+
|
|
418
|
+
The old key is disabled and a new key with the same name is
|
|
419
|
+
created. The new plaintext key is printed to stdout.
|
|
420
|
+
|
|
421
|
+
EXAMPLES
|
|
422
|
+
|
|
423
|
+
raictl mcp rotate-key --name claude-code
|
|
424
|
+
LONGDESC
|
|
425
|
+
method_option :name, type: :string, required: true, desc: 'Key name to rotate'
|
|
426
|
+
method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
|
|
427
|
+
map 'rotate-key' => :rotate_key
|
|
428
|
+
def rotate_key
|
|
429
|
+
keyfile = RosettAi::Mcp::Keyfile.new(File.expand_path(options[:keyfile]))
|
|
430
|
+
keyfile.load!
|
|
431
|
+
|
|
432
|
+
result = keyfile.rotate_key(options[:name])
|
|
433
|
+
if result
|
|
434
|
+
say(Rainbow("Key '#{options[:name]}' rotated. New key:").green)
|
|
435
|
+
say(result[:plaintext])
|
|
436
|
+
say(Rainbow('Store this key securely — it cannot be recovered.').yellow)
|
|
437
|
+
else
|
|
438
|
+
warn(Rainbow("Key '#{options[:name]}' not found.").red)
|
|
439
|
+
exit(1)
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
private
|
|
444
|
+
|
|
445
|
+
def t(key, **args)
|
|
446
|
+
::I18n.t("rosett_ai.mcp.#{key}", **args)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def build_registry
|
|
450
|
+
RosettAi::Mcp::Admin::Registry.new
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def build_trust_manager
|
|
454
|
+
RosettAi::Mcp::Settings::TrustManager.new
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def build_installer
|
|
458
|
+
RosettAi::Mcp::Settings::ServerInstaller.new(
|
|
459
|
+
trust_manager: build_trust_manager,
|
|
460
|
+
registry: build_registry,
|
|
461
|
+
schema_validator: RosettAi::Mcp::Admin::SchemaValidator.new
|
|
462
|
+
)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def render_trust_sources(sources)
|
|
466
|
+
if options[:format] == 'json'
|
|
467
|
+
say(JSON.pretty_generate(sources))
|
|
468
|
+
return
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
table = ::Terminal::Table.new(
|
|
472
|
+
headings: ['Domain', 'Type', 'Description'],
|
|
473
|
+
rows: sources.map { |s| [s[:domain], s[:type], s[:description]] }
|
|
474
|
+
)
|
|
475
|
+
say(table)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def render_server_list(servers)
|
|
479
|
+
if options[:format] == 'json'
|
|
480
|
+
say(JSON.pretty_generate(servers))
|
|
481
|
+
return
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
table = ::Terminal::Table.new(
|
|
485
|
+
headings: ['Name', 'Transport', 'Command/URL'],
|
|
486
|
+
rows: servers.map { |s| [s[:name], s[:transport], s[:command] || s[:url]] }
|
|
487
|
+
)
|
|
488
|
+
say(table)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def render_validation_results(results)
|
|
492
|
+
if options[:format] == 'json'
|
|
493
|
+
say(JSON.pretty_generate(results))
|
|
494
|
+
return
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
results.each do |r|
|
|
498
|
+
status = r[:valid] ? Rainbow('valid').green : Rainbow('invalid').red
|
|
499
|
+
say(" #{r[:name]}: #{status}")
|
|
500
|
+
r[:errors]&.each { |e| say(Rainbow(" #{e}").red) }
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def render_health_results(results)
|
|
505
|
+
if options[:format] == 'json'
|
|
506
|
+
say(JSON.pretty_generate(results))
|
|
507
|
+
return
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
table = ::Terminal::Table.new(
|
|
511
|
+
headings: ['Name', 'Transport', 'Status', 'Message'],
|
|
512
|
+
rows: results.map do |r|
|
|
513
|
+
status = r[:status] == :available ? Rainbow('available').green : Rainbow(r[:status].to_s).red
|
|
514
|
+
[r[:name], r[:transport], status, r[:message]]
|
|
515
|
+
end
|
|
516
|
+
)
|
|
517
|
+
say(table)
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def render_audit_report(report)
|
|
521
|
+
if options[:format] == 'json'
|
|
522
|
+
say(JSON.pretty_generate(report))
|
|
523
|
+
return
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
say("Audit timestamp: #{report[:timestamp]}")
|
|
527
|
+
say("Servers: #{report[:summary][:total]}")
|
|
528
|
+
say("Healthy: #{report[:summary][:healthy]}")
|
|
529
|
+
say("Schema valid: #{report[:summary][:schema_valid]}")
|
|
530
|
+
say("Secure transport: #{report[:summary][:secure_transport]}")
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
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 'rainbow'
|
|
7
|
+
|
|
8
|
+
module RosettAi
|
|
9
|
+
module Thor
|
|
10
|
+
module Tasks
|
|
11
|
+
# CLI task for `raictl migrate` — legacy namespace migration.
|
|
12
|
+
#
|
|
13
|
+
# Handles migration of:
|
|
14
|
+
# - ~/.config/nncc/ → ~/.config/rosett-ai/ (XDG global config)
|
|
15
|
+
# - .nncc/ → .rosett-ai/ (per-project marker directories)
|
|
16
|
+
#
|
|
17
|
+
# @author hugo
|
|
18
|
+
# @author claude
|
|
19
|
+
class Migrate < ::Thor
|
|
20
|
+
default_task :run_migrate
|
|
21
|
+
|
|
22
|
+
desc 'run_migrate', 'Migrate legacy .nncc and ~/.config/nncc paths'
|
|
23
|
+
long_desc <<~LONGDESC
|
|
24
|
+
Migrates legacy nncc namespace artifacts to rosett-ai:
|
|
25
|
+
|
|
26
|
+
- ~/.config/nncc/ → ~/.config/rosett-ai/ (global XDG config)
|
|
27
|
+
- .nncc/ → .rosett-ai/ (project marker in current directory)
|
|
28
|
+
|
|
29
|
+
With --workspace, scans the given path recursively for projects
|
|
30
|
+
containing .nncc/ directories and migrates them all.
|
|
31
|
+
|
|
32
|
+
EXAMPLES
|
|
33
|
+
|
|
34
|
+
raictl migrate
|
|
35
|
+
raictl migrate --workspace ~/git
|
|
36
|
+
LONGDESC
|
|
37
|
+
option :workspace, type: :string, desc: 'Scan and migrate all .nncc/ dirs under PATH'
|
|
38
|
+
option :dry_run, type: :boolean, default: false, desc: 'Show what would be migrated'
|
|
39
|
+
def run_migrate
|
|
40
|
+
migrate_xdg_config
|
|
41
|
+
migrate_projects
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def migrate_xdg_config
|
|
47
|
+
migrator = RosettAi::Migration::NnccConfigMigrator.new
|
|
48
|
+
return report_no_config unless migrator.migration_needed?
|
|
49
|
+
return show_config_plan(migrator) if options[:dry_run]
|
|
50
|
+
|
|
51
|
+
result = migrator.migrate!
|
|
52
|
+
puts Rainbow("Migrated #{result.size} file(s) from ~/.config/nncc/").green
|
|
53
|
+
result.each { |file| puts " #{file}" }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def report_no_config
|
|
57
|
+
puts Rainbow('~/.config/nncc/ not found — nothing to migrate').green
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def migrate_projects
|
|
61
|
+
migrator = RosettAi::Migration::NnccProjectMigrator.new
|
|
62
|
+
|
|
63
|
+
if options[:workspace]
|
|
64
|
+
migrate_workspace(migrator)
|
|
65
|
+
else
|
|
66
|
+
migrate_current_project(migrator)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def migrate_workspace(migrator)
|
|
71
|
+
workspace = options[:workspace]
|
|
72
|
+
return show_workspace_plan(migrator, workspace) if options[:dry_run]
|
|
73
|
+
|
|
74
|
+
migrator.migrate_workspace(workspace)
|
|
75
|
+
show_project_results(migrator.results)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def show_workspace_plan(migrator, workspace)
|
|
79
|
+
projects = migrator.detect(workspace)
|
|
80
|
+
if projects.empty?
|
|
81
|
+
puts Rainbow("No .nncc/ directories found under #{workspace}").green
|
|
82
|
+
else
|
|
83
|
+
puts "Found #{projects.size} project(s) with .nncc/:"
|
|
84
|
+
projects.each { |project| puts " #{project}" }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def migrate_current_project(migrator)
|
|
89
|
+
pwd = Dir.pwd
|
|
90
|
+
return show_current_plan(pwd) if options[:dry_run]
|
|
91
|
+
|
|
92
|
+
migrator.migrate_project(pwd)
|
|
93
|
+
show_project_results(migrator.results)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def show_current_plan(pwd)
|
|
97
|
+
nncc = Pathname.new(pwd).join('.nncc')
|
|
98
|
+
if nncc.directory?
|
|
99
|
+
puts "Would migrate: #{nncc} → #{pwd}/.rosett-ai/"
|
|
100
|
+
else
|
|
101
|
+
puts Rainbow('No .nncc/ in current directory').green
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def show_config_plan(migrator)
|
|
106
|
+
plan = migrator.plan
|
|
107
|
+
puts "Would migrate #{plan.size} file(s) from ~/.config/nncc/:"
|
|
108
|
+
plan.each { |file| puts " #{file[:path]} (#{file[:size]} bytes)" }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def show_project_results(results)
|
|
112
|
+
color_map = { migrated: :green, skipped: :yellow }
|
|
113
|
+
results.each do |entry|
|
|
114
|
+
color = color_map.fetch(entry[:status], :red)
|
|
115
|
+
puts Rainbow(" #{entry[:path]}: #{entry[:message]}").send(color)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|