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,415 @@
|
|
|
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 'thor'
|
|
7
|
+
require 'terminal-table'
|
|
8
|
+
require 'rainbow'
|
|
9
|
+
require 'tty-spinner'
|
|
10
|
+
require 'fileutils'
|
|
11
|
+
require 'yaml'
|
|
12
|
+
|
|
13
|
+
module RosettAi
|
|
14
|
+
module Thor
|
|
15
|
+
module Tasks
|
|
16
|
+
# Context for file compilation operations
|
|
17
|
+
CompileFileContext = Struct.new(:target_path, :content, :checksum, :filename)
|
|
18
|
+
|
|
19
|
+
# Thor task for compiling YAML configuration into markdown rule files
|
|
20
|
+
class Compile < ::Thor
|
|
21
|
+
default_task :generate
|
|
22
|
+
|
|
23
|
+
desc 'generate', 'Compile YAML configurations into markdown rule files'
|
|
24
|
+
long_desc <<~LONGDESC
|
|
25
|
+
Compiles behaviour and design YAML into engine-specific rule files.
|
|
26
|
+
By default, compiles for the configured default engine to ~/.claude/rules/.
|
|
27
|
+
|
|
28
|
+
Flags:
|
|
29
|
+
--verbose: Show detailed compilation output
|
|
30
|
+
--simulate: Dry run with diffs, no file writes
|
|
31
|
+
--vendor: Write compile.lock.yml lockfile
|
|
32
|
+
--engine NAME: Compile for specific engine
|
|
33
|
+
--strict: Treat warnings as errors
|
|
34
|
+
--locales: Compile locale files to gettext/Qt formats
|
|
35
|
+
|
|
36
|
+
Exit codes: 1 if validation fails in --strict mode
|
|
37
|
+
|
|
38
|
+
EXAMPLES
|
|
39
|
+
|
|
40
|
+
raictl compile
|
|
41
|
+
raictl compile --verbose --simulate
|
|
42
|
+
raictl compile --engine generic --strict
|
|
43
|
+
raictl compile --vendor
|
|
44
|
+
raictl compile --locales
|
|
45
|
+
|
|
46
|
+
Related commands: raictl behaviour validate, rai adopt
|
|
47
|
+
LONGDESC
|
|
48
|
+
method_option :verbose,
|
|
49
|
+
type: :boolean,
|
|
50
|
+
default: false,
|
|
51
|
+
desc: 'Show detailed output'
|
|
52
|
+
method_option :vendor,
|
|
53
|
+
type: :boolean,
|
|
54
|
+
default: false,
|
|
55
|
+
desc: 'Write compile.lock.yml lockfile'
|
|
56
|
+
method_option :simulate,
|
|
57
|
+
type: :boolean,
|
|
58
|
+
default: false,
|
|
59
|
+
desc: 'Dry run — show diffs without writing'
|
|
60
|
+
method_option :update,
|
|
61
|
+
type: :boolean,
|
|
62
|
+
default: false,
|
|
63
|
+
desc: 'Update existing rules (default behaviour)'
|
|
64
|
+
method_option :engine,
|
|
65
|
+
type: :string,
|
|
66
|
+
default: nil,
|
|
67
|
+
desc: 'Engine name (e.g., claude, generic)'
|
|
68
|
+
method_option :target,
|
|
69
|
+
type: :string,
|
|
70
|
+
default: nil,
|
|
71
|
+
desc: '[DEPRECATED] Use --engine instead'
|
|
72
|
+
method_option :strict,
|
|
73
|
+
type: :boolean,
|
|
74
|
+
default: false,
|
|
75
|
+
desc: 'Treat warnings as errors'
|
|
76
|
+
method_option :locales,
|
|
77
|
+
type: :boolean,
|
|
78
|
+
default: false,
|
|
79
|
+
desc: 'Compile locale files to gettext and Qt formats'
|
|
80
|
+
|
|
81
|
+
def generate
|
|
82
|
+
if options[:locales]
|
|
83
|
+
compile_locales
|
|
84
|
+
return
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
check_xdg_migration
|
|
88
|
+
compiler = build_compiler
|
|
89
|
+
categories = compiler.discover_categories
|
|
90
|
+
|
|
91
|
+
if categories.empty?
|
|
92
|
+
puts Rainbow(t('compile.no_categories')).yellow
|
|
93
|
+
return
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
log_categories(categories, compiler) if options[:verbose]
|
|
97
|
+
|
|
98
|
+
compiled = compiler.compile
|
|
99
|
+
check_conflict_warnings(compiler)
|
|
100
|
+
check_capabilities(resolve_engine_option)
|
|
101
|
+
results = process_compiled(compiler, compiled)
|
|
102
|
+
remove_orphans(compiler, compiled)
|
|
103
|
+
write_lockfile(compiler, compiled) if options[:vendor] && !options[:simulate]
|
|
104
|
+
print_summary(results)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def t(key, **)
|
|
110
|
+
::I18n.t("rosett_ai.cli.#{key}", **)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def build_compiler
|
|
114
|
+
engine_name = resolve_engine_option
|
|
115
|
+
backend = RosettAi::Compiler::Backend.for(engine_name)
|
|
116
|
+
scope = detect_compile_scope
|
|
117
|
+
target = resolve_target_dir(backend) || default_target_dir(scope)
|
|
118
|
+
RosettAi::Compiler::CompilationPipeline.new(
|
|
119
|
+
source_dir: resolve_source_dir(scope),
|
|
120
|
+
target_dir: target,
|
|
121
|
+
schema_dir: RosettAi.root.join('conf', 'schemas'),
|
|
122
|
+
backend: backend,
|
|
123
|
+
scope: scope,
|
|
124
|
+
additional_source_dirs: additional_dirs(scope)
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Resolves the primary source directory for compilation.
|
|
129
|
+
#
|
|
130
|
+
# Global scope: XDG-compliant ~/.config/rosett-ai/conf (user behaviours)
|
|
131
|
+
# Project scope: .rosett-ai/conf (project behaviours, merged with global via additional_source_dirs)
|
|
132
|
+
# Internal (rosett-ai dev): rosett-ai repo's own conf/ (for development)
|
|
133
|
+
def resolve_source_dir(scope)
|
|
134
|
+
return RosettAi.root.join('conf') if RosettAi.context.rai_internal?
|
|
135
|
+
|
|
136
|
+
scope == :project ? RosettAi.conf_root.join('conf') : global_conf_dir
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Additional source dirs for the 3-tier lookup chain.
|
|
140
|
+
# Order matters: later dirs override earlier ones (CompilationPipeline
|
|
141
|
+
# appends @source_dir last). So packaged defaults go first, then XDG
|
|
142
|
+
# user config, so user rules override packaged defaults by filename.
|
|
143
|
+
def additional_dirs(scope)
|
|
144
|
+
dirs = []
|
|
145
|
+
packaged = RosettAi.paths.packaged_conf_dir
|
|
146
|
+
dirs << packaged if packaged.directory?
|
|
147
|
+
dirs << global_conf_dir if scope == :project || RosettAi.context.rai_internal?
|
|
148
|
+
dirs
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def global_conf_dir
|
|
152
|
+
RosettAi.paths.rai_conf_dir
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def detect_compile_scope
|
|
156
|
+
ctx = RosettAi.context
|
|
157
|
+
ctx.project? && !ctx.rai_internal? ? :project : :global
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def check_xdg_migration
|
|
161
|
+
return if RosettAi.context.rai_internal?
|
|
162
|
+
|
|
163
|
+
migrator = RosettAi::Migration::XdgMigrator.new
|
|
164
|
+
return unless migrator.migration_needed?
|
|
165
|
+
|
|
166
|
+
puts Rainbow(migrator.warning_message).yellow
|
|
167
|
+
migrated = migrator.migrate!
|
|
168
|
+
migrated.each { |f| puts Rainbow(" Migrated: #{f}").green }
|
|
169
|
+
puts Rainbow(" Migration complete. #{migrated.size} file(s) moved.").green unless migrated.empty?
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def check_conflict_warnings(compiler)
|
|
173
|
+
warnings = compiler.conflict_warnings
|
|
174
|
+
return if warnings.empty?
|
|
175
|
+
|
|
176
|
+
warnings.each do |warning|
|
|
177
|
+
puts Rainbow(" WARNING: #{warning}").yellow
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
return unless options[:strict]
|
|
181
|
+
|
|
182
|
+
raise RosettAi::CompileError,
|
|
183
|
+
"#{warnings.size} duplicate rule ID(s) detected in strict mode"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def check_capabilities(engine_name)
|
|
187
|
+
checker = RosettAi::Compiler::CapabilityChecker.new(engine_name)
|
|
188
|
+
summaries = checker.check_summarized(RosettAi.conf_root.join('conf'))
|
|
189
|
+
return if summaries.empty?
|
|
190
|
+
|
|
191
|
+
summaries.each do |summary|
|
|
192
|
+
puts Rainbow(" WARNING: #{summary[:message]}").yellow
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
return unless options[:strict]
|
|
196
|
+
|
|
197
|
+
total = summaries.sum { |summary| summary[:count] }
|
|
198
|
+
raise RosettAi::CompileError,
|
|
199
|
+
"#{total} capability warning(s) in strict mode"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def resolve_engine_option
|
|
203
|
+
engine = options[:engine]
|
|
204
|
+
target = options[:target]
|
|
205
|
+
|
|
206
|
+
if engine && target
|
|
207
|
+
raise RosettAi::CompileError,
|
|
208
|
+
t('compile.engine_and_target_conflict')
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
if target
|
|
212
|
+
if options[:strict]
|
|
213
|
+
raise RosettAi::CompileError,
|
|
214
|
+
t('compile.target_deprecated_strict')
|
|
215
|
+
end
|
|
216
|
+
RosettAi::Deprecation.warn_deprecated(
|
|
217
|
+
feature: '--target',
|
|
218
|
+
replacement: '--engine',
|
|
219
|
+
since: '1.0.0',
|
|
220
|
+
removal: '2.0.0'
|
|
221
|
+
)
|
|
222
|
+
return target
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
engine || RosettAi.rai_config.default_engine
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def resolve_target_dir(backend)
|
|
229
|
+
dir = backend.profile.output_dir
|
|
230
|
+
dir ? Pathname.new(File.expand_path(dir)) : nil
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def default_target_dir(scope)
|
|
234
|
+
return RosettAi.context.project_root.join('.claude', 'rules') if scope == :project
|
|
235
|
+
|
|
236
|
+
RosettAi.paths.rules_dir
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Resolves the target directory for a compiled output based on its
|
|
240
|
+
# declared scope. Falls back to the pipeline's target_dir when no
|
|
241
|
+
# scope is declared or the behaviour's scope matches the pipeline's.
|
|
242
|
+
#
|
|
243
|
+
# Scope routing:
|
|
244
|
+
# global/personal → ~/.claude/rules/ (global rules dir)
|
|
245
|
+
# project → .claude/rules/ (project-local)
|
|
246
|
+
# nil → pipeline default (current behaviour)
|
|
247
|
+
def resolve_scoped_target_dir(compiler, info)
|
|
248
|
+
declared_scope = info[:scope]
|
|
249
|
+
return compiler.target_dir unless declared_scope
|
|
250
|
+
|
|
251
|
+
case declared_scope
|
|
252
|
+
when 'project'
|
|
253
|
+
if RosettAi.context.project?
|
|
254
|
+
RosettAi.context.project_root.join('.claude', 'rules')
|
|
255
|
+
else
|
|
256
|
+
compiler.target_dir
|
|
257
|
+
end
|
|
258
|
+
when 'global', 'personal'
|
|
259
|
+
RosettAi.paths.rules_dir
|
|
260
|
+
else
|
|
261
|
+
compiler.target_dir
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def compile_locales
|
|
266
|
+
locale_compiler = RosettAi::Compiler::LocaleCompiler.new(
|
|
267
|
+
source_dir: RosettAi.root.join('locales'),
|
|
268
|
+
gettext_dir: RosettAi.root.join('locales', 'compiled', 'gettext'),
|
|
269
|
+
qt_dir: RosettAi.root.join('locales', 'compiled', 'qt')
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if options[:simulate]
|
|
273
|
+
puts Rainbow(t('compile.locale_simulate')).yellow
|
|
274
|
+
log_verbose("Source: #{locale_compiler.source_dir}")
|
|
275
|
+
log_verbose("Gettext output: #{locale_compiler.gettext_dir}")
|
|
276
|
+
log_verbose("Qt output: #{locale_compiler.qt_dir}")
|
|
277
|
+
return
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
results = locale_compiler.compile
|
|
281
|
+
puts Rainbow(t('compile.locale_gettext_compiled', count: results[:gettext].size)).green
|
|
282
|
+
puts Rainbow(t('compile.locale_qt_compiled', count: results[:qt].size)).green
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def log_categories(categories, compiler)
|
|
286
|
+
puts Rainbow(t('compile.discovered_categories')).bright
|
|
287
|
+
categories.each { |cat| puts " #{Rainbow(cat).cyan}" }
|
|
288
|
+
compiler.skipped_project_categories.each do |cat|
|
|
289
|
+
puts " #{Rainbow(cat).dark} (project scope only)"
|
|
290
|
+
end
|
|
291
|
+
puts
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def process_compiled(compiler, compiled)
|
|
295
|
+
results = { created: [], updated: [], unchanged: [], skipped: [] }
|
|
296
|
+
|
|
297
|
+
compiled.each do |filename, info|
|
|
298
|
+
target_dir = resolve_scoped_target_dir(compiler, info)
|
|
299
|
+
target_path = target_dir.join(filename)
|
|
300
|
+
content = info[:content]
|
|
301
|
+
new_checksum = compiler.checksum(content)
|
|
302
|
+
|
|
303
|
+
ctx = CompileFileContext.new(
|
|
304
|
+
target_path: target_path, content: content, checksum: new_checksum, filename: filename
|
|
305
|
+
)
|
|
306
|
+
if options[:simulate]
|
|
307
|
+
handle_simulate(compiler, ctx, results)
|
|
308
|
+
else
|
|
309
|
+
handle_write(compiler, ctx, results)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
results
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def handle_simulate(compiler, ctx, results)
|
|
317
|
+
if File.exist?(ctx.target_path)
|
|
318
|
+
diff_output = compiler.diff(ctx.target_path, ctx.content)
|
|
319
|
+
if diff_output
|
|
320
|
+
puts Rainbow(t('compile.would_update', filename: ctx.filename)).yellow
|
|
321
|
+
puts diff_output if options[:verbose]
|
|
322
|
+
results[:updated] << ctx.filename
|
|
323
|
+
else
|
|
324
|
+
puts Rainbow(t('compile.unchanged', filename: ctx.filename)).green if options[:verbose]
|
|
325
|
+
results[:unchanged] << ctx.filename
|
|
326
|
+
end
|
|
327
|
+
else
|
|
328
|
+
puts Rainbow(t('compile.would_create', filename: ctx.filename)).cyan
|
|
329
|
+
results[:created] << ctx.filename
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def handle_write(compiler, ctx, results)
|
|
334
|
+
FileUtils.mkdir_p(ctx.target_path.dirname)
|
|
335
|
+
action = determine_write_action(compiler, ctx)
|
|
336
|
+
perform_write_action(ctx, action, results)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def determine_write_action(compiler, ctx)
|
|
340
|
+
return :created unless File.exist?(ctx.target_path)
|
|
341
|
+
|
|
342
|
+
existing = compiler.checksum(File.read(ctx.target_path, encoding: 'utf-8'))
|
|
343
|
+
existing == ctx.checksum ? :unchanged : :updated
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def perform_write_action(ctx, action, results)
|
|
347
|
+
target_path, content, filename, checksum_prefix = extract_ctx(ctx)
|
|
348
|
+
write_file(target_path, content) if [:updated, :created].include?(action)
|
|
349
|
+
log_verbose("#{format_action_label(action)}: #{filename} (#{checksum_prefix})")
|
|
350
|
+
results[action] << filename
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def format_action_label(action)
|
|
354
|
+
{ created: t('compile.created'), updated: t('compile.updated'),
|
|
355
|
+
unchanged: t('compile.unchanged', filename: '') }.fetch(action, action.to_s)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def extract_ctx(ctx)
|
|
359
|
+
[ctx.target_path, ctx.content, ctx.filename, ctx.checksum[0..7]]
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def write_file(path, content)
|
|
363
|
+
File.open(path, 'w', 0o644) { |f| f.write(content) }
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def remove_orphans(compiler, compiled)
|
|
367
|
+
# Guard: when compile produced no output (e.g. empty project conf/),
|
|
368
|
+
# every managed file in target_dir would appear orphaned. Removing
|
|
369
|
+
# them would silently destroy all compiled rules. See WP#712.
|
|
370
|
+
return if compiled.empty?
|
|
371
|
+
|
|
372
|
+
orphans = compiler.orphaned_files(compiled.keys)
|
|
373
|
+
orphans.each do |orphan|
|
|
374
|
+
basename = File.basename(orphan)
|
|
375
|
+
if options[:simulate]
|
|
376
|
+
puts Rainbow(t('compile.would_remove_orphan', filename: basename)).red
|
|
377
|
+
else
|
|
378
|
+
File.delete(orphan)
|
|
379
|
+
log_verbose(t('compile.removed_orphan', filename: basename))
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def write_lockfile(compiler, compiled)
|
|
385
|
+
lockfile_path = RosettAi.conf_root.join('conf', 'compile.lock.yml')
|
|
386
|
+
data = compiler.lockfile_data(compiled)
|
|
387
|
+
File.open(lockfile_path, 'w', 0o644) { |f| f.write(data.to_yaml) }
|
|
388
|
+
log_verbose(t('compile.wrote_lockfile', path: lockfile_path))
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def print_summary(results)
|
|
392
|
+
rows = []
|
|
393
|
+
rows << [t('compile.created'), results[:created].size] unless results[:created].empty?
|
|
394
|
+
rows << [t('compile.updated'), results[:updated].size] unless results[:updated].empty?
|
|
395
|
+
rows << [t('compile.unchanged', filename: ''), results[:unchanged].size] unless results[:unchanged].empty?
|
|
396
|
+
|
|
397
|
+
return if rows.empty?
|
|
398
|
+
|
|
399
|
+
puts
|
|
400
|
+
table = ::Terminal::Table.new(
|
|
401
|
+
title: Rainbow(t('compile.summary_title')).bright,
|
|
402
|
+
headings: [t('compile.action'), t('compile.count')],
|
|
403
|
+
rows: rows,
|
|
404
|
+
style: { border: :unicode_round }
|
|
405
|
+
)
|
|
406
|
+
puts table
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def log_verbose(message)
|
|
410
|
+
puts " #{message}" if options[:verbose]
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
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 Thor
|
|
8
|
+
module Tasks
|
|
9
|
+
# Thor subcommand for generating shell completion scripts.
|
|
10
|
+
#
|
|
11
|
+
# Generates bash, zsh, and fish completion scripts from the Thor
|
|
12
|
+
# command registry. Scripts are output to stdout for piping or
|
|
13
|
+
# can be installed to the standard completion directory.
|
|
14
|
+
#
|
|
15
|
+
# @author hugo
|
|
16
|
+
# @author claude
|
|
17
|
+
class Completion < ::Thor
|
|
18
|
+
INSTALL_PATHS = {
|
|
19
|
+
'bash' => '/usr/share/bash-completion/completions/rai',
|
|
20
|
+
'zsh' => '/usr/share/zsh/vendor-completions/_rai',
|
|
21
|
+
'fish' => "#{Dir.home}/.config/fish/completions/raictl.fish"
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
desc 'bash', 'Generate bash completion script'
|
|
25
|
+
long_desc <<~LONGDESC
|
|
26
|
+
Generate a bash completion script and write to stdout.
|
|
27
|
+
|
|
28
|
+
Example: raictl completion bash > /usr/share/bash-completion/completions/rai
|
|
29
|
+
|
|
30
|
+
Related: completion zsh, completion fish, completion install
|
|
31
|
+
LONGDESC
|
|
32
|
+
def bash
|
|
33
|
+
output_completion('bash')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc 'zsh', 'Generate zsh completion script'
|
|
37
|
+
long_desc <<~LONGDESC
|
|
38
|
+
Generate a zsh completion script and write to stdout.
|
|
39
|
+
|
|
40
|
+
Example: raictl completion zsh > /usr/share/zsh/vendor-completions/_rai
|
|
41
|
+
|
|
42
|
+
Related: completion bash, completion fish, completion install
|
|
43
|
+
LONGDESC
|
|
44
|
+
def zsh
|
|
45
|
+
output_completion('zsh')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc 'fish', 'Generate fish completion script'
|
|
49
|
+
long_desc <<~LONGDESC
|
|
50
|
+
Generate a fish completion script and write to stdout.
|
|
51
|
+
|
|
52
|
+
Example: raictl completion fish > ~/.config/fish/completions/raictl.fish
|
|
53
|
+
|
|
54
|
+
Related: completion bash, completion zsh, completion install
|
|
55
|
+
LONGDESC
|
|
56
|
+
def fish
|
|
57
|
+
output_completion('fish')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc 'install', 'Install completion script for the current shell'
|
|
61
|
+
long_desc <<~LONGDESC
|
|
62
|
+
Install the completion script to the standard system path.
|
|
63
|
+
|
|
64
|
+
The shell is auto-detected from $SHELL unless overridden
|
|
65
|
+
with --shell. Supported shells: bash, zsh, fish.
|
|
66
|
+
|
|
67
|
+
Install paths:
|
|
68
|
+
bash: /usr/share/bash-completion/completions/rai
|
|
69
|
+
zsh: /usr/share/zsh/vendor-completions/_rai
|
|
70
|
+
fish: ~/.config/fish/completions/raictl.fish
|
|
71
|
+
|
|
72
|
+
EXAMPLES
|
|
73
|
+
|
|
74
|
+
raictl completion install
|
|
75
|
+
raictl completion install --shell zsh
|
|
76
|
+
|
|
77
|
+
Related: completion bash, completion zsh, completion fish
|
|
78
|
+
LONGDESC
|
|
79
|
+
method_option :shell, type: :string, default: nil,
|
|
80
|
+
desc: 'Shell to install for (auto-detected from $SHELL)'
|
|
81
|
+
def install
|
|
82
|
+
shell = detect_shell
|
|
83
|
+
generator = RosettAi::Completion::Generator.new
|
|
84
|
+
script = generator.generate(shell)
|
|
85
|
+
path = INSTALL_PATHS.fetch(shell)
|
|
86
|
+
|
|
87
|
+
install_script(script, path, shell)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def output_completion(shell)
|
|
93
|
+
generator = RosettAi::Completion::Generator.new
|
|
94
|
+
puts generator.generate(shell)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def detect_shell
|
|
98
|
+
shell = options[:shell] || File.basename(ENV.fetch('SHELL', ''))
|
|
99
|
+
shell = shell.downcase
|
|
100
|
+
|
|
101
|
+
unless RosettAi::Completion::Generator::SUPPORTED_SHELLS.include?(shell)
|
|
102
|
+
raise RosettAi::Error,
|
|
103
|
+
"Shell #{shell} is not supported — available: " \
|
|
104
|
+
"#{RosettAi::Completion::Generator::SUPPORTED_SHELLS.join(', ')}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
shell
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def install_script(script, path, shell)
|
|
111
|
+
dir = File.dirname(path)
|
|
112
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
|
113
|
+
|
|
114
|
+
File.write(path, script)
|
|
115
|
+
warn "Installed #{shell} completion to #{path}"
|
|
116
|
+
rescue Errno::EACCES
|
|
117
|
+
raise RosettAi::Error,
|
|
118
|
+
"Permission denied writing to #{path} — try: sudo rai completion install --shell #{shell}"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'terminal-table'
|
|
7
|
+
|
|
8
|
+
module RosettAi
|
|
9
|
+
module Thor
|
|
10
|
+
module Tasks
|
|
11
|
+
# CLI task for `rai comply` — compliance verification.
|
|
12
|
+
#
|
|
13
|
+
# Runs CRA, license, and SPDX header compliance checks.
|
|
14
|
+
# Produces TTY-aware or machine-readable (JSON) output.
|
|
15
|
+
# Exit codes: 0 = all pass, 1 = warnings only, 2 = any failure.
|
|
16
|
+
#
|
|
17
|
+
# @author hugo
|
|
18
|
+
# @author claude
|
|
19
|
+
class Comply < ::Thor
|
|
20
|
+
default_task :check
|
|
21
|
+
|
|
22
|
+
desc 'check', 'Run all compliance checks'
|
|
23
|
+
long_desc <<~LONGDESC
|
|
24
|
+
Runs compliance checks and produces a summary report.
|
|
25
|
+
|
|
26
|
+
By default, all checks are run: CRA requirements, license
|
|
27
|
+
compatibility, and SPDX header verification.
|
|
28
|
+
|
|
29
|
+
Use --cra, --license, or --headers to run specific checks.
|
|
30
|
+
Use --format json for machine-readable CI output.
|
|
31
|
+
|
|
32
|
+
Exit codes: 0 = all pass, 1 = warnings only, 2 = any failure.
|
|
33
|
+
|
|
34
|
+
EXAMPLES
|
|
35
|
+
|
|
36
|
+
raictl comply
|
|
37
|
+
raictl comply --cra
|
|
38
|
+
raictl comply --license --headers
|
|
39
|
+
raictl comply --format json
|
|
40
|
+
LONGDESC
|
|
41
|
+
method_option :cra, type: :boolean, default: false,
|
|
42
|
+
desc: 'Run CRA-specific checks only'
|
|
43
|
+
method_option :license, type: :boolean, default: false,
|
|
44
|
+
desc: 'Run license audit checks only'
|
|
45
|
+
method_option :headers, type: :boolean, default: false,
|
|
46
|
+
desc: 'Run SPDX header checks only'
|
|
47
|
+
method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
|
|
48
|
+
desc: 'Output format'
|
|
49
|
+
def check
|
|
50
|
+
checks = selected_checks
|
|
51
|
+
results = RosettAi::Comply::Runner.new(
|
|
52
|
+
project_root: resolve_project_root,
|
|
53
|
+
checks: checks
|
|
54
|
+
).run
|
|
55
|
+
|
|
56
|
+
reporter = RosettAi::Comply::Reporter.new(
|
|
57
|
+
results: results,
|
|
58
|
+
format: options[:format]
|
|
59
|
+
)
|
|
60
|
+
reporter.report
|
|
61
|
+
|
|
62
|
+
code = reporter.exit_code
|
|
63
|
+
exit code unless code.zero?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def selected_checks
|
|
69
|
+
active = []
|
|
70
|
+
active << :cra if options[:cra]
|
|
71
|
+
active << :license if options[:license]
|
|
72
|
+
active << :headers if options[:headers]
|
|
73
|
+
active.empty? ? nil : active
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def resolve_project_root
|
|
77
|
+
Pathname.new(ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|