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,59 @@
|
|
|
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 Doctor
|
|
8
|
+
module Checks
|
|
9
|
+
# Warns when a stale .rosett-ai/ directory exists at $HOME level.
|
|
10
|
+
#
|
|
11
|
+
# Having .rosett-ai/ at $HOME is almost never intentional — it is typically
|
|
12
|
+
# left over from a previous `rai init --project` run at the home
|
|
13
|
+
# directory. It causes scope resolution confusion because behaviour
|
|
14
|
+
# files in ~/.rosett-ai/conf/behaviour/ shadow XDG-path files.
|
|
15
|
+
#
|
|
16
|
+
# @author hugo
|
|
17
|
+
# @author claude
|
|
18
|
+
class StaleHomeNnccCheck
|
|
19
|
+
include Check
|
|
20
|
+
|
|
21
|
+
check_name 'stale_home_nncc'
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def perform
|
|
26
|
+
home_rai = Pathname.new(Dir.home).join('.rosett-ai')
|
|
27
|
+
|
|
28
|
+
unless home_rai.directory?
|
|
29
|
+
pass!(::I18n.t('rosett_ai.doctor.stale_home_nncc.clean'))
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
warn_stale(home_rai)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def warn_stale(home_rai)
|
|
37
|
+
path_str = home_rai.to_s
|
|
38
|
+
removal = ::I18n.t('rosett_ai.doctor.stale_home_nncc.remove', path: path_str)
|
|
39
|
+
behaviour_dir = home_rai.join('conf', 'behaviour')
|
|
40
|
+
children = behaviour_dir.directory? ? behaviour_dir.children : []
|
|
41
|
+
|
|
42
|
+
if children.any?
|
|
43
|
+
warn!(
|
|
44
|
+
::I18n.t('rosett_ai.doctor.stale_home_nncc.found', path: path_str, count: children.count),
|
|
45
|
+
remediation: removal
|
|
46
|
+
)
|
|
47
|
+
else
|
|
48
|
+
warn!(
|
|
49
|
+
::I18n.t('rosett_ai.doctor.stale_home_nncc.empty', path: path_str),
|
|
50
|
+
remediation: removal
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
RosettAi::Doctor.register(RosettAi::Doctor::Checks::StaleHomeNnccCheck)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
# Self-diagnostic runner for Rosett-AI runtime prerequisites.
|
|
8
|
+
#
|
|
9
|
+
# Orchestrates registered checks and collects results.
|
|
10
|
+
# Each check implements the Check interface (#name, #run, #status,
|
|
11
|
+
# #message, #remediation).
|
|
12
|
+
#
|
|
13
|
+
# @author hugo
|
|
14
|
+
# @author claude
|
|
15
|
+
# @see conf/design/doctor.yml
|
|
16
|
+
module Doctor
|
|
17
|
+
STATUSES = ['pass', 'warn', 'fail'].freeze
|
|
18
|
+
|
|
19
|
+
@checks = []
|
|
20
|
+
@mutex = Mutex.new
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# Register a check class.
|
|
24
|
+
#
|
|
25
|
+
# @param check_class [Class] a class that includes Doctor::Check
|
|
26
|
+
# @return [void]
|
|
27
|
+
def register(check_class)
|
|
28
|
+
@mutex.synchronize { @checks << check_class }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Return all registered check classes.
|
|
32
|
+
#
|
|
33
|
+
# @return [Array<Class>]
|
|
34
|
+
def checks
|
|
35
|
+
@mutex.synchronize { @checks.dup }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Return the names of all registered checks.
|
|
39
|
+
#
|
|
40
|
+
# @return [Array<String>]
|
|
41
|
+
def check_names
|
|
42
|
+
checks.map(&:check_name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Run all registered checks (or a filtered subset).
|
|
46
|
+
#
|
|
47
|
+
# @param only [String, nil] run only the named check
|
|
48
|
+
# @return [Array<Hash>] results with :name, :status, :message, :remediation
|
|
49
|
+
def run_all(only: nil)
|
|
50
|
+
targets = only ? checks.select { |klass| klass.check_name == only } : checks
|
|
51
|
+
targets.map do |check_class|
|
|
52
|
+
instance = check_class.new
|
|
53
|
+
instance.run
|
|
54
|
+
{
|
|
55
|
+
name: instance.name,
|
|
56
|
+
status: instance.status.to_s,
|
|
57
|
+
message: instance.message,
|
|
58
|
+
remediation: instance.remediation
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Determine the overall exit code from results.
|
|
64
|
+
#
|
|
65
|
+
# @param results [Array<Hash>] check results
|
|
66
|
+
# @return [Integer] 0=all pass, 1=any warn, 2=any fail
|
|
67
|
+
def exit_code(results)
|
|
68
|
+
return 2 if results.any? { |r| r[:status] == 'fail' }
|
|
69
|
+
return 1 if results.any? { |r| r[:status] == 'warn' }
|
|
70
|
+
|
|
71
|
+
0
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Reset registered checks. Intended for test isolation.
|
|
75
|
+
# @return [void]
|
|
76
|
+
def reset!
|
|
77
|
+
@mutex.synchronize { @checks = [] }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
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 'fileutils'
|
|
7
|
+
require 'pathname'
|
|
8
|
+
|
|
9
|
+
module RosettAi
|
|
10
|
+
module Documentation
|
|
11
|
+
# Compiles the LaTeX technical reference document.
|
|
12
|
+
#
|
|
13
|
+
# Runs the full pdflatex → biber → makeindex → pdflatex toolchain
|
|
14
|
+
# to produce doc/reference/rosett-ai-technical-reference.pdf from the
|
|
15
|
+
# LaTeX sources in doc/reference/src/.
|
|
16
|
+
#
|
|
17
|
+
# Design reference: conf/design/documentation.yml
|
|
18
|
+
class ReferenceCompiler
|
|
19
|
+
REQUIRED_TOOLS = ['pdflatex', 'biber', 'makeindex'].freeze
|
|
20
|
+
OUTPUT_NAME = 'rosett-ai-technical-reference.pdf'
|
|
21
|
+
BUILD_ARTIFACTS = ['aux', 'bbl', 'bcf', 'blg', 'idx', 'ilg', 'ind',
|
|
22
|
+
'lof', 'log', 'lot', 'out', 'ptc', 'run.xml', 'toc'].freeze
|
|
23
|
+
|
|
24
|
+
def initialize(root: nil)
|
|
25
|
+
@root = root || RosettAi.root
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def available?
|
|
29
|
+
REQUIRED_TOOLS.all? { |tool| tool_on_path?(tool) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def missing_tools
|
|
33
|
+
REQUIRED_TOOLS.reject { |tool| tool_on_path?(tool) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def stale?
|
|
37
|
+
pdf = output_path
|
|
38
|
+
return true unless pdf.exist?
|
|
39
|
+
|
|
40
|
+
pdf_mtime = pdf.mtime
|
|
41
|
+
source_files.any? { |f| f.mtime > pdf_mtime }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def compile!
|
|
45
|
+
raise RosettAi::DocumentationError, "Missing LaTeX tools: #{missing_tools.join(', ')}" unless available?
|
|
46
|
+
raise RosettAi::DocumentationError, "Source directory not found: #{source_dir}" unless source_dir.exist?
|
|
47
|
+
|
|
48
|
+
Dir.chdir(source_dir) do
|
|
49
|
+
run_pdflatex!
|
|
50
|
+
run_biber!
|
|
51
|
+
run_makeindex
|
|
52
|
+
3.times { run_pdflatex! }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
install_pdf!
|
|
56
|
+
clean_artifacts!
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def output_path
|
|
60
|
+
@root.join('doc', 'reference', OUTPUT_NAME)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def source_dir
|
|
64
|
+
@root.join('doc', 'reference', 'src')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def source_files
|
|
70
|
+
patterns = [
|
|
71
|
+
@root.join('conf', 'design', '*.yml'),
|
|
72
|
+
@root.join('conf', 'schemas', '*.json'),
|
|
73
|
+
source_dir.join('*.tex'),
|
|
74
|
+
source_dir.join('*.bib'),
|
|
75
|
+
source_dir.join('*.ist')
|
|
76
|
+
]
|
|
77
|
+
patterns.flat_map { |pat| Dir.glob(pat).map { |f| Pathname.new(f) } }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def run_pdflatex!
|
|
81
|
+
success = system('pdflatex', '-interaction=nonstopmode', 'main.tex',
|
|
82
|
+
[:out, :err] => File::NULL)
|
|
83
|
+
raise RosettAi::DocumentationError, 'pdflatex compilation failed' unless success
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def run_biber!
|
|
87
|
+
success = system('biber', 'main', [:out, :err] => File::NULL)
|
|
88
|
+
raise RosettAi::DocumentationError, 'biber bibliography processing failed' unless success
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# makeindex failure is non-fatal — the document compiles without an index
|
|
92
|
+
def run_makeindex
|
|
93
|
+
idx_file = source_dir.join('main.idx')
|
|
94
|
+
return unless idx_file.exist?
|
|
95
|
+
|
|
96
|
+
system('makeindex', 'main.idx', '-s', 'StyleInd.ist',
|
|
97
|
+
[:out, :err] => File::NULL)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def install_pdf!
|
|
101
|
+
built_pdf = source_dir.join('main.pdf')
|
|
102
|
+
raise RosettAi::DocumentationError, 'LaTeX compilation produced no PDF' unless built_pdf.exist?
|
|
103
|
+
|
|
104
|
+
FileUtils.mkdir_p(output_path.dirname)
|
|
105
|
+
FileUtils.cp(built_pdf.to_s, output_path.to_s)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def clean_artifacts!
|
|
109
|
+
BUILD_ARTIFACTS.each do |ext|
|
|
110
|
+
FileUtils.rm_f(source_dir.join("main.#{ext}").to_s)
|
|
111
|
+
end
|
|
112
|
+
FileUtils.rm_f(source_dir.join('main.pdf').to_s)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def tool_on_path?(name)
|
|
116
|
+
ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).any? do |dir|
|
|
117
|
+
File.executable?(File.join(dir, name))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
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
|
+
require 'fileutils'
|
|
7
|
+
|
|
8
|
+
module RosettAi
|
|
9
|
+
module Documentation
|
|
10
|
+
# Produces draft translations of documentation files using the Claude API.
|
|
11
|
+
#
|
|
12
|
+
# Reads source documentation, sends to API with a translation prompt,
|
|
13
|
+
# and writes the result to doc/locales/{locale}/.
|
|
14
|
+
class Translator
|
|
15
|
+
MODEL = 'claude-sonnet-4-20250514'
|
|
16
|
+
|
|
17
|
+
def initialize(doc_dir: nil)
|
|
18
|
+
@doc_dir = doc_dir || RosettAi.root.join('doc')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def translate(source_file, from:, to:)
|
|
22
|
+
content = File.read(source_file)
|
|
23
|
+
basename = File.basename(source_file)
|
|
24
|
+
|
|
25
|
+
translated = call_api(content, from: from, to: to)
|
|
26
|
+
|
|
27
|
+
output_dir = @doc_dir.join('locales', to)
|
|
28
|
+
FileUtils.mkdir_p(output_dir)
|
|
29
|
+
output_path = output_dir.join(basename)
|
|
30
|
+
File.open(output_path, 'w', 0o644) { |f| f.write(translated) }
|
|
31
|
+
|
|
32
|
+
output_path.to_s
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def call_api(content, from:, to:)
|
|
38
|
+
require 'anthropic' # lazy require — gem not in core deps
|
|
39
|
+
client = Anthropic::Client.new
|
|
40
|
+
response = client.messages.create(
|
|
41
|
+
model: MODEL,
|
|
42
|
+
max_tokens: 4096,
|
|
43
|
+
messages: [{ role: 'user', content: build_prompt(content, from: from, to: to) }]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
response.content.first.text
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def build_prompt(content, from:, to:)
|
|
50
|
+
<<~PROMPT
|
|
51
|
+
Translate the following documentation from #{from} to #{to}.
|
|
52
|
+
Preserve all markdown formatting, code blocks, and technical terms.
|
|
53
|
+
Do not translate code examples, file paths, or command names.
|
|
54
|
+
Return only the translated document, no explanations.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
#{content}
|
|
58
|
+
PROMPT
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
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
|
+
require 'digest'
|
|
9
|
+
|
|
10
|
+
module RosettAi
|
|
11
|
+
module Engines
|
|
12
|
+
# Abstract base class for engine config compilers.
|
|
13
|
+
#
|
|
14
|
+
# Extracts shared compilation infrastructure from Claude::ConfigCompiler:
|
|
15
|
+
# file discovery, YAML loading, schema validation, checksum diffing,
|
|
16
|
+
# action determination, and result writing.
|
|
17
|
+
#
|
|
18
|
+
# Subclasses must implement:
|
|
19
|
+
# - #config_dir → Pathname to scope YAML directory
|
|
20
|
+
# - #schema_path → Pathname to JSON Schema file
|
|
21
|
+
# - #scope_router → object responding to #target_path(scope)
|
|
22
|
+
# - #transform(data, scope) → [json_data, warnings]
|
|
23
|
+
# - #output_format → Symbol (:json, :yaml)
|
|
24
|
+
class BaseConfigCompiler
|
|
25
|
+
HEADER_KEYS = ['name', 'scope', 'version', 'status', 'compatibility',
|
|
26
|
+
'author', 'created_at', 'modified_at', 'modified_by'].freeze
|
|
27
|
+
|
|
28
|
+
def initialize(simulate: false, secret_resolver: nil)
|
|
29
|
+
@simulate = simulate
|
|
30
|
+
@secret_resolver = secret_resolver || RosettAi::Config::SecretResolver.new
|
|
31
|
+
@schema = load_schema
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Compile all scope files. Returns [RosettAi::Config::CompileResult].
|
|
35
|
+
def compile
|
|
36
|
+
discover_scope_files.map { |path| compile_scope(path) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Compile a single scope file. Returns RosettAi::Config::CompileResult.
|
|
40
|
+
def compile_scope(path)
|
|
41
|
+
warnings = []
|
|
42
|
+
data = load_and_validate(path)
|
|
43
|
+
header = extract_header(data)
|
|
44
|
+
scope = header['scope']
|
|
45
|
+
warnings.concat(check_compatibility(header, path))
|
|
46
|
+
|
|
47
|
+
json_data, transform_warnings = transform(data, scope)
|
|
48
|
+
warnings.concat(transform_warnings)
|
|
49
|
+
|
|
50
|
+
json_data = @secret_resolver.resolve_all(json_data)
|
|
51
|
+
|
|
52
|
+
target = scope_router.target_path(scope)
|
|
53
|
+
action, diff = determine_action(target, json_data)
|
|
54
|
+
|
|
55
|
+
RosettAi::Config::CompileResult.new(
|
|
56
|
+
scope: scope, source_path: Pathname.new(path), target_path: target,
|
|
57
|
+
json_data: json_data, warnings: warnings, action: action, diff: diff
|
|
58
|
+
)
|
|
59
|
+
rescue RosettAi::Error, ArgumentError, JSON::Schema::ValidationError => e
|
|
60
|
+
RosettAi::Config::CompileResult.new(
|
|
61
|
+
scope: extract_scope_from_path(path), source_path: Pathname.new(path),
|
|
62
|
+
target_path: nil, json_data: nil,
|
|
63
|
+
warnings: [e.message], action: :skipped, diff: nil
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Discover scope files in the engine's config directory.
|
|
68
|
+
def discover_scope_files
|
|
69
|
+
dir = config_dir
|
|
70
|
+
return [] unless dir.exist?
|
|
71
|
+
|
|
72
|
+
Dir.glob(dir.join('*.yml'))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Write compiled results to their target paths.
|
|
76
|
+
def write_results(results)
|
|
77
|
+
return results if @simulate
|
|
78
|
+
|
|
79
|
+
results.each do |result|
|
|
80
|
+
next unless result.changed? && result.target_path
|
|
81
|
+
|
|
82
|
+
write_output(result.target_path, result.json_data)
|
|
83
|
+
end
|
|
84
|
+
results
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
protected
|
|
88
|
+
|
|
89
|
+
# Subclass must override: path to scope YAML directory.
|
|
90
|
+
def config_dir
|
|
91
|
+
raise NotImplementedError, "#{self.class}#config_dir must be implemented"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Subclass must override: path to JSON Schema file.
|
|
95
|
+
def schema_path
|
|
96
|
+
raise NotImplementedError, "#{self.class}#schema_path must be implemented"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Subclass must override: scope router instance.
|
|
100
|
+
def scope_router
|
|
101
|
+
raise NotImplementedError, "#{self.class}#scope_router must be implemented"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Subclass must override: transform data into output format.
|
|
105
|
+
# @return [Array(Hash, Array<String>)] [output_data, warnings]
|
|
106
|
+
def transform(_data, _scope)
|
|
107
|
+
raise NotImplementedError, "#{self.class}#transform must be implemented"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Subclass may override: output format (:json or :yaml).
|
|
111
|
+
def output_format = :json
|
|
112
|
+
|
|
113
|
+
# Subclass may override: compatibility checks.
|
|
114
|
+
def check_compatibility(_header, _path)
|
|
115
|
+
[]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def load_schema
|
|
121
|
+
schema_data = JSON.parse(schema_path.read)
|
|
122
|
+
JSONSchemer.schema(schema_data)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def load_and_validate(path)
|
|
126
|
+
data = RosettAi::YamlLoader.load_file(path)
|
|
127
|
+
errors = @schema.validate(data).to_a
|
|
128
|
+
return data if errors.empty?
|
|
129
|
+
|
|
130
|
+
messages = errors.map do |err|
|
|
131
|
+
pointer = err['data_pointer'].empty? ? 'root' : err['data_pointer']
|
|
132
|
+
"#{pointer}: #{err['type']}"
|
|
133
|
+
end
|
|
134
|
+
raise RosettAi::ValidationError, "Schema validation failed for #{path}: #{messages.join(', ')}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def extract_header(data)
|
|
138
|
+
HEADER_KEYS.each_with_object({}) do |key, header|
|
|
139
|
+
header[key] = data[key] if data.key?(key)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def determine_action(target_path, output_data)
|
|
144
|
+
return [:created, nil] unless target_path.exist?
|
|
145
|
+
|
|
146
|
+
existing = File.read(target_path)
|
|
147
|
+
new_content = serialize(output_data)
|
|
148
|
+
|
|
149
|
+
if checksum(existing) == checksum(new_content)
|
|
150
|
+
[:unchanged, nil]
|
|
151
|
+
else
|
|
152
|
+
diff = @simulate ? generate_diff(existing, new_content) : nil
|
|
153
|
+
[:updated, diff]
|
|
154
|
+
end
|
|
155
|
+
rescue Errno::ENOENT
|
|
156
|
+
[:created, nil]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def serialize(data)
|
|
160
|
+
case output_format
|
|
161
|
+
when :yaml then YAML.dump(data)
|
|
162
|
+
else JSON.pretty_generate(data)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def generate_diff(old_content, new_content)
|
|
167
|
+
old_lines = old_content.lines
|
|
168
|
+
new_lines = new_content.lines
|
|
169
|
+
|
|
170
|
+
lines = []
|
|
171
|
+
lines << '--- a/settings'
|
|
172
|
+
lines << '+++ b/settings'
|
|
173
|
+
|
|
174
|
+
max = [old_lines.size, new_lines.size].max
|
|
175
|
+
(0...max).each do |i|
|
|
176
|
+
old_line = old_lines[i]
|
|
177
|
+
new_line = new_lines[i]
|
|
178
|
+
next if old_line == new_line
|
|
179
|
+
|
|
180
|
+
lines << "@@ -#{i + 1} +#{i + 1} @@"
|
|
181
|
+
lines << "-#{old_line&.chomp}" if old_line
|
|
182
|
+
lines << "+#{new_line&.chomp}" if new_line
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
lines.join("\n")
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def checksum(content)
|
|
189
|
+
Digest::SHA256.hexdigest(content)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def write_output(path, data)
|
|
193
|
+
FileUtils.mkdir_p(path.dirname)
|
|
194
|
+
content = serialize(data)
|
|
195
|
+
File.open(path, 'w', 0o644) { |f| f.write(content) }
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def extract_scope_from_path(path)
|
|
199
|
+
File.basename(path, '.yml')
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
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 Engines
|
|
8
|
+
# Base engine detector that reads manifest detection rules and probes the system.
|
|
9
|
+
#
|
|
10
|
+
# Subclasses may override `custom_checks` to add engine-specific detection logic
|
|
11
|
+
# beyond the manifest-driven cli_binary and config_dir checks.
|
|
12
|
+
class Detector
|
|
13
|
+
attr_reader :engine_name
|
|
14
|
+
|
|
15
|
+
def initialize(engine_name)
|
|
16
|
+
@engine_name = engine_name.to_s
|
|
17
|
+
@manifest = Registry.manifest(@engine_name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def detect
|
|
21
|
+
checks = []
|
|
22
|
+
detection = @manifest['detection'] || {}
|
|
23
|
+
|
|
24
|
+
checks << check_cli_binary(detection['cli_binary']) if detection['cli_binary']
|
|
25
|
+
checks << check_config_dir(detection['config_dir']) if detection['config_dir']
|
|
26
|
+
checks.concat(custom_checks)
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
engine: @engine_name,
|
|
30
|
+
display_name: @manifest['display_name'],
|
|
31
|
+
checks: checks,
|
|
32
|
+
detected: checks.all? { |check| check[:passed] }
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
|
|
38
|
+
def custom_checks
|
|
39
|
+
[]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def check_cli_binary(binary_name)
|
|
45
|
+
found = system('which', binary_name, out: File::NULL, err: File::NULL)
|
|
46
|
+
{
|
|
47
|
+
type: :cli_binary,
|
|
48
|
+
name: binary_name,
|
|
49
|
+
passed: found == true
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_config_dir(dir_path)
|
|
54
|
+
expanded = File.expand_path(dir_path)
|
|
55
|
+
{
|
|
56
|
+
type: :config_dir,
|
|
57
|
+
name: dir_path,
|
|
58
|
+
passed: Dir.exist?(expanded)
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
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_schemer'
|
|
7
|
+
|
|
8
|
+
module RosettAi
|
|
9
|
+
module Engines
|
|
10
|
+
# Engine registry — delegates to RosettAi::Plugins::Registry for dynamic
|
|
11
|
+
# plugin discovery. External engine gems self-register via their
|
|
12
|
+
# register.rb files, discovered by Gem.find_files.
|
|
13
|
+
class Registry
|
|
14
|
+
class << self
|
|
15
|
+
def available
|
|
16
|
+
RosettAi::Plugins::Registry.available(:engine)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def engine_module(name)
|
|
20
|
+
RosettAi::Plugins::Registry.plugin_module(:engine, name.to_s)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def manifest(name)
|
|
24
|
+
mod = engine_module(name)
|
|
25
|
+
path = mod.manifest_path
|
|
26
|
+
raise RosettAi::Error, "No manifest for engine: #{name}" unless path.exist?
|
|
27
|
+
|
|
28
|
+
data = RosettAi::YamlLoader.load_file(path)
|
|
29
|
+
validate_manifest!(data, path)
|
|
30
|
+
data
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def validate_manifest!(data, path)
|
|
36
|
+
schema_path = RosettAi.root.join('conf', 'schemas', 'engine_manifest_schema.json')
|
|
37
|
+
schema = JSON.parse(schema_path.read)
|
|
38
|
+
schemer = JSONSchemer.schema(schema)
|
|
39
|
+
errors = schemer.validate(data).to_a
|
|
40
|
+
|
|
41
|
+
return if errors.empty?
|
|
42
|
+
|
|
43
|
+
messages = errors.map { |e| "#{e['data_pointer']}: #{e['type']}" }
|
|
44
|
+
raise RosettAi::Error,
|
|
45
|
+
"Invalid engine manifest at #{path}: #{messages.join(', ')}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|