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,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'yaml'
|
|
7
|
+
require 'pathname'
|
|
8
|
+
|
|
9
|
+
module RosettAi
|
|
10
|
+
module Mcp
|
|
11
|
+
module Tools
|
|
12
|
+
# MCP tool: query applicable rules for a given context.
|
|
13
|
+
#
|
|
14
|
+
# Answers "What rules apply to this situation?" by matching rules
|
|
15
|
+
# against a file path, tool name, or action. Returns applicable
|
|
16
|
+
# rules ranked by priority. Read-only operation.
|
|
17
|
+
#
|
|
18
|
+
# @author hugo
|
|
19
|
+
# @author claude
|
|
20
|
+
class ContextQueryTool
|
|
21
|
+
TOOL_NAME = 'rai_context_query'
|
|
22
|
+
DESCRIPTION = 'Query applicable rules for a file path, tool name, or action'
|
|
23
|
+
|
|
24
|
+
ANNOTATIONS = {
|
|
25
|
+
'readOnlyHint' => true,
|
|
26
|
+
'destructiveHint' => false,
|
|
27
|
+
'idempotentHint' => true,
|
|
28
|
+
'openWorldHint' => false
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
INPUT_SCHEMA = {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
file_path: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'File path to query applicable rules for'
|
|
37
|
+
},
|
|
38
|
+
tool_name: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
description: 'Tool name to query applicable rules for'
|
|
41
|
+
},
|
|
42
|
+
action: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Action to query applicable rules for'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}.freeze
|
|
48
|
+
|
|
49
|
+
# Queries rules applicable to the given context.
|
|
50
|
+
#
|
|
51
|
+
# @param file_path [String, nil] file path context
|
|
52
|
+
# @param tool_name [String, nil] tool name context (e.g. 'Bash', 'Edit')
|
|
53
|
+
# @param action [String, nil] action context (e.g. 'write', 'delete')
|
|
54
|
+
# @return [Hash] applicable rules with metadata
|
|
55
|
+
def call(file_path: nil, tool_name: nil, action: nil)
|
|
56
|
+
query = build_query(file_path, tool_name, action)
|
|
57
|
+
return ResponseHelper.error('At least one query parameter required') if query.empty?
|
|
58
|
+
|
|
59
|
+
matches = search_rules(query)
|
|
60
|
+
matches.sort_by! { |m| -(m[:priority] || 0) }
|
|
61
|
+
|
|
62
|
+
{
|
|
63
|
+
applicable_rules: matches,
|
|
64
|
+
total: matches.size,
|
|
65
|
+
query: query
|
|
66
|
+
}
|
|
67
|
+
rescue StandardError => e
|
|
68
|
+
ResponseHelper.error("Context query failed: #{e.message}")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# @param file_path [String, nil]
|
|
74
|
+
# @param tool_name [String, nil]
|
|
75
|
+
# @param action [String, nil]
|
|
76
|
+
# @return [Hash]
|
|
77
|
+
def build_query(file_path, tool_name, action)
|
|
78
|
+
query = {}
|
|
79
|
+
query[:file_path] = file_path if file_path
|
|
80
|
+
query[:tool_name] = tool_name if tool_name
|
|
81
|
+
query[:action] = action if action
|
|
82
|
+
query
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param query [Hash]
|
|
86
|
+
# @return [Array<Hash>]
|
|
87
|
+
def search_rules(query)
|
|
88
|
+
matches = []
|
|
89
|
+
each_behaviour do |data|
|
|
90
|
+
match_rules(data, query, matches)
|
|
91
|
+
end
|
|
92
|
+
matches
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @param data [Hash]
|
|
96
|
+
# @param query [Hash]
|
|
97
|
+
# @param matches [Array<Hash>]
|
|
98
|
+
def match_rules(data, query, matches)
|
|
99
|
+
rules = data['rules']
|
|
100
|
+
return unless rules.is_a?(Array)
|
|
101
|
+
|
|
102
|
+
behaviour_name = data['name']
|
|
103
|
+
rules.each do |rule|
|
|
104
|
+
next unless rule.is_a?(Hash) && rule.fetch('enabled', true)
|
|
105
|
+
next unless rule_matches_query?(rule, query)
|
|
106
|
+
|
|
107
|
+
matches << format_match(behaviour_name, rule)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @param rule [Hash]
|
|
112
|
+
# @param query [Hash]
|
|
113
|
+
# @return [Boolean]
|
|
114
|
+
def rule_matches_query?(rule, query)
|
|
115
|
+
description = rule['description'].to_s.downcase
|
|
116
|
+
|
|
117
|
+
query.any? do |_key, value|
|
|
118
|
+
description.include?(value.to_s.downcase)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @param behaviour_name [String]
|
|
123
|
+
# @param rule [Hash]
|
|
124
|
+
# @return [Hash]
|
|
125
|
+
def format_match(behaviour_name, rule)
|
|
126
|
+
{
|
|
127
|
+
rule_id: rule['id'],
|
|
128
|
+
behaviour: behaviour_name,
|
|
129
|
+
priority: rule['priority'] || 0,
|
|
130
|
+
description: rule['description'].to_s.strip[0, 200]
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @yield [Hash] parsed behaviour data
|
|
135
|
+
def each_behaviour
|
|
136
|
+
behaviour_dirs.each do |dir|
|
|
137
|
+
next unless dir.directory?
|
|
138
|
+
|
|
139
|
+
dir.glob('*.yml').each do |path|
|
|
140
|
+
data = YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
141
|
+
yield data if data.is_a?(Hash)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @return [Array<Pathname>]
|
|
147
|
+
def behaviour_dirs
|
|
148
|
+
dirs = [RosettAi.root.join('conf', 'behaviour')]
|
|
149
|
+
xdg = RosettAi.paths.rai_conf_dir.join('behaviour')
|
|
150
|
+
dirs << xdg if xdg.directory?
|
|
151
|
+
dirs
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: list rai design documents.
|
|
10
|
+
#
|
|
11
|
+
# Returns a structured list of all design documents with
|
|
12
|
+
# metadata (name, status, priority, domain).
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class DesignListTool
|
|
17
|
+
TOOL_NAME = 'rai_design_list'
|
|
18
|
+
DESCRIPTION = 'List all rai design documents'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => true,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
# Executes the listing.
|
|
28
|
+
#
|
|
29
|
+
# @return [Hash] result with :designs array
|
|
30
|
+
def call
|
|
31
|
+
dir = RosettAi.root.join('conf', 'design')
|
|
32
|
+
return { designs: [] } unless dir.directory?
|
|
33
|
+
|
|
34
|
+
designs = dir.glob('*.yml').filter_map { |path| load_design(path) }
|
|
35
|
+
{ designs: designs }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def load_design(path)
|
|
41
|
+
data = YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
42
|
+
return nil unless data.is_a?(Hash)
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
name: data['name'],
|
|
46
|
+
status: data['status'],
|
|
47
|
+
priority: data['priority'],
|
|
48
|
+
domain: data['domain'],
|
|
49
|
+
file: path.basename.to_s
|
|
50
|
+
}
|
|
51
|
+
rescue Psych::SyntaxError
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: show a specific design document.
|
|
10
|
+
#
|
|
11
|
+
# Returns the full content and metadata of a named design document.
|
|
12
|
+
# Read-only operation.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class DesignShowTool
|
|
17
|
+
TOOL_NAME = 'rai_design_show'
|
|
18
|
+
DESCRIPTION = 'Show details of a specific rai design document'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => true,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
INPUT_SCHEMA = {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
name: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Design document name'
|
|
33
|
+
},
|
|
34
|
+
full: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
description: 'Include full content (default: false)'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
required: ['name']
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
# Executes the show operation.
|
|
43
|
+
#
|
|
44
|
+
# @param name [String] design document name (without .yml)
|
|
45
|
+
# @param full [Boolean] include acceptance criteria
|
|
46
|
+
# @return [Hash] design data or error
|
|
47
|
+
def call(name:, full: false)
|
|
48
|
+
path = RosettAi.root.join('conf', 'design', "#{name}.yml")
|
|
49
|
+
return ResponseHelper.error("Design document not found: #{name}") unless path.exist?
|
|
50
|
+
|
|
51
|
+
data = YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
52
|
+
result = {
|
|
53
|
+
name: data['name'],
|
|
54
|
+
description: data['description'],
|
|
55
|
+
status: data['status'],
|
|
56
|
+
priority: data['priority'],
|
|
57
|
+
domain: data['domain'],
|
|
58
|
+
version: data['version'],
|
|
59
|
+
file: path.basename.to_s
|
|
60
|
+
}
|
|
61
|
+
result[:acceptance_criteria] = Array(data['acceptance_criteria']) if full
|
|
62
|
+
result
|
|
63
|
+
rescue Psych::SyntaxError => e
|
|
64
|
+
ResponseHelper.error("YAML parse error in #{name}: #{e.message}")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: run rosett-ai diagnostics.
|
|
10
|
+
#
|
|
11
|
+
# Executes system diagnostic checks and reports status.
|
|
12
|
+
# Read-only operation.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class DoctorTool
|
|
17
|
+
TOOL_NAME = 'rai_doctor'
|
|
18
|
+
DESCRIPTION = 'Run rosett-ai diagnostic checks'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => true,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
INPUT_SCHEMA = {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
check: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Run a specific diagnostic check (default: all)'
|
|
33
|
+
},
|
|
34
|
+
format: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
enum: ['json', 'text'],
|
|
37
|
+
description: 'Output format (default: json)'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
# Executes diagnostic checks.
|
|
43
|
+
#
|
|
44
|
+
# @param check [String, nil] specific check to run
|
|
45
|
+
# @param format [String] output format ('json' or 'text')
|
|
46
|
+
# @return [Hash] diagnostic results
|
|
47
|
+
def call(check: nil, format: 'json')
|
|
48
|
+
results = RosettAi::Doctor.run_all(only: check)
|
|
49
|
+
failures = results.select { |r| r[:status] == 'fail' }
|
|
50
|
+
{
|
|
51
|
+
healthy: failures.empty?,
|
|
52
|
+
checks: results,
|
|
53
|
+
failures: failures,
|
|
54
|
+
format: format
|
|
55
|
+
}
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
ResponseHelper.error("Doctor check failed: #{e.message}")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: show documentation status.
|
|
10
|
+
#
|
|
11
|
+
# Returns the current state of project documentation.
|
|
12
|
+
# Read-only operation.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class DocumentationStatusTool
|
|
17
|
+
TOOL_NAME = 'rai_documentation_status'
|
|
18
|
+
DESCRIPTION = 'Show rai documentation status'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => true,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
# Executes the documentation status check.
|
|
28
|
+
#
|
|
29
|
+
# @return [Hash] documentation status
|
|
30
|
+
def call
|
|
31
|
+
root = RosettAi.root
|
|
32
|
+
doc_dir = root.join('doc')
|
|
33
|
+
man_dir = root.join('doc', 'man')
|
|
34
|
+
{
|
|
35
|
+
doc_files: doc_dir.directory? ? doc_dir.glob('*.md').size : 0,
|
|
36
|
+
man_pages: man_dir.directory? ? man_dir.glob('*.ronn').size : 0,
|
|
37
|
+
changelog_exists: root.join('CHANGELOG.md').exist?,
|
|
38
|
+
readme_exists: root.join('README.md').exist?,
|
|
39
|
+
install_guide_exists: root.join('INSTALL.md').exist?
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Mcp
|
|
8
|
+
module Tools
|
|
9
|
+
# MCP tool: manage and query rai engines.
|
|
10
|
+
#
|
|
11
|
+
# Lists, detects, and shows status of installed engines.
|
|
12
|
+
# Read-only operation.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class EnginesTool
|
|
17
|
+
TOOL_NAME = 'rosett_ai_engines'
|
|
18
|
+
DESCRIPTION = 'List, detect, or show status of rai engines'
|
|
19
|
+
|
|
20
|
+
ANNOTATIONS = {
|
|
21
|
+
'readOnlyHint' => true,
|
|
22
|
+
'destructiveHint' => false,
|
|
23
|
+
'idempotentHint' => true,
|
|
24
|
+
'openWorldHint' => false
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
VALID_ACTIONS = ['list', 'detect', 'status'].freeze
|
|
28
|
+
|
|
29
|
+
INPUT_SCHEMA = {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
action: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
enum: ['list', 'detect', 'status'],
|
|
35
|
+
description: 'Engine action (default: list)'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
# Executes the engine query.
|
|
41
|
+
#
|
|
42
|
+
# @param action [String] one of 'list', 'detect', 'status'
|
|
43
|
+
# @return [Hash] engine information
|
|
44
|
+
def call(action: 'list')
|
|
45
|
+
return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
|
|
46
|
+
|
|
47
|
+
case action
|
|
48
|
+
when 'list' then action_list
|
|
49
|
+
when 'detect' then action_detect
|
|
50
|
+
when 'status' then action_status
|
|
51
|
+
end
|
|
52
|
+
rescue StandardError => e
|
|
53
|
+
ResponseHelper.error("Engine #{action} failed: #{e.message}")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def action_list
|
|
59
|
+
engines = RosettAi::Plugins::Registry.available(:engine)
|
|
60
|
+
{ engines: engines.map { |name| { name: name } } }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def action_detect
|
|
64
|
+
RosettAi::Plugins::Registry.discover!
|
|
65
|
+
detected = RosettAi::Plugins::Registry.available(:engine)
|
|
66
|
+
{ detected: detected }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def action_status
|
|
70
|
+
default = resolve_default_engine
|
|
71
|
+
installed = RosettAi::Plugins::Registry.available(:engine)
|
|
72
|
+
{ default_engine: default, installed: installed.map { |name| { name: name } } }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [String] default engine name
|
|
76
|
+
def resolve_default_engine
|
|
77
|
+
RosettAi.rai_config.default_engine
|
|
78
|
+
rescue StandardError => e
|
|
79
|
+
"unknown (#{e.message})"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'time'
|
|
9
|
+
|
|
10
|
+
module RosettAi
|
|
11
|
+
module Mcp
|
|
12
|
+
module Tools
|
|
13
|
+
# MCP tool: install enforcement hook + register in settings.json.
|
|
14
|
+
#
|
|
15
|
+
# MUTATION tool — writes hook script to disk and updates Claude Code
|
|
16
|
+
# settings.json. Auto-backs up affected files before writing.
|
|
17
|
+
# Confirmation is required by default.
|
|
18
|
+
#
|
|
19
|
+
# @author hugo
|
|
20
|
+
# @author claude
|
|
21
|
+
class HookInstallTool
|
|
22
|
+
TOOL_NAME = 'rai_hook_install'
|
|
23
|
+
DESCRIPTION = 'Install enforcement hook: generate script, write to disk, register in settings'
|
|
24
|
+
|
|
25
|
+
ANNOTATIONS = {
|
|
26
|
+
'readOnlyHint' => false,
|
|
27
|
+
'destructiveHint' => true,
|
|
28
|
+
'idempotentHint' => true,
|
|
29
|
+
'openWorldHint' => false
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
INPUT_SCHEMA = {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
behaviour_name: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'Behaviour to generate hook for (default: all enforceable)'
|
|
38
|
+
},
|
|
39
|
+
rule_id: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'Specific rule ID to generate hook for'
|
|
42
|
+
},
|
|
43
|
+
scope: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: ['global', 'local'],
|
|
46
|
+
description: 'Installation scope (default: global)'
|
|
47
|
+
},
|
|
48
|
+
confirm: {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
description: 'Must be true to actually install — safety gate (default: false)'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}.freeze
|
|
54
|
+
|
|
55
|
+
HOOK_DIR_NAME = 'hooks'
|
|
56
|
+
HOOK_FILENAME = 'rai-enforce.rb'
|
|
57
|
+
BACKUP_SUFFIX = '.rai-backup'
|
|
58
|
+
|
|
59
|
+
# Installs the enforcement hook.
|
|
60
|
+
#
|
|
61
|
+
# @param behaviour_name [String, nil] specific behaviour
|
|
62
|
+
# @param rule_id [String, nil] specific rule
|
|
63
|
+
# @param scope [String] installation scope ('global' or 'project')
|
|
64
|
+
# @param confirm [Boolean] must be true to proceed (default false)
|
|
65
|
+
# @return [Hash] installation result with paths and backup info
|
|
66
|
+
def call(behaviour_name: nil, rule_id: nil, scope: 'global', confirm: false)
|
|
67
|
+
return confirmation_required_response unless confirm
|
|
68
|
+
|
|
69
|
+
preview = HookPreviewTool.new.call(
|
|
70
|
+
behaviour_name: behaviour_name, rule_id: rule_id, scope: scope
|
|
71
|
+
)
|
|
72
|
+
return preview if preview[:error]
|
|
73
|
+
|
|
74
|
+
hook_path = resolve_hook_path(scope)
|
|
75
|
+
backup_path = backup_existing(hook_path)
|
|
76
|
+
write_hook(hook_path, preview[:script])
|
|
77
|
+
settings_diff = register_in_settings(hook_path, scope)
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
installed: true,
|
|
81
|
+
hook_path: hook_path.to_s,
|
|
82
|
+
backup_path: backup_path&.to_s,
|
|
83
|
+
rules_count: preview[:rules_count],
|
|
84
|
+
behaviours: preview[:behaviours],
|
|
85
|
+
scope: scope,
|
|
86
|
+
settings_updated: settings_diff[:updated],
|
|
87
|
+
message: "Hook installed at #{hook_path} with #{preview[:rules_count]} rule(s)"
|
|
88
|
+
}
|
|
89
|
+
rescue StandardError => e
|
|
90
|
+
ResponseHelper.error("Hook install failed: #{e.message}")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
# @return [Hash] response requiring confirmation
|
|
96
|
+
def confirmation_required_response
|
|
97
|
+
{
|
|
98
|
+
error: true,
|
|
99
|
+
confirmation_required: true,
|
|
100
|
+
message: 'Hook installation requires confirm: true. ' \
|
|
101
|
+
'Use rai_hook_preview first to review the script.'
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @param scope [String]
|
|
106
|
+
# @return [Pathname]
|
|
107
|
+
def resolve_hook_path(scope)
|
|
108
|
+
base = scope == 'project' ? project_hooks_dir : global_hooks_dir
|
|
109
|
+
FileUtils.mkdir_p(base)
|
|
110
|
+
base.join(HOOK_FILENAME)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @return [Pathname]
|
|
114
|
+
def global_hooks_dir
|
|
115
|
+
RosettAi.paths.global_dir.join(HOOK_DIR_NAME)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @return [Pathname]
|
|
119
|
+
def project_hooks_dir
|
|
120
|
+
RosettAi.context.project_root.join('.claude', HOOK_DIR_NAME)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Backs up existing hook file before overwriting.
|
|
124
|
+
#
|
|
125
|
+
# @param hook_path [Pathname]
|
|
126
|
+
# @return [Pathname, nil] backup path or nil if no backup needed
|
|
127
|
+
def backup_existing(hook_path)
|
|
128
|
+
return nil unless hook_path.exist?
|
|
129
|
+
|
|
130
|
+
timestamp = Time.now.utc.strftime('%Y%m%d%H%M%S')
|
|
131
|
+
backup = Pathname.new("#{hook_path}#{BACKUP_SUFFIX}.#{timestamp}")
|
|
132
|
+
FileUtils.cp(hook_path, backup)
|
|
133
|
+
backup
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# @param hook_path [Pathname]
|
|
137
|
+
# @param content [String]
|
|
138
|
+
def write_hook(hook_path, content)
|
|
139
|
+
FileUtils.mkdir_p(hook_path.dirname)
|
|
140
|
+
File.write(hook_path, content)
|
|
141
|
+
FileUtils.chmod(0o755, hook_path)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Registers the hook in settings.json if not already present.
|
|
145
|
+
#
|
|
146
|
+
# @param hook_path [Pathname]
|
|
147
|
+
# @param scope [String]
|
|
148
|
+
# @return [Hash] diff info
|
|
149
|
+
def register_in_settings(hook_path, scope)
|
|
150
|
+
settings_path = resolve_settings_path(scope)
|
|
151
|
+
return { updated: false, reason: 'settings file not found' } unless settings_path.exist?
|
|
152
|
+
|
|
153
|
+
settings = JSON.parse(settings_path.read)
|
|
154
|
+
hooks = settings['hooks'] ||= {}
|
|
155
|
+
pre_tool = hooks['PreToolUse'] ||= []
|
|
156
|
+
|
|
157
|
+
hook_entry = build_hook_entry(hook_path)
|
|
158
|
+
|
|
159
|
+
if pre_tool.any? { |h| h['command'] == hook_entry['command'] }
|
|
160
|
+
return { updated: false, reason: 'hook already registered' }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
pre_tool << hook_entry
|
|
164
|
+
File.write(settings_path, JSON.pretty_generate(settings))
|
|
165
|
+
{ updated: true, settings_path: settings_path.to_s }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# @param scope [String]
|
|
169
|
+
# @return [Pathname]
|
|
170
|
+
def resolve_settings_path(scope)
|
|
171
|
+
if scope == 'project'
|
|
172
|
+
RosettAi.context.project_root.join('.claude', 'settings.json')
|
|
173
|
+
else
|
|
174
|
+
RosettAi.paths.global_dir.join('settings.json')
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @param hook_path [Pathname]
|
|
179
|
+
# @return [Hash]
|
|
180
|
+
def build_hook_entry(hook_path)
|
|
181
|
+
{
|
|
182
|
+
'type' => 'command',
|
|
183
|
+
'command' => "ruby #{hook_path}",
|
|
184
|
+
'description' => 'Rosett-AI enforcement hook'
|
|
185
|
+
}
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|