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,92 @@
|
|
|
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 'json_schemer'
|
|
8
|
+
|
|
9
|
+
module RosettAi
|
|
10
|
+
module Packaging
|
|
11
|
+
# Loads and validates packaging variant configs from conf/packaging/<name>.yml.
|
|
12
|
+
#
|
|
13
|
+
# Each variant declares a Debian package with its metadata, system
|
|
14
|
+
# dependencies, and package dependencies. The special version keyword
|
|
15
|
+
# "current" resolves to ">= #{RosettAi::VERSION}" at load time, ensuring
|
|
16
|
+
# atomic version updates per lifecycle_management.yml constraints.
|
|
17
|
+
class VariantConfig
|
|
18
|
+
FIELDS = [
|
|
19
|
+
:name, :variant, :description, :ui_adapter,
|
|
20
|
+
:system_dependencies, :package_dependencies
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
attr_reader(*FIELDS)
|
|
24
|
+
|
|
25
|
+
def initialize(data)
|
|
26
|
+
FIELDS.each do |field|
|
|
27
|
+
instance_variable_set(:"@#{field}", data[field.to_s])
|
|
28
|
+
end
|
|
29
|
+
@system_dependencies ||= []
|
|
30
|
+
@package_dependencies ||= []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.load(variant_name)
|
|
34
|
+
path = variants_dir.join("#{variant_name}.yml")
|
|
35
|
+
unless path.exist?
|
|
36
|
+
raise RosettAi::ConfigurationError,
|
|
37
|
+
"Unknown variant: #{variant_name}. Available: #{available.join(', ')}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
data = RosettAi::YamlLoader.load_file(path)
|
|
41
|
+
validate!(data, path)
|
|
42
|
+
new(data)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.available
|
|
46
|
+
Dir.glob(variants_dir.join('*.yml')).map do |file|
|
|
47
|
+
File.basename(file, '.yml')
|
|
48
|
+
end.sort
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.variants_dir
|
|
52
|
+
RosettAi.root.join('conf', 'packaging')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def fpm_depends
|
|
56
|
+
deps = system_dependencies.dup
|
|
57
|
+
package_dependencies.each do |dep|
|
|
58
|
+
version = resolve_version(dep['version'])
|
|
59
|
+
deps << "#{dep['name']} (#{version})"
|
|
60
|
+
end
|
|
61
|
+
deps
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def depends_on_core?
|
|
65
|
+
package_dependencies.any? { |dep| dep['name'] == 'rosett-ai' }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.validate!(data, path)
|
|
69
|
+
schema_path = RosettAi.root.join('conf', 'schemas', 'packaging_schema.json')
|
|
70
|
+
schema = JSON.parse(schema_path.read)
|
|
71
|
+
schemer = JSONSchemer.schema(schema)
|
|
72
|
+
errors = schemer.validate(data).to_a
|
|
73
|
+
return if errors.empty?
|
|
74
|
+
|
|
75
|
+
messages = errors.map do |err|
|
|
76
|
+
pointer = err['data_pointer'].empty? ? 'root' : err['data_pointer']
|
|
77
|
+
"#{pointer}: #{err['type']}"
|
|
78
|
+
end
|
|
79
|
+
raise RosettAi::ConfigurationError, "Invalid variant config #{path}: #{messages.join(', ')}"
|
|
80
|
+
end
|
|
81
|
+
private_class_method :validate!
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def resolve_version(constraint)
|
|
86
|
+
return ">= #{RosettAi::VERSION}" if constraint == 'current'
|
|
87
|
+
|
|
88
|
+
constraint
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
# AI-Co-Author: Claude Opus 4.6 (Anthropic) <noreply@anthropic.com>
|
|
6
|
+
|
|
7
|
+
module RosettAi
|
|
8
|
+
# Resolves filesystem paths for rai configuration directories.
|
|
9
|
+
# Uses instance methods for future multi-engine support (ADR-006).
|
|
10
|
+
class PathResolver
|
|
11
|
+
attr_reader :engine
|
|
12
|
+
|
|
13
|
+
# Create a new path resolver for the given engine.
|
|
14
|
+
#
|
|
15
|
+
# @param engine [Symbol] engine name
|
|
16
|
+
# @return [PathResolver]
|
|
17
|
+
def initialize(engine: :claude)
|
|
18
|
+
@engine = engine
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Engine-specific paths (upstream convention)
|
|
22
|
+
|
|
23
|
+
# Global configuration directory.
|
|
24
|
+
# @return [Pathname] ~/.claude/
|
|
25
|
+
def global_dir = Pathname.new(File.expand_path('~/.claude'))
|
|
26
|
+
|
|
27
|
+
# Compiled rules output directory.
|
|
28
|
+
# @return [Pathname] ~/.claude/rules/
|
|
29
|
+
def rules_dir = global_dir.join('rules')
|
|
30
|
+
|
|
31
|
+
# Project-local configuration directory.
|
|
32
|
+
# @return [Pathname] .claude/ in the user's working directory
|
|
33
|
+
def local_dir = Pathname.new(user_pwd).join('.claude')
|
|
34
|
+
|
|
35
|
+
# Project-level CLAUDE.md file.
|
|
36
|
+
# @return [Pathname] CLAUDE.md in the user's working directory
|
|
37
|
+
def project_claude_md = Pathname.new(user_pwd).join('CLAUDE.md')
|
|
38
|
+
|
|
39
|
+
# Rosett-AI domain (XDG-compliant)
|
|
40
|
+
|
|
41
|
+
# XDG-compliant rai configuration directory.
|
|
42
|
+
# @return [Pathname] $XDG_CONFIG_HOME/rosett-ai or ~/.config/rosett-ai
|
|
43
|
+
def rai_config_dir
|
|
44
|
+
xdg = ENV.fetch('XDG_CONFIG_HOME', File.join(Dir.home, '.config'))
|
|
45
|
+
Pathname.new(xdg).join('rosett-ai')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# XDG-compliant rai behaviour/design conf directory.
|
|
49
|
+
# This is where user-level behaviours live — the global compile source.
|
|
50
|
+
# @return [Pathname] $XDG_CONFIG_HOME/rosett-ai/conf or ~/.config/rosett-ai/conf
|
|
51
|
+
def rai_conf_dir = rai_config_dir.join('conf')
|
|
52
|
+
|
|
53
|
+
# Packaged defaults directory (from .deb install).
|
|
54
|
+
# @return [Pathname] /opt/rosett-ai/app/conf
|
|
55
|
+
def packaged_conf_dir = Pathname.new('/opt/rosett-ai/app/conf')
|
|
56
|
+
|
|
57
|
+
# Encrypted secrets store.
|
|
58
|
+
# @return [Pathname] secrets.yml inside the rosett-ai config directory
|
|
59
|
+
def secrets_file = rai_config_dir.join('secrets.yml')
|
|
60
|
+
|
|
61
|
+
# Ed25519-signed JWT license key file.
|
|
62
|
+
# @return [Pathname] license.key inside the rosett-ai config directory
|
|
63
|
+
def license_file = rai_config_dir.join('license.key')
|
|
64
|
+
|
|
65
|
+
# Premium content packs directory.
|
|
66
|
+
# @return [Pathname] premium/ inside the rosett-ai config directory
|
|
67
|
+
def premium_content_dir = rai_config_dir.join('premium')
|
|
68
|
+
|
|
69
|
+
# Content pack cache directory.
|
|
70
|
+
# @return [Pathname] cache/content/ inside the rosett-ai config directory
|
|
71
|
+
def content_cache_dir = rai_config_dir.join('cache', 'content')
|
|
72
|
+
|
|
73
|
+
# Engine settings target paths
|
|
74
|
+
|
|
75
|
+
# System-wide managed settings (enterprise).
|
|
76
|
+
# @return [Pathname] /etc/claude-code/managed-settings.json
|
|
77
|
+
def managed_settings_path
|
|
78
|
+
Pathname.new('/etc/claude-code/managed-settings.json')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Global user settings file.
|
|
82
|
+
# @return [Pathname] settings.json inside ~/.claude/
|
|
83
|
+
def user_settings_path = global_dir.join('settings.json')
|
|
84
|
+
|
|
85
|
+
# Project-level settings file.
|
|
86
|
+
# @return [Pathname] settings.json inside .claude/
|
|
87
|
+
def project_settings_path = local_dir.join('settings.json')
|
|
88
|
+
|
|
89
|
+
# Local settings override (gitignored).
|
|
90
|
+
# @return [Pathname] settings.local.json inside .claude/
|
|
91
|
+
def local_settings_path = local_dir.join('settings.local.json')
|
|
92
|
+
|
|
93
|
+
# Claude engine configuration source directory.
|
|
94
|
+
# @return [Pathname] conf/engines/claude/config/ relative to Rosett-AI root
|
|
95
|
+
def claude_code_config_dir = RosettAi.root.join('conf', 'engines', 'claude', 'config')
|
|
96
|
+
|
|
97
|
+
# Tilde-form display path for lockfile output.
|
|
98
|
+
# @return [String] ~/.claude/rules
|
|
99
|
+
def rules_display_path = '~/.claude/rules'
|
|
100
|
+
|
|
101
|
+
# The user's original working directory.
|
|
102
|
+
# When running from the .deb wrapper, Dir.pwd is /opt/rosett-ai/app, so
|
|
103
|
+
# RAI_ORIGINAL_PWD (set by the wrapper) preserves the real directory.
|
|
104
|
+
# @return [String] user's working directory
|
|
105
|
+
def user_pwd
|
|
106
|
+
ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Memoized default instance for single-engine usage.
|
|
110
|
+
# @return [PathResolver] instance with engine set to :claude
|
|
111
|
+
def self.default
|
|
112
|
+
@default ||= new
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
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 Plugins
|
|
8
|
+
# Base contract module shared by all plugin types (engines, GUIs, MCPs).
|
|
9
|
+
#
|
|
10
|
+
# Every Rosett-AI plugin gem must include this module (or a type-specific
|
|
11
|
+
# subcontract that itself includes Contract) and implement the required
|
|
12
|
+
# class methods. The registry uses these methods for discovery,
|
|
13
|
+
# display, and version negotiation.
|
|
14
|
+
module Contract
|
|
15
|
+
def self.included(base)
|
|
16
|
+
base.extend(ClassMethods)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Class-level interface that every plugin must implement.
|
|
20
|
+
module ClassMethods
|
|
21
|
+
# @return [Symbol] one of :engine, :gui, :mcp
|
|
22
|
+
def plugin_type
|
|
23
|
+
raise NotImplementedError, "#{self}#plugin_type must return :engine, :gui, or :mcp"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [String] machine-readable plugin name (e.g. 'claude', 'gtk4')
|
|
27
|
+
def plugin_name
|
|
28
|
+
raise NotImplementedError, "#{self}#plugin_name must return the plugin identifier"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [String] human-readable display name (e.g. 'ACME AI Tool (Example)')
|
|
32
|
+
def display_name
|
|
33
|
+
raise NotImplementedError, "#{self}#display_name must return a human-readable name"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [String] SemVer version string
|
|
37
|
+
def version
|
|
38
|
+
raise NotImplementedError, "#{self}#version must return a SemVer string"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Plugins
|
|
8
|
+
# Contract module for engine plugins.
|
|
9
|
+
#
|
|
10
|
+
# Engine plugins compile behaviour YAML into tool-specific formats,
|
|
11
|
+
# detect installed AI tools, execute API calls, and compile
|
|
12
|
+
# tool-native configuration.
|
|
13
|
+
#
|
|
14
|
+
# Include this module in your engine's registration module and
|
|
15
|
+
# implement all required class methods.
|
|
16
|
+
module EngineContract
|
|
17
|
+
def self.included(base)
|
|
18
|
+
base.include(Contract)
|
|
19
|
+
base.extend(ClassMethods)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Engine-specific class methods beyond the base Contract.
|
|
23
|
+
module ClassMethods
|
|
24
|
+
def plugin_type = :engine
|
|
25
|
+
|
|
26
|
+
# @return [String] engine identifier (e.g. 'claude', 'goose')
|
|
27
|
+
def engine_name
|
|
28
|
+
raise NotImplementedError, "#{self}#engine_name must return the engine identifier"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Class] the Backend subclass for rule compilation
|
|
32
|
+
def backend_class
|
|
33
|
+
raise NotImplementedError, "#{self}#backend_class must return the backend class"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Class, nil] optional Detector subclass for system probing
|
|
37
|
+
def detector_class = nil
|
|
38
|
+
|
|
39
|
+
# @return [Class, nil] optional Executor subclass for API calls
|
|
40
|
+
def executor_class = nil
|
|
41
|
+
|
|
42
|
+
# @return [Class, nil] optional ConfigCompiler subclass for settings compilation
|
|
43
|
+
def config_compiler_class = nil
|
|
44
|
+
|
|
45
|
+
# @return [Pathname] path to the engine's manifest.yml
|
|
46
|
+
def manifest_path
|
|
47
|
+
raise NotImplementedError, "#{self}#manifest_path must return the manifest path"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @return [Pathname] path to the engine's target profile YAML
|
|
51
|
+
def target_profile_path
|
|
52
|
+
manifest_path.dirname.join('target.yml')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Convenience alias — delegates to plugin_name for Contract compatibility.
|
|
56
|
+
def plugin_name = engine_name
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Plugins
|
|
8
|
+
# Contract module for GUI plugins.
|
|
9
|
+
#
|
|
10
|
+
# GUI plugins provide desktop interfaces (GTK4, Qt6, etc.) that
|
|
11
|
+
# communicate with Rosett-AI core via D-Bus IPC.
|
|
12
|
+
#
|
|
13
|
+
# Include this module in your GUI's registration module and
|
|
14
|
+
# implement all required class methods.
|
|
15
|
+
#
|
|
16
|
+
# @author hugo
|
|
17
|
+
# @author claude
|
|
18
|
+
module GuiContract
|
|
19
|
+
def self.included(base)
|
|
20
|
+
base.include(Contract)
|
|
21
|
+
base.extend(ClassMethods)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# GUI-specific class methods beyond the base Contract.
|
|
25
|
+
module ClassMethods
|
|
26
|
+
def plugin_type = :gui
|
|
27
|
+
|
|
28
|
+
# @return [String] GUI identifier (e.g. 'gtk4', 'qt6')
|
|
29
|
+
def gui_name
|
|
30
|
+
raise NotImplementedError, "#{self}#gui_name must return the GUI identifier"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Symbol] toolkit identifier (:gtk4, :qt6, etc.)
|
|
34
|
+
def toolkit
|
|
35
|
+
raise NotImplementedError, "#{self}#toolkit must return a toolkit symbol"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Class] the main application class
|
|
39
|
+
def application_class
|
|
40
|
+
raise NotImplementedError, "#{self}#application_class must return the application class"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check if system dependencies for this GUI are available.
|
|
44
|
+
#
|
|
45
|
+
# @return [Boolean]
|
|
46
|
+
def available?
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Launch the GUI application.
|
|
51
|
+
#
|
|
52
|
+
# @param argv [Array<String>] command-line arguments
|
|
53
|
+
# @return [Integer] exit code
|
|
54
|
+
def launch(argv)
|
|
55
|
+
app = application_class.new
|
|
56
|
+
app.run(argv)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# List system packages required by this GUI.
|
|
60
|
+
#
|
|
61
|
+
# @return [Array<String>] package names (e.g. ['libgtk-4-1'])
|
|
62
|
+
def required_system_packages
|
|
63
|
+
[]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [Class, nil] optional D-Bus interface class
|
|
67
|
+
def dbus_interface_class = nil
|
|
68
|
+
|
|
69
|
+
# Convenience alias — delegates to plugin_name for Contract compatibility.
|
|
70
|
+
def plugin_name = gui_name
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
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 Plugins
|
|
8
|
+
# Contract module for MCP (Model Context Protocol) plugins.
|
|
9
|
+
#
|
|
10
|
+
# MCP plugins expose rosett-ai functionality as MCP servers with
|
|
11
|
+
# tool and resource definitions.
|
|
12
|
+
#
|
|
13
|
+
# Include this module in your MCP's registration module and
|
|
14
|
+
# implement all required class methods.
|
|
15
|
+
module McpContract
|
|
16
|
+
def self.included(base)
|
|
17
|
+
base.include(Contract)
|
|
18
|
+
base.extend(ClassMethods)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# MCP-specific class methods beyond the base Contract.
|
|
22
|
+
module ClassMethods
|
|
23
|
+
def plugin_type = :mcp
|
|
24
|
+
|
|
25
|
+
# @return [String] MCP server identifier (e.g. 'server')
|
|
26
|
+
def mcp_name
|
|
27
|
+
raise NotImplementedError, "#{self}#mcp_name must return the MCP identifier"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [Class] the MCP server class
|
|
31
|
+
def server_class
|
|
32
|
+
raise NotImplementedError, "#{self}#server_class must return the server class"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Array<Hash>] MCP tool schema definitions
|
|
36
|
+
def tool_definitions
|
|
37
|
+
raise NotImplementedError, "#{self}#tool_definitions must return tool schemas"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Array<Hash>] MCP resource definitions (optional)
|
|
41
|
+
def resource_definitions = []
|
|
42
|
+
|
|
43
|
+
# Convenience alias — delegates to plugin_name for Contract compatibility.
|
|
44
|
+
def plugin_name = mcp_name
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
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 Plugins
|
|
8
|
+
# Dynamic, multi-type plugin registry.
|
|
9
|
+
#
|
|
10
|
+
# Plugins self-register via `RosettAi::Plugins::Registry.register(:engine, 'claude', MyModule)`.
|
|
11
|
+
# External plugin gems are discovered via `Gem.find_files` — install a gem,
|
|
12
|
+
# and it becomes available automatically.
|
|
13
|
+
#
|
|
14
|
+
# Replaces the hardcoded ENGINES and BACKENDS maps from
|
|
15
|
+
# RosettAi::Engines::Registry and RosettAi::Compiler::Backend.
|
|
16
|
+
class Registry
|
|
17
|
+
PLUGIN_TYPES = [:engine, :gui, :mcp].freeze
|
|
18
|
+
|
|
19
|
+
class PluginNotFoundError < RosettAi::Error; end
|
|
20
|
+
|
|
21
|
+
@plugins = Hash.new { |h, k| h[k] = {} }
|
|
22
|
+
@discovered = false
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
# Register a plugin module under a type and name.
|
|
26
|
+
#
|
|
27
|
+
# @param type [Symbol] one of :engine, :gui, :mcp
|
|
28
|
+
# @param name [String, Symbol] plugin identifier
|
|
29
|
+
# @param plugin_module [Module] the module including the appropriate contract
|
|
30
|
+
def register(type, name, plugin_module)
|
|
31
|
+
validate_type!(type)
|
|
32
|
+
@plugins[type][name.to_s] = plugin_module
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @param type [Symbol] plugin type
|
|
36
|
+
# @return [Array<String>] sorted list of registered plugin names
|
|
37
|
+
def available(type)
|
|
38
|
+
validate_type!(type)
|
|
39
|
+
@plugins[type].keys.sort
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @param type [Symbol] plugin type
|
|
43
|
+
# @param name [String, Symbol] plugin identifier
|
|
44
|
+
# @return [Module] the registered plugin module
|
|
45
|
+
# @raise [PluginNotFoundError] if not found
|
|
46
|
+
def plugin_module(type, name)
|
|
47
|
+
validate_type!(type)
|
|
48
|
+
@plugins[type].fetch(name.to_s) do
|
|
49
|
+
raise PluginNotFoundError,
|
|
50
|
+
"Unknown #{type} plugin: #{name}. Available: #{available(type).join(', ')}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param type [Symbol] plugin type
|
|
55
|
+
# @param name [String, Symbol] plugin identifier
|
|
56
|
+
# @return [Boolean]
|
|
57
|
+
def registered?(type, name)
|
|
58
|
+
validate_type!(type)
|
|
59
|
+
@plugins[type].key?(name.to_s)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Convenience accessors for each plugin type.
|
|
63
|
+
def engines = @plugins[:engine]
|
|
64
|
+
def guis = @plugins[:gui]
|
|
65
|
+
def mcps = @plugins[:mcp]
|
|
66
|
+
|
|
67
|
+
# Discover and load all installed plugin gems.
|
|
68
|
+
#
|
|
69
|
+
# Scans for `rosett_ai_engine/*/register.rb`, `rosett_ai_gui/*/register.rb`,
|
|
70
|
+
# and `rosett_ai_mcp/*/register.rb` across all gem load paths.
|
|
71
|
+
# Each register.rb is expected to call `Registry.register`.
|
|
72
|
+
def discover!
|
|
73
|
+
return if @discovered
|
|
74
|
+
|
|
75
|
+
discover_type('rosett_ai_engine')
|
|
76
|
+
discover_type('rosett_ai_gui')
|
|
77
|
+
discover_type('rosett_ai_mcp')
|
|
78
|
+
@discovered = true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Reset the registry. Intended for testing only.
|
|
82
|
+
def reset!
|
|
83
|
+
@plugins = Hash.new { |h, k| h[k] = {} }
|
|
84
|
+
@discovered = false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def validate_type!(type)
|
|
90
|
+
return if PLUGIN_TYPES.include?(type)
|
|
91
|
+
|
|
92
|
+
raise ArgumentError, "Invalid plugin type: #{type}. Valid: #{PLUGIN_TYPES.join(', ')}"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def discover_type(prefix)
|
|
96
|
+
# 1. Standard gem discovery (works when GEM_PATH matches Ruby ABI)
|
|
97
|
+
Gem.find_files("#{prefix}/*/register.rb").each do |path|
|
|
98
|
+
require path
|
|
99
|
+
rescue LoadError => e
|
|
100
|
+
RosettAi.logger.warn("Failed to load plugin from #{path}: #{e.message}")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# 2. Fallback: version-agnostic scan of embedded gem tree
|
|
104
|
+
# Covers Ruby ABI mismatch between embedded Ruby and engine gems
|
|
105
|
+
discover_embedded_gems(prefix)
|
|
106
|
+
|
|
107
|
+
# 3. Custom engine path (dev/testing via RAI_ENGINE_PATH)
|
|
108
|
+
discover_custom_path(prefix)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Scan `/opt/rosett-ai/embedded/lib/ruby/gems/*/gems/*/lib/` for plugin
|
|
112
|
+
# register.rb files. Handles Ruby ABI version mismatches between
|
|
113
|
+
# the embedded Ruby and engine gems built with a different minor version.
|
|
114
|
+
#
|
|
115
|
+
# @param prefix [String] plugin namespace prefix (e.g. 'rosett_ai_engine')
|
|
116
|
+
def discover_embedded_gems(prefix)
|
|
117
|
+
embedded_gems = Pathname.new('/opt/rosett-ai/embedded/lib/ruby/gems')
|
|
118
|
+
return unless embedded_gems.directory?
|
|
119
|
+
|
|
120
|
+
Dir.glob(embedded_gems.join('*', 'gems', '*', 'lib', prefix, '*', 'register.rb').to_s).each do |path|
|
|
121
|
+
lib_dir = File.expand_path('../../..', path)
|
|
122
|
+
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
|
|
123
|
+
require path
|
|
124
|
+
rescue LoadError => e
|
|
125
|
+
RosettAi.logger.warn("Failed to load embedded plugin from #{path}: #{e.message}")
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Scan directories listed in `RAI_ENGINE_PATH` (colon-separated)
|
|
130
|
+
# for plugin register.rb files. Useful for development and testing.
|
|
131
|
+
#
|
|
132
|
+
# @param prefix [String] plugin namespace prefix (e.g. 'rosett_ai_engine')
|
|
133
|
+
def discover_custom_path(prefix)
|
|
134
|
+
custom_path = ENV.fetch('RAI_ENGINE_PATH', nil)
|
|
135
|
+
return unless custom_path
|
|
136
|
+
|
|
137
|
+
custom_path.split(':').each do |dir|
|
|
138
|
+
Dir.glob(File.join(dir, 'lib', prefix, '*', 'register.rb')).each do |path|
|
|
139
|
+
lib_dir = File.expand_path('../../..', path)
|
|
140
|
+
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
|
|
141
|
+
require path
|
|
142
|
+
rescue LoadError => e
|
|
143
|
+
RosettAi.logger.warn("Failed to load custom plugin from #{path}: #{e.message}")
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
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 Policy
|
|
8
|
+
# Generates compliance audit reports for the current project.
|
|
9
|
+
#
|
|
10
|
+
# Audit results include deny list coverage, protected files,
|
|
11
|
+
# opt-out markers, and tier hierarchy validation.
|
|
12
|
+
class Auditor
|
|
13
|
+
# @param deny_list [DenyList] active deny list
|
|
14
|
+
# @param protected_files [ProtectedFiles] active protected files
|
|
15
|
+
# @param opt_out_scanner [OptOutScanner] scanner instance
|
|
16
|
+
def initialize(deny_list:, protected_files:, opt_out_scanner: OptOutScanner.new)
|
|
17
|
+
@deny_list = deny_list
|
|
18
|
+
@protected_files = protected_files
|
|
19
|
+
@opt_out_scanner = opt_out_scanner
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Generates an audit report.
|
|
23
|
+
#
|
|
24
|
+
# @param project_files [Array<String>] list of project files to audit
|
|
25
|
+
# @return [Hash] audit report
|
|
26
|
+
def audit(project_files: [])
|
|
27
|
+
denied = project_files.select { |path| @deny_list.denied?(path) }
|
|
28
|
+
opted_out = @opt_out_scanner.scan(project_files)
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
deny_list_patterns: @deny_list.size,
|
|
32
|
+
denied_files: denied.size,
|
|
33
|
+
protected_files: @protected_files.size,
|
|
34
|
+
opted_out_files: opted_out.size,
|
|
35
|
+
opted_out_paths: opted_out,
|
|
36
|
+
compliant: true
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|