rosett-ai 1.3.3 → 1.5.10
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 +4 -4
- data/CHANGELOG.md +413 -22
- data/README.md +56 -42
- data/bin/raictl +13 -0
- data/conf/adopt_redactions.yml +2 -0
- data/conf/compliance/cra_rules.yml +2 -0
- data/conf/compliance/license_rules.yml +2 -0
- data/conf/mcp/server_defaults.yml +2 -0
- data/conf/mcp/trust.yml +1 -0
- data/conf/packaging/core.yml +2 -0
- data/conf/packaging/gtk4.yml +2 -0
- data/conf/packaging/qt6.yml +2 -0
- data/conf/policy/default_deny_list.yml +2 -0
- data/conf/review/cli-command-audit.yml +2 -0
- data/conf/review/design-docs.yml +2 -0
- data/conf/review/design-questionnaire.yml +2 -0
- data/conf/review/questionnaire.yml +2 -0
- data/conf/review/rosett-ai-core.yml +2 -0
- data/conf/schemas/ai_config_schema.json +14 -2
- data/conf/schemas/autoqueue.schema.json +153 -0
- data/conf/schemas/behaviour_schema.json +31 -7
- data/conf/schemas/compliance_rule_schema.json +17 -4
- data/conf/schemas/content_pack_manifest_schema.json +23 -4
- data/conf/schemas/design_schema.json +177 -42
- data/conf/schemas/engine_manifest_schema.json +22 -5
- data/conf/schemas/lockfile_schema.json +26 -4
- data/conf/schemas/mcp_server_schema.json +50 -9
- data/conf/schemas/packaging_schema.json +23 -5
- data/conf/schemas/policy_schema.json +35 -9
- data/conf/schemas/provenance_schema.json +33 -5
- data/conf/schemas/rai_config_schema.json +6 -2
- data/conf/schemas/rai_project_schema.json +8 -2
- data/conf/schemas/scope_hierarchy_schema.json +22 -5
- data/conf/schemas/target_schema.json +33 -6
- data/conf/schemas/tooling_schema.json +53 -12
- data/conf/schemas/workflow_schema.json +39 -9
- data/conf/targets/agents_md.yml +1 -0
- data/conf/targets/claude.yml +2 -0
- data/conf/tooling/tools.yml +2 -0
- data/lib/rosett_ai/adopter/local_analysis_collector.rb +5 -0
- data/lib/rosett_ai/adopter/rule_adopter.rb +40 -0
- data/lib/rosett_ai/ai_config/context_window.rb +3 -0
- data/lib/rosett_ai/ai_config/cost_controls.rb +1 -0
- data/lib/rosett_ai/ai_config/fallback_chain.rb +1 -0
- data/lib/rosett_ai/ai_config/model_router.rb +1 -0
- data/lib/rosett_ai/ai_config/validator.rb +2 -0
- data/lib/rosett_ai/authorship/attribution_compiler.rb +1 -0
- data/lib/rosett_ai/authorship/disclosure_policy.rb +2 -0
- data/lib/rosett_ai/authorship/review_validator.rb +1 -0
- data/lib/rosett_ai/authorship/trailer_generator.rb +1 -0
- data/lib/rosett_ai/backup/compressor.rb +3 -0
- data/lib/rosett_ai/backup/destination.rb +1 -0
- data/lib/rosett_ai/behaviour/manager.rb +1 -0
- data/lib/rosett_ai/compiler/backend.rb +3 -0
- data/lib/rosett_ai/compiler/backends/agents_md_backend.rb +1 -0
- data/lib/rosett_ai/compiler/backends/claude_backend.rb +1 -0
- data/lib/rosett_ai/compiler/behaviour_compiler.rb +1 -0
- data/lib/rosett_ai/compiler/compilation_pipeline.rb +1 -7
- data/lib/rosett_ai/compiler/locale_compiler.rb +2 -0
- data/lib/rosett_ai/compiler/target_profile.rb +11 -0
- data/lib/rosett_ai/completion/generator.rb +2 -0
- data/lib/rosett_ai/completion/shells/zsh_generator.rb +1 -0
- data/lib/rosett_ai/comply/checkers/cra_checker.rb +1 -0
- data/lib/rosett_ai/comply/checkers/license_checker.rb +2 -0
- data/lib/rosett_ai/comply/checkers/spdx_header_checker.rb +4 -0
- data/lib/rosett_ai/comply/runner.rb +2 -0
- data/lib/rosett_ai/composition/composer.rb +1 -0
- data/lib/rosett_ai/composition/lockfile.rb +1 -0
- data/lib/rosett_ai/composition/merge_strategy.rb +1 -0
- data/lib/rosett_ai/config/masking_secret_resolver.rb +9 -0
- data/lib/rosett_ai/config/secret_resolver.rb +4 -0
- data/lib/rosett_ai/configuration.rb +4 -0
- data/lib/rosett_ai/content/content_client.rb +15 -0
- data/lib/rosett_ai/content/pack_installer.rb +1 -0
- data/lib/rosett_ai/content/pack_manifest.rb +21 -0
- data/lib/rosett_ai/content_packs/manager.rb +1 -0
- data/lib/rosett_ai/dbus/compositor_detector.rb +2 -0
- data/lib/rosett_ai/dbus/focus_adapters/base.rb +1 -0
- data/lib/rosett_ai/dbus/focus_adapters/gnome_adapter.rb +5 -0
- data/lib/rosett_ai/dbus/focus_adapters/hyprland_adapter.rb +4 -0
- data/lib/rosett_ai/dbus/focus_adapters/i3_adapter.rb +4 -0
- data/lib/rosett_ai/dbus/focus_adapters/kwin_adapter.rb +4 -0
- data/lib/rosett_ai/dbus/focus_adapters/x11_adapter.rb +4 -0
- data/lib/rosett_ai/dbus/focus_monitor_interface.rb +1 -0
- data/lib/rosett_ai/dbus/manager_interface.rb +1 -0
- data/lib/rosett_ai/dbus/plugin_manager_interface.rb +2 -0
- data/lib/rosett_ai/dbus/rate_limiter.rb +1 -0
- data/lib/rosett_ai/dbus/service.rb +3 -0
- data/lib/rosett_ai/dbus/status_notifier_interface.rb +14 -0
- data/lib/rosett_ai/desktop/dbus_client.rb +6 -0
- data/lib/rosett_ai/desktop/gtk4_app.rb +3 -0
- data/lib/rosett_ai/desktop/gtk4_preferences.rb +1 -0
- data/lib/rosett_ai/desktop/gui_logger.rb +2 -0
- data/lib/rosett_ai/doctor/check.rb +1 -1
- data/lib/rosett_ai/doctor/checks/cache_health_check.rb +1 -0
- data/lib/rosett_ai/doctor.rb +1 -0
- data/lib/rosett_ai/documentation/reference_compiler.rb +9 -0
- data/lib/rosett_ai/documentation/translator.rb +8 -0
- data/lib/rosett_ai/engines/base_config_compiler.rb +71 -1
- data/lib/rosett_ai/engines/detector.rb +5 -0
- data/lib/rosett_ai/engines/registry.rb +7 -0
- data/lib/rosett_ai/exit_codes.rb +6 -0
- data/lib/rosett_ai/formatting.rb +1 -0
- data/lib/rosett_ai/gem_consistency_checker.rb +6 -0
- data/lib/rosett_ai/git_hooks/chain_detector.rb +1 -0
- data/lib/rosett_ai/git_hooks/installer.rb +4 -0
- data/lib/rosett_ai/git_hooks/script_generator.rb +2 -0
- data/lib/rosett_ai/gitlab/validators/supplementary_gitlab_ci_yaml_validator.rb +6 -0
- data/lib/rosett_ai/i18n/utf8_checker.rb +1 -0
- data/lib/rosett_ai/init/config_file_writer.rb +4 -0
- data/lib/rosett_ai/init/directory_builder.rb +15 -0
- data/lib/rosett_ai/init/file_copier.rb +51 -0
- data/lib/rosett_ai/init/mcp_registrar.rb +1 -0
- data/lib/rosett_ai/init/project_initializer.rb +1 -0
- data/lib/rosett_ai/licensing/license_key.rb +4 -0
- data/lib/rosett_ai/licensing/license_store.rb +11 -0
- data/lib/rosett_ai/licensing/tier.rb +9 -0
- data/lib/rosett_ai/mcp/admin/auditor.rb +1 -0
- data/lib/rosett_ai/mcp/admin/health_checker.rb +1 -0
- data/lib/rosett_ai/mcp/admin/registry.rb +1 -0
- data/lib/rosett_ai/mcp/enforcement/hook_generator.rb +27 -4
- data/lib/rosett_ai/mcp/enforcement/validator.rb +24 -3
- data/lib/rosett_ai/mcp/governance.rb +9 -0
- data/lib/rosett_ai/mcp/http_security_config.rb +22 -0
- data/lib/rosett_ai/mcp/key_hasher.rb +4 -2
- data/lib/rosett_ai/mcp/keyfile.rb +4 -0
- data/lib/rosett_ai/mcp/middleware/cors.rb +4 -0
- data/lib/rosett_ai/mcp/middleware/origin_validation.rb +1 -0
- data/lib/rosett_ai/mcp/middleware/rate_limit.rb +3 -0
- data/lib/rosett_ai/mcp/plugins.rb +1 -0
- data/lib/rosett_ai/mcp/prompts/compilation_prompt.rb +2 -0
- data/lib/rosett_ai/mcp/prompts/compliance_prompt.rb +3 -0
- data/lib/rosett_ai/mcp/prompts/diagnostics_prompt.rb +2 -0
- data/lib/rosett_ai/mcp/prompts/validation_prompt.rb +2 -0
- data/lib/rosett_ai/mcp/resources/behaviour_resource.rb +2 -0
- data/lib/rosett_ai/mcp/resources/config_resource.rb +2 -0
- data/lib/rosett_ai/mcp/resources/design_resource.rb +1 -0
- data/lib/rosett_ai/mcp/resources/hooks_resource.rb +3 -0
- data/lib/rosett_ai/mcp/resources/provenance_resource.rb +2 -0
- data/lib/rosett_ai/mcp/resources/rules_resource.rb +1 -0
- data/lib/rosett_ai/mcp/resources/schema_resource.rb +1 -0
- data/lib/rosett_ai/mcp/server.rb +7 -1
- data/lib/rosett_ai/mcp/settings/trust_manager.rb +3 -0
- data/lib/rosett_ai/mcp/tools/adopt_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/backup_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/behaviour_display_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/behaviour_list_tool.rb +3 -0
- data/lib/rosett_ai/mcp/tools/behaviour_manage_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/behaviour_show_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/compile_status_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/compile_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/comply_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/config_compile_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/config_status_tool.rb +3 -0
- data/lib/rosett_ai/mcp/tools/content_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/context_query_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/design_list_tool.rb +3 -0
- data/lib/rosett_ai/mcp/tools/design_show_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/doctor_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/documentation_status_tool.rb +3 -0
- data/lib/rosett_ai/mcp/tools/engines_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/hook_install_tool.rb +7 -0
- data/lib/rosett_ai/mcp/tools/hook_preview_tool.rb +33 -8
- data/lib/rosett_ai/mcp/tools/hooks_status_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/init_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/license_status_tool.rb +3 -0
- data/lib/rosett_ai/mcp/tools/project_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/provenance_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/provenance_write_tool.rb +3 -0
- data/lib/rosett_ai/mcp/tools/retrofit_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/rule_search_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/schema_get_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/tooling_tool.rb +5 -0
- data/lib/rosett_ai/mcp/tools/validate_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/workflow_execute_tool.rb +4 -0
- data/lib/rosett_ai/mcp/tools/workflow_tool.rb +5 -0
- data/lib/rosett_ai/migration/detector.rb +1 -0
- data/lib/rosett_ai/migration/nncc_config_migrator.rb +2 -0
- data/lib/rosett_ai/migration/xdg_migrator.rb +3 -1
- data/lib/rosett_ai/package_manager/apt.rb +5 -0
- data/lib/rosett_ai/package_manager/base.rb +1 -0
- data/lib/rosett_ai/package_manager/gem_backend.rb +1 -0
- data/lib/rosett_ai/packaging/variant_config.rb +12 -0
- data/lib/rosett_ai/plugins/contract.rb +1 -0
- data/lib/rosett_ai/plugins/engine_contract.rb +2 -0
- data/lib/rosett_ai/plugins/gui_contract.rb +2 -0
- data/lib/rosett_ai/plugins/mcp_contract.rb +2 -0
- data/lib/rosett_ai/plugins/registry.rb +10 -0
- data/lib/rosett_ai/policy/auditor.rb +1 -0
- data/lib/rosett_ai/policy/opt_out_scanner.rb +1 -0
- data/lib/rosett_ai/policy/tier_hierarchy.rb +2 -0
- data/lib/rosett_ai/policy/validator.rb +1 -0
- data/lib/rosett_ai/profiler.rb +7 -0
- data/lib/rosett_ai/project/drift_detector.rb +1 -0
- data/lib/rosett_ai/project/manager.rb +1 -0
- data/lib/rosett_ai/project/template_applier.rb +1 -0
- data/lib/rosett_ai/provenance/entry.rb +2 -0
- data/lib/rosett_ai/provenance/source.rb +1 -0
- data/lib/rosett_ai/provenance/store.rb +3 -0
- data/lib/rosett_ai/provenance/validator.rb +1 -0
- data/lib/rosett_ai/quorum/collector.rb +1 -0
- data/lib/rosett_ai/quorum/comparator.rb +2 -0
- data/lib/rosett_ai/quorum/strategies/adopt.rb +1 -0
- data/lib/rosett_ai/rai_config.rb +9 -0
- data/lib/rosett_ai/retrofit/engine.rb +2 -0
- data/lib/rosett_ai/retrofit/parsers/agents_md_parser.rb +8 -0
- data/lib/rosett_ai/retrofit/parsers/claude_parser.rb +10 -0
- data/lib/rosett_ai/retrofit/parsers/cursor_parser.rb +9 -0
- data/lib/rosett_ai/retrofit/secret_detector.rb +3 -0
- data/lib/rosett_ai/secrets_resolver.rb +1 -0
- data/lib/rosett_ai/session_hooks/installer.rb +130 -0
- data/lib/rosett_ai/smart_feedback/suggester.rb +4 -0
- data/lib/rosett_ai/smart_feedback/thor_middleware.rb +7 -0
- data/lib/rosett_ai/structured_logger.rb +1 -0
- data/lib/rosett_ai/telemetry/log_rotator.rb +1 -0
- data/lib/rosett_ai/telemetry.rb +1 -0
- data/lib/rosett_ai/thor/cli.rb +9 -0
- data/lib/rosett_ai/thor/tasks/adopt.rb +2 -0
- data/lib/rosett_ai/thor/tasks/backfill_user_yml.rb +207 -0
- data/lib/rosett_ai/thor/tasks/backup.rb +6 -0
- data/lib/rosett_ai/thor/tasks/behaviour.rb +12 -0
- data/lib/rosett_ai/thor/tasks/build.rb +106 -21
- data/lib/rosett_ai/thor/tasks/compile.rb +76 -32
- data/lib/rosett_ai/thor/tasks/completion.rb +9 -0
- data/lib/rosett_ai/thor/tasks/comply.rb +2 -0
- data/lib/rosett_ai/thor/tasks/config.rb +21 -5
- data/lib/rosett_ai/thor/tasks/content.rb +8 -0
- data/lib/rosett_ai/thor/tasks/dbus.rb +13 -0
- data/lib/rosett_ai/thor/tasks/design.rb +2 -0
- data/lib/rosett_ai/thor/tasks/desktop.rb +4 -0
- data/lib/rosett_ai/thor/tasks/doctor.rb +2 -0
- data/lib/rosett_ai/thor/tasks/documentation.rb +7 -0
- data/lib/rosett_ai/thor/tasks/engines.rb +8 -0
- data/lib/rosett_ai/thor/tasks/hooks.rb +8 -0
- data/lib/rosett_ai/thor/tasks/init.rb +1 -0
- data/lib/rosett_ai/thor/tasks/license.rb +8 -0
- data/lib/rosett_ai/thor/tasks/mcp.rb +40 -0
- data/lib/rosett_ai/thor/tasks/migrate.rb +2 -0
- data/lib/rosett_ai/thor/tasks/plugins.rb +15 -0
- data/lib/rosett_ai/thor/tasks/project.rb +8 -0
- data/lib/rosett_ai/thor/tasks/provenance.rb +8 -0
- data/lib/rosett_ai/thor/tasks/release.rb +8 -0
- data/lib/rosett_ai/thor/tasks/retrofit.rb +4 -0
- data/lib/rosett_ai/thor/tasks/validate.rb +1 -0
- data/lib/rosett_ai/thor/tasks/workflow.rb +14 -0
- data/lib/rosett_ai/tooling/version_checker.rb +1 -0
- data/lib/rosett_ai/ui/accessible_tui.rb +18 -0
- data/lib/rosett_ai/ui/base.rb +3 -0
- data/lib/rosett_ai/ui/gtk4.rb +19 -0
- data/lib/rosett_ai/ui/kde.rb +1 -0
- data/lib/rosett_ai/ui/qt6.rb +1 -0
- data/lib/rosett_ai/ui/registry.rb +15 -0
- data/lib/rosett_ai/ui/tui.rb +22 -0
- data/lib/rosett_ai/validators/behaviour_validator.rb +1 -0
- data/lib/rosett_ai/validators/design_validator.rb +2 -0
- data/lib/rosett_ai/validators/schema_validator.rb +4 -0
- data/lib/rosett_ai/validators/tooling_validator.rb +1 -0
- data/lib/rosett_ai/version.rb +2 -1
- data/lib/rosett_ai/version_consistency_checker.rb +6 -0
- data/lib/rosett_ai/workflow/engine.rb +1 -0
- data/lib/rosett_ai/workflow/manager.rb +1 -0
- data/lib/rosett_ai/workflow/schema_validator.rb +2 -0
- data/lib/rosett_ai/workflow/step_runner.rb +1 -0
- data/lib/rosett_ai/workflow/steps/rai_step.rb +1 -0
- data/lib/rosett_ai/yaml_loader.rb +2 -0
- data/lib/rosett_ai.rb +2 -0
- data/lib/scripts/generated/docker_hub_tags.rb +2 -0
- data/locales/ar.yml +2 -0
- data/locales/en.yml +5 -0
- data/locales/fr.yml +2 -0
- metadata +9 -189
- data/.ai-provenance.yml +0 -119
- data/.debride_whitelist +0 -186
- data/.fasterer.yml +0 -29
- data/.mdl_style.rb +0 -10
- data/.mdlrc +0 -3
- data/.mutant.yml +0 -49
- data/.namespace-allowlist +0 -42
- data/.reek.yml +0 -1040
- data/.rosett-ai/config.yml +0 -3
- data/.rspec +0 -5
- data/.rubocop.yml +0 -380
- data/.ruby-version +0 -1
- data/.yamllint +0 -51
- data/.yardopts +0 -12
- data/AI-DISCLOSURE.md +0 -48
- data/CLAUDE.md +0 -141
- data/CONTRIBUTING.md +0 -734
- data/INSTALL.md +0 -154
- data/LICENSE.md +0 -675
- data/QUICKSTART.md +0 -73
- data/Rakefile +0 -200
- data/SECURITY.md +0 -114
- data/cliff.toml +0 -52
- data/conf/behaviour/.gitkeep +0 -0
- data/conf/design/aaif_alignment.yml +0 -181
- data/conf/design/ab_testing.yml +0 -172
- data/conf/design/accessibility.yml +0 -84
- data/conf/design/ai_authorship.yml +0 -210
- data/conf/design/ai_provenance.yml +0 -224
- data/conf/design/ai_tool_configuration.yml +0 -207
- data/conf/design/architecture.yml +0 -139
- data/conf/design/autocompletion.yml +0 -115
- data/conf/design/backward_compatibility.yml +0 -112
- data/conf/design/behaviour_composition.yml +0 -246
- data/conf/design/build_rake_extraction.yml +0 -57
- data/conf/design/ci_pipeline.yml +0 -100
- data/conf/design/claude_code_configuration.yml +0 -157
- data/conf/design/compiler.yml +0 -128
- data/conf/design/comply.yml +0 -153
- data/conf/design/content_packs.yml +0 -84
- data/conf/design/desktop_integration.yml +0 -289
- data/conf/design/distribution.yml +0 -216
- data/conf/design/doctor.yml +0 -184
- data/conf/design/documentation.yml +0 -152
- data/conf/design/engine_architecture.yml +0 -257
- data/conf/design/error_handling.yml +0 -103
- data/conf/design/feature_flags.yml +0 -142
- data/conf/design/git_hooks.yml +0 -165
- data/conf/design/gui_plugins.yml +0 -475
- data/conf/design/i18n.yml +0 -84
- data/conf/design/integration_testing.yml +0 -56
- data/conf/design/licensing_system.yml +0 -88
- data/conf/design/lifecycle_management.yml +0 -208
- data/conf/design/mcp_integration.yml +0 -207
- data/conf/design/mcp_settings.yml +0 -126
- data/conf/design/migration.yml +0 -56
- data/conf/design/monitoring_observability.yml +0 -194
- data/conf/design/namespace_cleanup.yml +0 -145
- data/conf/design/plugin_test_segregation.yml +0 -145
- data/conf/design/policy_management.yml +0 -229
- data/conf/design/project_management.yml +0 -183
- data/conf/design/rai_mcp_asset_discovery.yml +0 -164
- data/conf/design/rai_mcp_server.yml +0 -605
- data/conf/design/release_management.yml +0 -117
- data/conf/design/retrofit.yml +0 -199
- data/conf/design/retrospective_analyzer.yml +0 -79
- data/conf/design/scope_hierarchy.yml +0 -352
- data/conf/design/security.yml +0 -115
- data/conf/design/session_retrospective.yml +0 -85
- data/conf/design/smart_ui_feedback.yml +0 -89
- data/conf/design/structured_logging.yml +0 -148
- data/conf/design/styles.yml +0 -123
- data/conf/design/test_peer_review.yml +0 -89
- data/conf/design/testing.yml +0 -136
- data/conf/design/threat_model.yml +0 -108
- data/conf/design/ui_framework.yml +0 -111
- data/conf/design/usage_optimization.yml +0 -122
- data/conf/design/version_management.yml +0 -60
- data/conf/design/workflow.yml +0 -227
- data/doc/AAIF_POSITIONING.md +0 -58
- data/doc/ADOPT.md +0 -224
- data/doc/AI_PROVENANCE.md +0 -139
- data/doc/ARCHITECTURE.md +0 -920
- data/doc/BEHAVIOUR.md +0 -409
- data/doc/BUILD.md +0 -138
- data/doc/CI_CD_RECIPES.md +0 -171
- data/doc/CLAUDE_SESSIONS_MOVED.md +0 -16
- data/doc/COMMAND_ANALYSIS.md +0 -229
- data/doc/CONFIGURATION.md +0 -281
- data/doc/DESIGN_AUDIT.md +0 -235
- data/doc/DESIGN_PEER_REVIEW.md +0 -771
- data/doc/DESKTOP.md +0 -447
- data/doc/ENGINES.md +0 -567
- data/doc/ENGINE_DEVELOPMENT_GUIDE.md +0 -417
- data/doc/FEATURE_AUDIT.md +0 -218
- data/doc/IMPLEMENTATION_PLAN.md +0 -669
- data/doc/INCIDENT_REPORT_2026-02-02.md +0 -251
- data/doc/MIGRATION_GUIDE.md +0 -88
- data/doc/PACKAGING.md +0 -232
- data/doc/PROJECT_DASHBOARD.md +0 -153
- data/doc/PULP_DEPLOYMENT.md +0 -164
- data/doc/QUALITY_FIX_SUMMARY.md +0 -110
- data/doc/QUICK_START.md +0 -162
- data/doc/REEK_CONFIGURATION.md +0 -166
- data/doc/REFERENCE.md +0 -253
- data/doc/REFERENCES.md +0 -324
- data/doc/SECURITY_REVIEW_CHECKLIST.md +0 -72
- data/doc/SESSION_2026-02-28_GTK4_HARDENING.md +0 -359
- data/doc/SETUP.md +0 -202
- data/doc/TEST_PEER_REVIEW.md +0 -152
- data/doc/THREAT_MODEL.md +0 -230
- data/doc/USAGE.md +0 -545
- data/doc/USER_MANUAL.md +0 -585
- data/doc/ai_test_review_checklist.md +0 -110
- data/doc/changes/2026-02-18-packaging-fpm.md +0 -155
- data/doc/changes/2026-02-19-testing-infrastructure.md +0 -221
- data/doc/changes/2026-02-20-security-implementation.md +0 -281
- data/doc/changes/2026-02-20-styles-implementation.md +0 -220
- data/doc/changes/2026-02-21-architecture-completion.md +0 -95
- data/doc/changes/2026-02-21-architecture-ui-layer.md +0 -253
- data/doc/changes/2026-02-21-cc-config-implementation.md +0 -108
- data/doc/changes/2026-02-21-ci-pipeline-implementation.md +0 -214
- data/doc/changes/2026-02-21-compiler-multi-target-pipeline.md +0 -241
- data/doc/changes/2026-02-21-config-design-show-commands.md +0 -61
- data/doc/changes/2026-02-21-design-implementation-overview.md +0 -455
- data/doc/changes/2026-02-21-lifecycle-management.md +0 -196
- data/doc/changes/2026-02-21-path-resolver.md +0 -128
- data/doc/changes/2026-02-24-ci-tmpdir-mutant-fetch.md +0 -45
- data/doc/changes/2026-03-01-ci-bundler-strategy.md +0 -120
- data/doc/changes/2026-03-20-security-hardening-phase2.md +0 -163
- data/doc/context/SESSION-HANDOFF.md +0 -69
- data/doc/context/ai-engine-usage-trends-2026.md +0 -80
- data/doc/context/plan-pluggable-engines.md +0 -590
- data/doc/decisions/001-flog-deferred.md +0 -32
- data/doc/decisions/002-path-resolution-strategy.md +0 -158
- data/doc/decisions/003-ui-adapter-selection.md +0 -193
- data/doc/decisions/004-design-document-validation.md +0 -179
- data/doc/decisions/005-package-splitting-strategy.md +0 -200
- data/doc/decisions/006-multi-engine-architecture.md +0 -147
- data/doc/decisions/007-engine-agnostic-pivot.md +0 -219
- data/doc/decisions/008-ci-bundler-strategy.md +0 -129
- data/doc/decisions/009-core-only-v1-release.md +0 -60
- data/doc/decisions/010-engine-debian-packaging.md +0 -66
- data/doc/decisions/011-context-aware-cli.md +0 -71
- data/doc/dependency_decisions.yml +0 -247
- data/doc/issues/001-wrapper-missing-environment-variables.md +0 -197
- data/doc/issues/002-embedded-ruby-wrong-prefix.md +0 -217
- data/doc/issues/003-smoke-test-false-positive.md +0 -127
- data/doc/issues/004-market-research-design-updates.md +0 -109
- data/doc/issues/005-compile-scope-coexistence.md +0 -161
- data/doc/locales/.gitkeep +0 -0
- data/doc/man/rai.1.ronn +0 -505
- data/doc/operations/packaging.md +0 -133
- data/doc/operations/rosett-ai-release.md +0 -65
- data/doc/reference/error-catalog.md +0 -107
- 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 +0 -4
- data/doc/reference/src/bibliography.bib +0 -79
- data/doc/reference/src/main.tex +0 -1288
- data/doc/reference/src/structure.tex +0 -303
- data/doc/rosett-ai-bookmarks.html +0 -301
- data/kitchen.yml +0 -46
- data/lib/rosett_ai/mcp/enforcement/.gitkeep +0 -0
- data/locales/.gitkeep +0 -0
- data/packaging/build-engine-deb.sh +0 -81
- data/packaging/scripts/postinst +0 -17
- data/packaging/scripts/postrm +0 -19
- data/packaging/scripts/prerm +0 -10
- data/packaging/wrapper.sh.template +0 -38
- data/rosett-ai.gemspec +0 -63
- data/rules/.gitkeep +0 -0
- data/scripts/publish/pulp_upload.sh +0 -123
- data/settings.json +0 -29
|
@@ -22,6 +22,10 @@ module RosettAi
|
|
|
22
22
|
:stage_timings, :completed_stages,
|
|
23
23
|
:failed_stage, :failure_reason, :summary_printed
|
|
24
24
|
) do
|
|
25
|
+
# Create a new build context with a unique ID and log file.
|
|
26
|
+
#
|
|
27
|
+
# @param log_dir [Pathname] directory for build log files
|
|
28
|
+
# @return [BuildContext]
|
|
25
29
|
def self.create(log_dir)
|
|
26
30
|
id = SecureRandom.uuid
|
|
27
31
|
log_path = log_dir.join("build-#{id}.log")
|
|
@@ -60,6 +64,7 @@ module RosettAi
|
|
|
60
64
|
|
|
61
65
|
default_task :create
|
|
62
66
|
|
|
67
|
+
# @return [String] Target installation directory for packages.
|
|
63
68
|
INSTALL_DIR = '/opt/rosett-ai'
|
|
64
69
|
|
|
65
70
|
# Files/directories excluded when syncing app source into the staging tree
|
|
@@ -68,9 +73,12 @@ module RosettAi
|
|
|
68
73
|
'.git', '.gitignore',
|
|
69
74
|
# Bundler / gem build artifacts
|
|
70
75
|
'.bundle', '.binstubs', 'vendor/bundle', '*.gem',
|
|
71
|
-
'Gemfile.dev', 'Gemfile.dev.lock',
|
|
76
|
+
'Gemfile.dev', 'Gemfile.dev.lock',
|
|
77
|
+
'Gemfile.integration', 'Gemfile.integration.lock',
|
|
72
78
|
# CI/CD
|
|
73
79
|
'.gitlab-ci*',
|
|
80
|
+
# CI artifacts — never ship test/lint output
|
|
81
|
+
'rspec-report.xml', 'rubocop-report.json',
|
|
74
82
|
# Build & packaging
|
|
75
83
|
'coverage', 'tmp', 'pkg', 'spec', 'test', 'packaging',
|
|
76
84
|
# Dev tool configs
|
|
@@ -79,8 +87,24 @@ module RosettAi
|
|
|
79
87
|
'.overcommit.yml', '.git-hooks',
|
|
80
88
|
'.mdlrc', '.mdl_style.rb', '.yamllint', '.yardopts',
|
|
81
89
|
'.project', '.rosett-ai',
|
|
90
|
+
# Dev-only documentation (not needed at runtime)
|
|
91
|
+
'doc/api', 'doc/reference', 'doc/context', 'doc/issues',
|
|
92
|
+
'doc/decisions', 'doc/changes', 'doc/design',
|
|
93
|
+
# Security advisories — internal, never ship publicly
|
|
94
|
+
'doc/security',
|
|
95
|
+
# Design docs — carry internal WP refs, not runtime-required
|
|
96
|
+
'conf/design',
|
|
97
|
+
# Behaviour rules — user-authored, must never ship in packages (sec_001/rule_073)
|
|
98
|
+
'conf/behaviour',
|
|
99
|
+
# Peer review configs (dev-only)
|
|
100
|
+
'conf/review',
|
|
82
101
|
# AI assistant state — never ship in package
|
|
83
102
|
'.claude', 'doc/claude-sessions',
|
|
103
|
+
# AI provenance file — dev-only provenance tracking, not runtime-required
|
|
104
|
+
'.ai-provenance.yml',
|
|
105
|
+
# Internal audit, operations, signing, and rule-audit documents
|
|
106
|
+
'doc/audits', 'doc/operations', 'doc/signing',
|
|
107
|
+
'doc/rules-audit-*.md',
|
|
84
108
|
'cliff.toml', 'kitchen.yml', 'Rakefile'
|
|
85
109
|
].freeze
|
|
86
110
|
|
|
@@ -89,7 +113,7 @@ module RosettAi
|
|
|
89
113
|
:validate, :clean, :prepare, :compile_ruby,
|
|
90
114
|
:sync_app, :install_gems, :clean_build_artifacts,
|
|
91
115
|
:install_wrapper,
|
|
92
|
-
:install_config, :install_man, :install_build_info, :package
|
|
116
|
+
:install_config, :install_systemd, :install_man, :install_build_info, :rewrite_shebangs, :package
|
|
93
117
|
].freeze
|
|
94
118
|
|
|
95
119
|
# Bundler/RubyGems environment variables that must be stripped
|
|
@@ -162,6 +186,9 @@ module RosettAi
|
|
|
162
186
|
default: 'core',
|
|
163
187
|
desc: 'Package variant (core, gtk4, qt6)'
|
|
164
188
|
|
|
189
|
+
# Build a .deb package for the specified variant.
|
|
190
|
+
#
|
|
191
|
+
# @return [void]
|
|
165
192
|
def create
|
|
166
193
|
unless RosettAi.context.rai_internal?
|
|
167
194
|
raise ::Thor::Error,
|
|
@@ -181,8 +208,10 @@ module RosettAi
|
|
|
181
208
|
run_stage(:clean_build_artifacts) { clean_build_artifacts! }
|
|
182
209
|
run_stage(:install_wrapper) { install_wrapper! }
|
|
183
210
|
run_stage(:install_config) { install_default_config! }
|
|
211
|
+
run_stage(:install_systemd) { install_systemd_unit! }
|
|
184
212
|
run_stage(:install_man) { install_man_pages! }
|
|
185
213
|
run_stage(:install_build_info) { install_build_info! }
|
|
214
|
+
run_stage(:rewrite_shebangs) { rewrite_shebangs! }
|
|
186
215
|
run_stage(:package) { build_package! }
|
|
187
216
|
|
|
188
217
|
print_build_summary(:success)
|
|
@@ -295,7 +324,7 @@ module RosettAi
|
|
|
295
324
|
end
|
|
296
325
|
|
|
297
326
|
def check_ronn_available!
|
|
298
|
-
ronn_source = project_root.join('doc', 'man', '
|
|
327
|
+
ronn_source = project_root.join('doc', 'man', 'raictl.1.ronn')
|
|
299
328
|
return unless ronn_source.exist?
|
|
300
329
|
|
|
301
330
|
begin
|
|
@@ -415,13 +444,24 @@ module RosettAi
|
|
|
415
444
|
# -- gem installation --------------------------------------------------
|
|
416
445
|
|
|
417
446
|
def install_gems!
|
|
418
|
-
spinner = build_spinner('Installing gems
|
|
447
|
+
spinner = build_spinner('Installing gems into embedded gem home...')
|
|
448
|
+
ruby_abi = query_embedded_ruby('RbConfig::CONFIG["ruby_version"]')
|
|
449
|
+
gem_home = staging_embedded.join('lib', 'ruby', 'gems', ruby_abi)
|
|
450
|
+
FileUtils.mkdir_p(gem_home)
|
|
419
451
|
embedded_bundle = staging_embedded.join('bin', 'bundle').to_s
|
|
420
452
|
|
|
421
|
-
|
|
453
|
+
# Install runtime deps directly into the embedded Ruby's own gem home.
|
|
454
|
+
# The embedded ruby finds them by default (via GEM_HOME) with no wrapper
|
|
455
|
+
# env vars or bundler/setup at runtime — proving genuine self-containment.
|
|
456
|
+
env = clean_env.merge(
|
|
457
|
+
'PATH' => "#{staging_embedded.join('bin')}:#{HERMETIC_SYSTEM_PATH}",
|
|
458
|
+
'BUNDLE_GEMFILE' => staging_app.join('Gemfile').to_s,
|
|
459
|
+
'GEM_HOME' => gem_home.to_s,
|
|
460
|
+
'GEM_PATH' => gem_home.to_s,
|
|
461
|
+
'LD_LIBRARY_PATH' => staging_embedded.join('lib').to_s
|
|
462
|
+
)
|
|
463
|
+
|
|
422
464
|
cmds = [
|
|
423
|
-
[env, embedded_bundle, 'config', 'set', '--local', 'deployment', 'true'],
|
|
424
|
-
[env, embedded_bundle, 'config', 'set', '--local', 'path', 'vendor/bundle'],
|
|
425
465
|
[env, embedded_bundle, 'config', 'set', '--local', 'without', 'development test build desktop engines'],
|
|
426
466
|
[env, embedded_bundle, 'config', 'set', '--local', 'jobs', jobs_count.to_s],
|
|
427
467
|
[env, embedded_bundle, 'install']
|
|
@@ -433,8 +473,8 @@ module RosettAi
|
|
|
433
473
|
spinner.error(Rainbow(' failed').red)
|
|
434
474
|
log_bundle_debug(env, embedded_bundle)
|
|
435
475
|
abort_build(<<~MSG.chomp)
|
|
436
|
-
Gem installation failed.
|
|
437
|
-
The embedded Ruby bundler could not
|
|
476
|
+
Gem installation into embedded gem home failed.
|
|
477
|
+
The embedded Ruby bundler could not install gems into #{gem_home}.
|
|
438
478
|
This usually means Bundler environment variables from the parent
|
|
439
479
|
process leaked into the build. Check the build log for details.
|
|
440
480
|
MSG
|
|
@@ -463,8 +503,8 @@ module RosettAi
|
|
|
463
503
|
end
|
|
464
504
|
|
|
465
505
|
def collect_build_artifacts
|
|
466
|
-
Dir.glob(
|
|
467
|
-
Dir.glob(
|
|
506
|
+
Dir.glob(staging_embedded.join('lib', 'ruby', 'gems', '**', '{gem_make.out,mkmf.log}')) +
|
|
507
|
+
Dir.glob(staging_embedded.join('lib', 'ruby', 'gems', '**', 'ext', '**', 'Makefile'))
|
|
468
508
|
end
|
|
469
509
|
|
|
470
510
|
def remove_artifact(path)
|
|
@@ -494,15 +534,6 @@ module RosettAi
|
|
|
494
534
|
# 1. Unsets all Bundler/RubyGems vars inherited from `bundle exec`
|
|
495
535
|
# 2. Sets PATH to prefer the embedded Ruby
|
|
496
536
|
# 3. Points BUNDLE_GEMFILE at the staging app's Gemfile
|
|
497
|
-
def embedded_env
|
|
498
|
-
env = clean_env.merge(
|
|
499
|
-
'PATH' => "#{staging_embedded.join('bin')}:#{HERMETIC_SYSTEM_PATH}",
|
|
500
|
-
'BUNDLE_GEMFILE' => staging_app.join('Gemfile').to_s
|
|
501
|
-
)
|
|
502
|
-
log_to_file("embedded_env: #{env.inspect}")
|
|
503
|
-
env
|
|
504
|
-
end
|
|
505
|
-
|
|
506
537
|
# Strips all Bundler/RubyGems variables. When passed to system(),
|
|
507
538
|
# nil values cause the variable to be unset in the child process.
|
|
508
539
|
def clean_env
|
|
@@ -560,10 +591,25 @@ module RosettAi
|
|
|
560
591
|
log_verbose("Installed conffile to #{etc_dir.join('settings.json')}")
|
|
561
592
|
end
|
|
562
593
|
|
|
594
|
+
# -- systemd -----------------------------------------------------------
|
|
595
|
+
|
|
596
|
+
def install_systemd_unit!
|
|
597
|
+
systemd_src = packaging_dir.join('lib', 'systemd')
|
|
598
|
+
return log_verbose('No systemd unit source found — skipping') unless systemd_src.directory?
|
|
599
|
+
|
|
600
|
+
systemd_dest = staging_install.join('lib', 'systemd')
|
|
601
|
+
FileUtils.mkdir_p(systemd_dest)
|
|
602
|
+
|
|
603
|
+
Dir.glob(systemd_src.join('*').to_s).each do |src_path|
|
|
604
|
+
FileUtils.cp_r(src_path, systemd_dest)
|
|
605
|
+
log_verbose("Installed #{File.basename(src_path)} to #{systemd_dest}")
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
563
609
|
# -- man pages ---------------------------------------------------------
|
|
564
610
|
|
|
565
611
|
def install_man_pages!
|
|
566
|
-
ronn_source = project_root.join('doc', 'man', '
|
|
612
|
+
ronn_source = project_root.join('doc', 'man', 'raictl.1.ronn')
|
|
567
613
|
return log_verbose('No man page source found — skipping') unless ronn_source.exist?
|
|
568
614
|
|
|
569
615
|
man_dir = staging_dir.join('usr', 'share', 'man', 'man1')
|
|
@@ -590,6 +636,39 @@ module RosettAi
|
|
|
590
636
|
log_verbose("Installed build info to #{dest}")
|
|
591
637
|
end
|
|
592
638
|
|
|
639
|
+
# -- shebang rewrite ---------------------------------------------------
|
|
640
|
+
|
|
641
|
+
# ruby-build compiles with the staging prefix as --prefix, so scripts
|
|
642
|
+
# in embedded/bin/ (gem, bundle, rake, irb, …) carry the build-time
|
|
643
|
+
# staging path in their shebang. Rewrite all ruby-shebang scripts to
|
|
644
|
+
# the runtime path AFTER gem installation (which still needs the
|
|
645
|
+
# staging shebang to run bundle) and BEFORE packaging.
|
|
646
|
+
def rewrite_shebangs!
|
|
647
|
+
spinner = build_spinner('Rewriting embedded bin shebangs...')
|
|
648
|
+
bin_dir = staging_embedded.join('bin')
|
|
649
|
+
count = count_shebang_rewrites(bin_dir, '#!/opt/rosett-ai/embedded/bin/ruby')
|
|
650
|
+
log_verbose("Rewrote #{count} shebang(s) in #{bin_dir}")
|
|
651
|
+
spinner.success(Rainbow(" done (#{count} scripts)").green)
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
def count_shebang_rewrites(bin_dir, target)
|
|
655
|
+
return 0 unless bin_dir.directory?
|
|
656
|
+
|
|
657
|
+
bin_dir.each_child.count { |path| !path.directory? && rewrite_ruby_shebang!(path, target) }
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def rewrite_ruby_shebang!(path, target)
|
|
661
|
+
content = path.binread
|
|
662
|
+
return unless content.start_with?('#!')
|
|
663
|
+
|
|
664
|
+
nl = content.index("\n") || content.length
|
|
665
|
+
first_line = content[0, nl]
|
|
666
|
+
return unless first_line.include?('ruby') && first_line.strip != target
|
|
667
|
+
|
|
668
|
+
path.binwrite("#{target}#{content[nl..]}")
|
|
669
|
+
true
|
|
670
|
+
end
|
|
671
|
+
|
|
593
672
|
def build_info_content
|
|
594
673
|
<<~INFO
|
|
595
674
|
version=#{RosettAi::VERSION}
|
|
@@ -741,8 +820,10 @@ module RosettAi
|
|
|
741
820
|
install_gems: 'Install gems (deployment)',
|
|
742
821
|
install_wrapper: 'Install wrapper script',
|
|
743
822
|
install_config: 'Install default config',
|
|
823
|
+
install_systemd: 'Install systemd unit',
|
|
744
824
|
install_man: 'Install man pages',
|
|
745
825
|
install_build_info: 'Install build info',
|
|
826
|
+
rewrite_shebangs: 'Rewrite embedded bin shebangs',
|
|
746
827
|
package: 'Build .deb with fpm'
|
|
747
828
|
}.fetch(stage, stage.to_s)
|
|
748
829
|
end
|
|
@@ -848,6 +929,7 @@ module RosettAi
|
|
|
848
929
|
|
|
849
930
|
default_task :create
|
|
850
931
|
|
|
932
|
+
# @return [String] Base installation path for engine plugins.
|
|
851
933
|
INSTALL_BASE = '/opt/rosett-ai/embedded/lib/ruby/gems'
|
|
852
934
|
|
|
853
935
|
desc 'create', 'Build a .deb package for an rosett-ai engine plugin'
|
|
@@ -885,6 +967,9 @@ module RosettAi
|
|
|
885
967
|
default: 1,
|
|
886
968
|
desc: 'Package build iteration (release number)'
|
|
887
969
|
|
|
970
|
+
# Build a .deb package for an engine plugin.
|
|
971
|
+
#
|
|
972
|
+
# @return [void]
|
|
888
973
|
def create
|
|
889
974
|
resolve_engine_dir!
|
|
890
975
|
detect_gemspec!
|
|
@@ -30,6 +30,7 @@ module RosettAi
|
|
|
30
30
|
--simulate: Dry run with diffs, no file writes
|
|
31
31
|
--vendor: Write compile.lock.yml lockfile
|
|
32
32
|
--engine NAME: Compile for specific engine
|
|
33
|
+
--scope SCOPE: Compile scope (global/project/auto)
|
|
33
34
|
--strict: Treat warnings as errors
|
|
34
35
|
--locales: Compile locale files to gettext/Qt formats
|
|
35
36
|
|
|
@@ -39,6 +40,8 @@ module RosettAi
|
|
|
39
40
|
|
|
40
41
|
raictl compile
|
|
41
42
|
raictl compile --verbose --simulate
|
|
43
|
+
raictl compile --scope global
|
|
44
|
+
raictl compile --scope project
|
|
42
45
|
raictl compile --engine generic --strict
|
|
43
46
|
raictl compile --vendor
|
|
44
47
|
raictl compile --locales
|
|
@@ -73,11 +76,18 @@ module RosettAi
|
|
|
73
76
|
type: :boolean,
|
|
74
77
|
default: false,
|
|
75
78
|
desc: 'Treat warnings as errors'
|
|
79
|
+
method_option :scope,
|
|
80
|
+
type: :string,
|
|
81
|
+
default: 'auto',
|
|
82
|
+
enum: ['global', 'project', 'auto'],
|
|
83
|
+
desc: 'Compile scope: global (~/.claude/rules/), project (.claude/rules/), or auto-detect'
|
|
76
84
|
method_option :locales,
|
|
77
85
|
type: :boolean,
|
|
78
86
|
default: false,
|
|
79
87
|
desc: 'Compile locale files to gettext and Qt formats'
|
|
80
88
|
|
|
89
|
+
# Generate the compiled output.
|
|
90
|
+
# @return [void]
|
|
81
91
|
def generate
|
|
82
92
|
if options[:locales]
|
|
83
93
|
compile_locales
|
|
@@ -153,8 +163,17 @@ module RosettAi
|
|
|
153
163
|
end
|
|
154
164
|
|
|
155
165
|
def detect_compile_scope
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
case options[:scope]
|
|
167
|
+
when 'global'
|
|
168
|
+
:global
|
|
169
|
+
when 'project'
|
|
170
|
+
raise RosettAi::CompileError, t('compile.scope_project_no_project') unless RosettAi.context.project?
|
|
171
|
+
|
|
172
|
+
:project
|
|
173
|
+
else # 'auto'
|
|
174
|
+
ctx = RosettAi.context
|
|
175
|
+
ctx.project? && !ctx.rai_internal? ? :project : :global
|
|
176
|
+
end
|
|
158
177
|
end
|
|
159
178
|
|
|
160
179
|
def check_xdg_migration
|
|
@@ -236,30 +255,18 @@ module RosettAi
|
|
|
236
255
|
RosettAi.paths.rules_dir
|
|
237
256
|
end
|
|
238
257
|
|
|
239
|
-
# Resolves the target directory for a compiled output
|
|
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.
|
|
258
|
+
# Resolves the target directory for a compiled output.
|
|
242
259
|
#
|
|
243
|
-
#
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
#
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
|
260
|
+
# Always returns the pipeline's target_dir to prevent cross-directory
|
|
261
|
+
# writes. The pipeline scope (global vs project) already determines
|
|
262
|
+
# the correct output directory. Per-file scope metadata is preserved
|
|
263
|
+
# in lockfile output but does not affect write location.
|
|
264
|
+
#
|
|
265
|
+
# Previously, per-file scope: global/personal routed to
|
|
266
|
+
# ~/.claude/rules/ even during project-scope compilation, causing
|
|
267
|
+
# Claude Code to load duplicate rules from both directories.
|
|
268
|
+
def resolve_scoped_target_dir(compiler, _info)
|
|
269
|
+
compiler.target_dir
|
|
263
270
|
end
|
|
264
271
|
|
|
265
272
|
def compile_locales
|
|
@@ -296,10 +303,17 @@ module RosettAi
|
|
|
296
303
|
|
|
297
304
|
compiled.each do |filename, info|
|
|
298
305
|
target_dir = resolve_scoped_target_dir(compiler, info)
|
|
299
|
-
target_path = target_dir.join(filename)
|
|
300
306
|
content = info[:content]
|
|
301
307
|
new_checksum = compiler.checksum(content)
|
|
302
308
|
|
|
309
|
+
if deduplicate_against_global?(compiler, target_dir, filename, new_checksum)
|
|
310
|
+
cleanup_stale_project_copy(target_dir, filename) unless options[:simulate]
|
|
311
|
+
results[:skipped] << filename
|
|
312
|
+
log_verbose(t('compile.deduplicated_detail', filename: filename))
|
|
313
|
+
next
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
target_path = target_dir.join(filename)
|
|
303
317
|
ctx = CompileFileContext.new(
|
|
304
318
|
target_path: target_path, content: content, checksum: new_checksum, filename: filename
|
|
305
319
|
)
|
|
@@ -313,6 +327,32 @@ module RosettAi
|
|
|
313
327
|
results
|
|
314
328
|
end
|
|
315
329
|
|
|
330
|
+
# Checks whether a file should be deduplicated against the global
|
|
331
|
+
# rules directory. In project scope, if a compiled file is identical
|
|
332
|
+
# to its global counterpart in ~/.claude/rules/, writing it to the
|
|
333
|
+
# project .claude/rules/ would cause Claude Code to load it twice.
|
|
334
|
+
#
|
|
335
|
+
# @return [Boolean] true if the file should be skipped
|
|
336
|
+
def deduplicate_against_global?(compiler, target_dir, filename, checksum)
|
|
337
|
+
return false unless compiler.scope == :project
|
|
338
|
+
|
|
339
|
+
global_dir = RosettAi.paths.rules_dir
|
|
340
|
+
return false if target_dir.to_s == global_dir.to_s
|
|
341
|
+
|
|
342
|
+
global_file = global_dir.join(filename)
|
|
343
|
+
return false unless global_file.exist?
|
|
344
|
+
|
|
345
|
+
global_checksum = compiler.checksum(File.read(global_file, encoding: 'utf-8'))
|
|
346
|
+
checksum == global_checksum
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Removes a stale project-scope copy that was previously compiled
|
|
350
|
+
# but is now deduplicated against the global version.
|
|
351
|
+
def cleanup_stale_project_copy(target_dir, filename)
|
|
352
|
+
stale_path = target_dir.join(filename)
|
|
353
|
+
File.delete(stale_path) if stale_path.exist?
|
|
354
|
+
end
|
|
355
|
+
|
|
316
356
|
def handle_simulate(compiler, ctx, results)
|
|
317
357
|
if File.exist?(ctx.target_path)
|
|
318
358
|
diff_output = compiler.diff(ctx.target_path, ctx.content)
|
|
@@ -366,7 +406,7 @@ module RosettAi
|
|
|
366
406
|
def remove_orphans(compiler, compiled)
|
|
367
407
|
# Guard: when compile produced no output (e.g. empty project conf/),
|
|
368
408
|
# every managed file in target_dir would appear orphaned. Removing
|
|
369
|
-
# them would silently destroy all compiled rules.
|
|
409
|
+
# them would silently destroy all compiled rules.
|
|
370
410
|
return if compiled.empty?
|
|
371
411
|
|
|
372
412
|
orphans = compiler.orphaned_files(compiled.keys)
|
|
@@ -389,11 +429,15 @@ module RosettAi
|
|
|
389
429
|
end
|
|
390
430
|
|
|
391
431
|
def print_summary(results)
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
432
|
+
label_map = {
|
|
433
|
+
created: t('compile.created'),
|
|
434
|
+
updated: t('compile.updated'),
|
|
435
|
+
unchanged: t('compile.unchanged', filename: ''),
|
|
436
|
+
skipped: t('compile.deduplicated')
|
|
437
|
+
}
|
|
438
|
+
rows = label_map.filter_map do |key, label|
|
|
439
|
+
[label, results[key].size] unless results[key].empty?
|
|
440
|
+
end
|
|
397
441
|
return if rows.empty?
|
|
398
442
|
|
|
399
443
|
puts
|
|
@@ -15,6 +15,7 @@ module RosettAi
|
|
|
15
15
|
# @author hugo
|
|
16
16
|
# @author claude
|
|
17
17
|
class Completion < ::Thor
|
|
18
|
+
# @return [Hash] Shell-specific completion installation paths.
|
|
18
19
|
INSTALL_PATHS = {
|
|
19
20
|
'bash' => '/usr/share/bash-completion/completions/rai',
|
|
20
21
|
'zsh' => '/usr/share/zsh/vendor-completions/_rai',
|
|
@@ -29,6 +30,8 @@ module RosettAi
|
|
|
29
30
|
|
|
30
31
|
Related: completion zsh, completion fish, completion install
|
|
31
32
|
LONGDESC
|
|
33
|
+
# Generate bash shell completion script.
|
|
34
|
+
# @return [void]
|
|
32
35
|
def bash
|
|
33
36
|
output_completion('bash')
|
|
34
37
|
end
|
|
@@ -41,6 +44,8 @@ module RosettAi
|
|
|
41
44
|
|
|
42
45
|
Related: completion bash, completion fish, completion install
|
|
43
46
|
LONGDESC
|
|
47
|
+
# Generate zsh shell completion script.
|
|
48
|
+
# @return [void]
|
|
44
49
|
def zsh
|
|
45
50
|
output_completion('zsh')
|
|
46
51
|
end
|
|
@@ -53,6 +58,8 @@ module RosettAi
|
|
|
53
58
|
|
|
54
59
|
Related: completion bash, completion zsh, completion install
|
|
55
60
|
LONGDESC
|
|
61
|
+
# Generate fish shell completion script.
|
|
62
|
+
# @return [void]
|
|
56
63
|
def fish
|
|
57
64
|
output_completion('fish')
|
|
58
65
|
end
|
|
@@ -78,6 +85,8 @@ module RosettAi
|
|
|
78
85
|
LONGDESC
|
|
79
86
|
method_option :shell, type: :string, default: nil,
|
|
80
87
|
desc: 'Shell to install for (auto-detected from $SHELL)'
|
|
88
|
+
# Install the component.
|
|
89
|
+
# @return [void]
|
|
81
90
|
def install
|
|
82
91
|
shell = detect_shell
|
|
83
92
|
generator = RosettAi::Completion::Generator.new
|
|
@@ -46,6 +46,8 @@ module RosettAi
|
|
|
46
46
|
desc: 'Run SPDX header checks only'
|
|
47
47
|
method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
|
|
48
48
|
desc: 'Output format'
|
|
49
|
+
# Run the check and return results.
|
|
50
|
+
# @return [Hash]
|
|
49
51
|
def check
|
|
50
52
|
checks = selected_checks
|
|
51
53
|
results = RosettAi::Comply::Runner.new(
|
|
@@ -11,7 +11,7 @@ module RosettAi
|
|
|
11
11
|
module Thor
|
|
12
12
|
module Tasks
|
|
13
13
|
# Thor task for compiling engine configuration YAML to settings JSON.
|
|
14
|
-
class Config < ::Thor
|
|
14
|
+
class Config < ::Thor # rubocop:disable Metrics/ClassLength
|
|
15
15
|
default_task :compile
|
|
16
16
|
|
|
17
17
|
desc 'show SCOPE', 'Display compiled configuration for a scope with secrets masked'
|
|
@@ -36,6 +36,10 @@ module RosettAi
|
|
|
36
36
|
method_option :engine, type: :string, default: nil,
|
|
37
37
|
desc: 'Engine name (default: configured default)'
|
|
38
38
|
|
|
39
|
+
# Display detailed information for the named item.
|
|
40
|
+
#
|
|
41
|
+
# @param scope [Object] the scope
|
|
42
|
+
# @return [void]
|
|
39
43
|
def show(scope)
|
|
40
44
|
scope = RosettAi::TextSanitizer.normalize_nfc(scope)
|
|
41
45
|
compiler = resolve_config_compiler(simulate: true, masking: true)
|
|
@@ -58,6 +62,12 @@ module RosettAi
|
|
|
58
62
|
--simulate: Dry run without writing files
|
|
59
63
|
--scope NAME: Compile only one scope (managed, user, project, local)
|
|
60
64
|
--engine NAME: Compile for specific engine
|
|
65
|
+
--strict: Replace target entirely — do not preserve uncategorized entries
|
|
66
|
+
|
|
67
|
+
The default (non-strict) compile MERGES with the existing target file,
|
|
68
|
+
preserving any entries not present in the source YAML scopes and emitting
|
|
69
|
+
a warning for each. Use --strict to restore the old replace-all behaviour.
|
|
70
|
+
Run `raictl backfill-user-yml` to capture preserved entries into source.
|
|
61
71
|
|
|
62
72
|
Exit codes: 1 if any scope fails to compile
|
|
63
73
|
|
|
@@ -66,8 +76,9 @@ module RosettAi
|
|
|
66
76
|
raictl config compile
|
|
67
77
|
raictl config compile --verbose --simulate
|
|
68
78
|
raictl config compile --scope managed --engine claude
|
|
79
|
+
raictl config compile --strict
|
|
69
80
|
|
|
70
|
-
Related commands: raictl config show, rai compile
|
|
81
|
+
Related commands: raictl config show, raictl backfill-user-yml, rai compile
|
|
71
82
|
LONGDESC
|
|
72
83
|
method_option :verbose, type: :boolean, default: false,
|
|
73
84
|
desc: 'Show detailed output'
|
|
@@ -77,9 +88,13 @@ module RosettAi
|
|
|
77
88
|
desc: 'Compile specific scope only (managed, user, project, local)'
|
|
78
89
|
method_option :engine, type: :string, default: nil,
|
|
79
90
|
desc: 'Engine name (default: configured default)'
|
|
91
|
+
method_option :strict, type: :boolean, default: false,
|
|
92
|
+
desc: 'Replace target entirely — do not preserve uncategorized entries'
|
|
80
93
|
|
|
94
|
+
# Compile the configuration.
|
|
95
|
+
# @return [void]
|
|
81
96
|
def compile
|
|
82
|
-
compiler = resolve_config_compiler(simulate: options[:simulate])
|
|
97
|
+
compiler = resolve_config_compiler(simulate: options[:simulate], strict: options[:strict])
|
|
83
98
|
|
|
84
99
|
results = if options[:scope]
|
|
85
100
|
scope_path = find_scope_file(options[:scope], compiler)
|
|
@@ -110,7 +125,7 @@ module RosettAi
|
|
|
110
125
|
options[:engine] || RosettAi.rai_config.default_engine
|
|
111
126
|
end
|
|
112
127
|
|
|
113
|
-
def resolve_config_compiler(simulate: false, masking: false)
|
|
128
|
+
def resolve_config_compiler(simulate: false, masking: false, strict: false)
|
|
114
129
|
engine_name = resolve_engine_name
|
|
115
130
|
|
|
116
131
|
unless RosettAi::Plugins::Registry.registered?(:engine, engine_name)
|
|
@@ -128,7 +143,8 @@ module RosettAi
|
|
|
128
143
|
end
|
|
129
144
|
|
|
130
145
|
secret_resolver = masking ? RosettAi::Config::MaskingSecretResolver.new : nil
|
|
131
|
-
plugin.config_compiler_class.new(simulate: simulate, secret_resolver: secret_resolver
|
|
146
|
+
plugin.config_compiler_class.new(simulate: simulate, secret_resolver: secret_resolver,
|
|
147
|
+
strict: strict)
|
|
132
148
|
end
|
|
133
149
|
|
|
134
150
|
def engine_hint(available, engine_name)
|
|
@@ -32,6 +32,8 @@ module RosettAi
|
|
|
32
32
|
LONGDESC
|
|
33
33
|
method_option :available, type: :boolean, default: false,
|
|
34
34
|
desc: 'Show all available packs with tier labels'
|
|
35
|
+
# List available items.
|
|
36
|
+
# @return [void]
|
|
35
37
|
def list
|
|
36
38
|
if options[:available]
|
|
37
39
|
list_available
|
|
@@ -53,6 +55,10 @@ module RosettAi
|
|
|
53
55
|
|
|
54
56
|
Related commands: raictl content list --available, rai license status
|
|
55
57
|
LONGDESC
|
|
58
|
+
# Install the component.
|
|
59
|
+
#
|
|
60
|
+
# @param pack_name [Object] the pack name
|
|
61
|
+
# @return [void]
|
|
56
62
|
def install(pack_name)
|
|
57
63
|
result = validate_license!
|
|
58
64
|
tier = result[:tier]
|
|
@@ -85,6 +91,8 @@ module RosettAi
|
|
|
85
91
|
|
|
86
92
|
Related commands: raictl content list, rai license status
|
|
87
93
|
LONGDESC
|
|
94
|
+
# Update to the latest version.
|
|
95
|
+
# @return [void]
|
|
88
96
|
def update
|
|
89
97
|
validate_license!
|
|
90
98
|
registry = RosettAi::Content::PackRegistry.new
|
|
@@ -21,14 +21,19 @@ module RosettAi
|
|
|
21
21
|
# @author hugo
|
|
22
22
|
# @author claude
|
|
23
23
|
class DBus < ::Thor
|
|
24
|
+
# @return [Integer] Exit code for successful D-Bus operation.
|
|
24
25
|
EXIT_SUCCESS = 0
|
|
26
|
+
# @return [Integer] Exit code for D-Bus connection failure.
|
|
25
27
|
EXIT_DBUS_FAILURE = 1
|
|
28
|
+
# @return [Integer] Exit code for missing D-Bus dependency.
|
|
26
29
|
EXIT_MISSING_DEPENDENCY = 5
|
|
27
30
|
|
|
31
|
+
# @return [Array] Environment variables checked for compositor detection.
|
|
28
32
|
ENV_VARS = [
|
|
29
33
|
'SWAYSOCK', 'I3SOCK', 'HYPRLAND_INSTANCE_SIGNATURE', 'DISPLAY', 'WAYLAND_DISPLAY', 'XDG_RUNTIME_DIR', 'XDG_SESSION_TYPE'
|
|
30
34
|
].freeze
|
|
31
35
|
|
|
36
|
+
# @return [Hash] Maps compositor names to focus adapter classes.
|
|
32
37
|
ADAPTER_MAP = {
|
|
33
38
|
sway: 'I3Adapter (i3ipc)',
|
|
34
39
|
i3: 'I3Adapter (i3ipc)',
|
|
@@ -54,6 +59,8 @@ module RosettAi
|
|
|
54
59
|
|
|
55
60
|
Related commands: raictl dbus start, rai dbus detect
|
|
56
61
|
LONGDESC
|
|
62
|
+
# Return the current status.
|
|
63
|
+
# @return [String]
|
|
57
64
|
def status
|
|
58
65
|
require_desktop_gems!
|
|
59
66
|
check_dbus_availability
|
|
@@ -90,6 +97,8 @@ module RosettAi
|
|
|
90
97
|
default: false,
|
|
91
98
|
aliases: '-d',
|
|
92
99
|
desc: 'Run as daemon (background)'
|
|
100
|
+
# Start the adapter or service.
|
|
101
|
+
# @return [void]
|
|
93
102
|
def start
|
|
94
103
|
require_desktop_gems!
|
|
95
104
|
check_dbus_availability
|
|
@@ -121,6 +130,8 @@ module RosettAi
|
|
|
121
130
|
|
|
122
131
|
Related commands: raictl dbus start, rai dbus status
|
|
123
132
|
LONGDESC
|
|
133
|
+
# Stop the adapter or service.
|
|
134
|
+
# @return [void]
|
|
124
135
|
def stop
|
|
125
136
|
require_desktop_gems!
|
|
126
137
|
unless service_running?
|
|
@@ -145,6 +156,8 @@ module RosettAi
|
|
|
145
156
|
|
|
146
157
|
Related commands: raictl dbus status, rai dbus start
|
|
147
158
|
LONGDESC
|
|
159
|
+
# Detect available engines on the system.
|
|
160
|
+
# @return [Array<Hash>]
|
|
148
161
|
def detect
|
|
149
162
|
require_desktop_gems!
|
|
150
163
|
compositor = RosettAi::DBus::CompositorDetector.detect
|
|
@@ -29,6 +29,8 @@ module RosettAi
|
|
|
29
29
|
|
|
30
30
|
Related commands: raictl desktop status, rai dbus start
|
|
31
31
|
LONGDESC
|
|
32
|
+
# Launch the GTK4 desktop application.
|
|
33
|
+
# @return [void]
|
|
32
34
|
def gtk4
|
|
33
35
|
require_desktop_gems!
|
|
34
36
|
unless RosettAi::Desktop::Gtk4App.available?
|
|
@@ -52,6 +54,8 @@ module RosettAi
|
|
|
52
54
|
|
|
53
55
|
Related commands: raictl desktop gtk4, rai dbus status
|
|
54
56
|
LONGDESC
|
|
57
|
+
# Return the current status.
|
|
58
|
+
# @return [String]
|
|
55
59
|
def status
|
|
56
60
|
require_desktop_gems!
|
|
57
61
|
gtk4_available = RosettAi::Desktop::Gtk4App.available?
|