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,44 @@
|
|
|
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 AiConfig
|
|
8
|
+
# Advisory cost tier preferences for AI tool usage.
|
|
9
|
+
#
|
|
10
|
+
# Cost controls are purely advisory — rosett-ai is a compiler, not a runtime.
|
|
11
|
+
# These preferences guide model selection and are compiled into
|
|
12
|
+
# engine-specific configuration hints.
|
|
13
|
+
class CostControls
|
|
14
|
+
TIERS = ['economy', 'standard', 'premium'].freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :preferred_tier, :monthly_budget_note
|
|
17
|
+
|
|
18
|
+
# @param preferred_tier [String] one of {TIERS}
|
|
19
|
+
# @param monthly_budget_note [String, nil] advisory budget note
|
|
20
|
+
def initialize(preferred_tier: 'standard', monthly_budget_note: nil)
|
|
21
|
+
validate_tier!(preferred_tier)
|
|
22
|
+
|
|
23
|
+
@preferred_tier = preferred_tier.freeze
|
|
24
|
+
@monthly_budget_note = monthly_budget_note&.freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [Hash] serializable representation
|
|
28
|
+
def to_h
|
|
29
|
+
hash = { 'preferred_tier' => @preferred_tier }
|
|
30
|
+
hash['monthly_budget_note'] = @monthly_budget_note if @monthly_budget_note
|
|
31
|
+
hash
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def validate_tier!(tier)
|
|
37
|
+
return if TIERS.include?(tier)
|
|
38
|
+
|
|
39
|
+
raise ArgumentError,
|
|
40
|
+
"Invalid cost tier '#{tier}'. Allowed: #{TIERS.join(', ')}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
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 AiConfig
|
|
8
|
+
# Ordered engine fallback chain with local-to-remote transition warnings.
|
|
9
|
+
#
|
|
10
|
+
# When the primary engine is unavailable, the chain specifies fallback
|
|
11
|
+
# engines in order. Warns when a fallback would switch from local to
|
|
12
|
+
# remote (network requirement change).
|
|
13
|
+
class FallbackChain
|
|
14
|
+
LOCAL_ENGINES = ['ollama', 'gpt_neox'].freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :engines, :warnings
|
|
17
|
+
|
|
18
|
+
# @param engines [Array<String>] ordered engine identifiers
|
|
19
|
+
def initialize(engines: [])
|
|
20
|
+
@engines = engines.freeze
|
|
21
|
+
@warnings = detect_transition_warnings.freeze
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [String, nil] primary engine (first in chain)
|
|
25
|
+
def primary
|
|
26
|
+
@engines.first
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Array<String>] fallback engines (all after primary)
|
|
30
|
+
def fallbacks
|
|
31
|
+
@engines.drop(1)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Boolean] true if chain contains local-to-remote transitions
|
|
35
|
+
def local_to_remote_transition?
|
|
36
|
+
!@warnings.empty?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Hash] serializable representation
|
|
40
|
+
def to_h
|
|
41
|
+
{
|
|
42
|
+
'engines' => @engines.dup,
|
|
43
|
+
'warnings' => @warnings.dup
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def detect_transition_warnings
|
|
50
|
+
warnings = []
|
|
51
|
+
@engines.each_cons(2) do |current, fallback|
|
|
52
|
+
next unless local_engine?(current) && !local_engine?(fallback)
|
|
53
|
+
|
|
54
|
+
warnings << "Fallback from local (#{current}) to remote (#{fallback}) — network required"
|
|
55
|
+
end
|
|
56
|
+
warnings
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def local_engine?(engine)
|
|
60
|
+
LOCAL_ENGINES.include?(engine)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
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 AiConfig
|
|
8
|
+
# Maps canonical model tiers to engine-specific model identifiers.
|
|
9
|
+
#
|
|
10
|
+
# Supports three standard tiers (economy, standard, premium) plus
|
|
11
|
+
# custom model names. Engine manifests declare their model mappings.
|
|
12
|
+
# Use {.with_manifest_mappings} to load mappings from installed engines.
|
|
13
|
+
#
|
|
14
|
+
# @author hugo
|
|
15
|
+
# @author claude
|
|
16
|
+
class ModelRouter
|
|
17
|
+
CANONICAL_TIERS = ['economy', 'standard', 'premium'].freeze
|
|
18
|
+
|
|
19
|
+
# Default model mappings per engine (used when engine manifest unavailable).
|
|
20
|
+
DEFAULT_MAPPINGS = {
|
|
21
|
+
'claude' => {
|
|
22
|
+
'economy' => 'claude-haiku-4-5-20251001',
|
|
23
|
+
'standard' => 'claude-sonnet-4-5-20250929',
|
|
24
|
+
'premium' => 'claude-opus-4-6'
|
|
25
|
+
},
|
|
26
|
+
'ollama' => {
|
|
27
|
+
'economy' => 'llama3.2:3b',
|
|
28
|
+
'standard' => 'llama3.3:8b',
|
|
29
|
+
'premium' => 'llama3.3:70b'
|
|
30
|
+
}
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
# Builds a ModelRouter that merges manifest-declared model
|
|
34
|
+
# mappings from installed engines with DEFAULT_MAPPINGS.
|
|
35
|
+
#
|
|
36
|
+
# @param extra_mappings [Hash] additional overrides on top of manifest data
|
|
37
|
+
# @return [ModelRouter]
|
|
38
|
+
def self.with_manifest_mappings(extra_mappings: {})
|
|
39
|
+
manifest_mappings = load_manifest_mappings
|
|
40
|
+
new(custom_mappings: manifest_mappings.merge(extra_mappings))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param custom_mappings [Hash] additional engine model mappings
|
|
44
|
+
def initialize(custom_mappings: {})
|
|
45
|
+
@mappings = DEFAULT_MAPPINGS.merge(custom_mappings)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Resolves a canonical tier to an engine-specific model ID.
|
|
49
|
+
#
|
|
50
|
+
# @param tier [String] canonical tier name or "custom:<name>"
|
|
51
|
+
# @param engine [String] engine identifier
|
|
52
|
+
# @return [String] resolved model identifier
|
|
53
|
+
# @raise [RosettAi::AiConfigError] if tier is not recognised
|
|
54
|
+
def resolve(tier, engine:)
|
|
55
|
+
return extract_custom_model(tier) if tier.start_with?('custom:')
|
|
56
|
+
|
|
57
|
+
validate_tier!(tier)
|
|
58
|
+
engine_map = @mappings.fetch(engine, {})
|
|
59
|
+
engine_map.fetch(tier) do
|
|
60
|
+
raise RosettAi::AiConfigError,
|
|
61
|
+
"No model mapping for tier '#{tier}' on engine '#{engine}'"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Lists all available tiers for an engine.
|
|
66
|
+
#
|
|
67
|
+
# @param engine [String] engine identifier
|
|
68
|
+
# @return [Array<String>] available tier names
|
|
69
|
+
def available_tiers(engine)
|
|
70
|
+
engine_map = @mappings.fetch(engine, {})
|
|
71
|
+
engine_map.keys
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @param engine [String] engine identifier
|
|
75
|
+
# @return [Boolean] true if engine has model mappings
|
|
76
|
+
def engine_supported?(engine)
|
|
77
|
+
@mappings.key?(engine)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Resolves a canonical tier across all known engines.
|
|
81
|
+
#
|
|
82
|
+
# @param tier [String] canonical tier name
|
|
83
|
+
# @return [Hash<String, String>] engine name => resolved model ID
|
|
84
|
+
def resolve_all(tier)
|
|
85
|
+
validate_tier!(tier)
|
|
86
|
+
@mappings.each_with_object({}) do |(engine, map), result|
|
|
87
|
+
result[engine] = map[tier] if map.key?(tier)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def validate_tier!(tier)
|
|
94
|
+
return if CANONICAL_TIERS.include?(tier)
|
|
95
|
+
|
|
96
|
+
raise RosettAi::AiConfigError,
|
|
97
|
+
"Invalid model tier '#{tier}'. Allowed: #{CANONICAL_TIERS.join(', ')}, or custom:<name>"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def extract_custom_model(tier)
|
|
101
|
+
tier.delete_prefix('custom:')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class << self
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def load_manifest_mappings
|
|
108
|
+
mappings = {}
|
|
109
|
+
RosettAi::Engines::Registry.available.each do |engine_name|
|
|
110
|
+
manifest = RosettAi::Engines::Registry.manifest(engine_name)
|
|
111
|
+
routing = manifest['model_routing']
|
|
112
|
+
mappings[engine_name] = routing if routing.is_a?(Hash)
|
|
113
|
+
rescue RosettAi::Error
|
|
114
|
+
next
|
|
115
|
+
end
|
|
116
|
+
mappings
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'json_schemer'
|
|
8
|
+
|
|
9
|
+
module RosettAi
|
|
10
|
+
module AiConfig
|
|
11
|
+
# Validates AI tool configuration files against the JSON Schema.
|
|
12
|
+
class Validator
|
|
13
|
+
SCHEMA_PATH = File.join(RosettAi.root, 'conf', 'schemas', 'ai_config_schema.json')
|
|
14
|
+
|
|
15
|
+
# Validates AI config data against the schema.
|
|
16
|
+
#
|
|
17
|
+
# @param data [Hash] parsed ai_config.yml data
|
|
18
|
+
# @return [Array<String>] list of validation errors (empty if valid)
|
|
19
|
+
def validate(data)
|
|
20
|
+
schema = load_schema
|
|
21
|
+
schemer = JSONSchemer.schema(schema)
|
|
22
|
+
errors = schemer.validate(data)
|
|
23
|
+
errors.map { |error| format_error(error) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @param data [Hash] AI config data
|
|
27
|
+
# @return [Boolean] true if data is valid
|
|
28
|
+
def valid?(data)
|
|
29
|
+
validate(data).empty?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def load_schema
|
|
35
|
+
JSON.parse(File.read(SCHEMA_PATH))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def format_error(error)
|
|
39
|
+
path = error['data_pointer']
|
|
40
|
+
message = error['type']
|
|
41
|
+
"#{path}: #{message}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
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 Authorship
|
|
8
|
+
# Compiles authorship attribution sections for engine output.
|
|
9
|
+
#
|
|
10
|
+
# Generates markdown sections for CLAUDE.md, AGENTS.md, and other
|
|
11
|
+
# engine-specific output based on the active disclosure level.
|
|
12
|
+
# Ensures compiled metadata never exceeds what the disclosure level
|
|
13
|
+
# permits — per-file details are stripped at any level below full.
|
|
14
|
+
#
|
|
15
|
+
# @author hugo
|
|
16
|
+
# @author claude
|
|
17
|
+
class AttributionCompiler
|
|
18
|
+
FILES_KEYS = ['files', :files].freeze
|
|
19
|
+
|
|
20
|
+
# @param disclosure_policy [DisclosurePolicy] active disclosure policy
|
|
21
|
+
def initialize(disclosure_policy:)
|
|
22
|
+
@policy = disclosure_policy
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Compiles an attribution section for inclusion in engine output.
|
|
26
|
+
#
|
|
27
|
+
# @param engine_name [String] active engine display name
|
|
28
|
+
# @param provider [String] engine provider
|
|
29
|
+
# @param provenance_entries [Array<Hash>] recent provenance entries
|
|
30
|
+
# @return [Hash] with +:section+ (String, nil) and +:warnings+ (Array<String>)
|
|
31
|
+
def compile(engine_name:, provider:, provenance_entries: [])
|
|
32
|
+
warnings = []
|
|
33
|
+
return { section: nil, warnings: warnings } unless @policy.disclose?
|
|
34
|
+
|
|
35
|
+
safe_entries = sanitize_entries(provenance_entries)
|
|
36
|
+
section = build_section(engine_name, provider, safe_entries, warnings)
|
|
37
|
+
{ section: section, warnings: warnings }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Strips per-file data from entries when disclosure level does not
|
|
43
|
+
# permit it, preventing metadata leakage.
|
|
44
|
+
def sanitize_entries(entries)
|
|
45
|
+
return entries if @policy.include_per_file?
|
|
46
|
+
|
|
47
|
+
entries.map { |e| e.reject { |k| FILES_KEYS.include?(k) } }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def build_section(engine_name, provider, entries, warnings)
|
|
51
|
+
lines = []
|
|
52
|
+
|
|
53
|
+
if @policy.include_attribution?
|
|
54
|
+
lines << '## AI Attribution'
|
|
55
|
+
lines << ''
|
|
56
|
+
lines << "This project uses AI-assisted development via #{engine_name} (#{provider})."
|
|
57
|
+
lines << 'The human operator is always the accountable author.'
|
|
58
|
+
lines << 'AI tools are not listed as copyright holders.'
|
|
59
|
+
elsif @policy.include_trailer_guidance?
|
|
60
|
+
lines << "<!-- AI-assisted development via #{engine_name} (#{provider}) -->"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
append_trailer_guidance(lines) if @policy.include_trailer_guidance?
|
|
64
|
+
append_per_file_summary(lines, entries, warnings) if @policy.include_per_file?
|
|
65
|
+
|
|
66
|
+
lines.join("\n")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def append_trailer_guidance(lines)
|
|
70
|
+
lines << ''
|
|
71
|
+
lines << '### Commit Trailers'
|
|
72
|
+
lines << ''
|
|
73
|
+
lines << 'Use standardised commit trailers to attribute AI involvement:'
|
|
74
|
+
lines << ''
|
|
75
|
+
lines << '- `AI-Generated-By`: code primarily generated by AI'
|
|
76
|
+
lines << '- `AI-Co-Author`: code co-authored with AI'
|
|
77
|
+
lines << '- `AI-Assisted-By`: AI used for suggestions or review'
|
|
78
|
+
lines << '- `AI-Reviewed-By`: AI reviewed human-written code'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def append_per_file_summary(lines, entries, _warnings)
|
|
82
|
+
return if entries.empty?
|
|
83
|
+
|
|
84
|
+
lines << ''
|
|
85
|
+
lines << '### Per-File AI Involvement'
|
|
86
|
+
lines << ''
|
|
87
|
+
|
|
88
|
+
entries.each do |entry|
|
|
89
|
+
role = entry['ai_role'] || entry[:ai_role]
|
|
90
|
+
files = entry['files'] || entry[:files] || []
|
|
91
|
+
files.each do |file|
|
|
92
|
+
path = file['path'] || file[:path]
|
|
93
|
+
lines << "- `#{path}`: #{role}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
module RosettAi
|
|
7
|
+
module Authorship
|
|
8
|
+
# Manages disclosure levels for AI authorship attribution.
|
|
9
|
+
#
|
|
10
|
+
# Four levels control how much AI involvement metadata appears in
|
|
11
|
+
# compiled output and commit messages:
|
|
12
|
+
#
|
|
13
|
+
# - +none+: no authorship metadata emitted
|
|
14
|
+
# - +minimal+: brief note that AI tools were used
|
|
15
|
+
# - +standard+: AI Attribution section in compiled output
|
|
16
|
+
# - +full+: per-file AI involvement summary
|
|
17
|
+
#
|
|
18
|
+
# Disclosure level is read from +.rosett-ai/config.yml+ under
|
|
19
|
+
# +authorship.disclosure+, not from behaviour YAML.
|
|
20
|
+
#
|
|
21
|
+
# @author hugo
|
|
22
|
+
# @author claude
|
|
23
|
+
class DisclosurePolicy
|
|
24
|
+
LEVELS = ['none', 'minimal', 'standard', 'full'].freeze
|
|
25
|
+
DEFAULT_LEVEL = 'standard'
|
|
26
|
+
|
|
27
|
+
attr_reader :level
|
|
28
|
+
|
|
29
|
+
# @param level [String] one of {LEVELS}
|
|
30
|
+
# @raise [ArgumentError] if level is not valid
|
|
31
|
+
def initialize(level: DEFAULT_LEVEL)
|
|
32
|
+
validate_level!(level)
|
|
33
|
+
@level = level.freeze
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Loads the disclosure level from a config data hash.
|
|
37
|
+
#
|
|
38
|
+
# @param data [Hash] configuration hash (e.g. from YAML config file)
|
|
39
|
+
# @return [DisclosurePolicy] policy with configured level
|
|
40
|
+
def self.from_config(data)
|
|
41
|
+
level = data.dig('authorship', 'disclosure') || DEFAULT_LEVEL
|
|
42
|
+
new(level: level)
|
|
43
|
+
rescue ArgumentError
|
|
44
|
+
new(level: DEFAULT_LEVEL)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Boolean] true if any metadata should be emitted
|
|
48
|
+
def disclose?
|
|
49
|
+
@level != 'none'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [Boolean] true if compiled output should include attribution section
|
|
53
|
+
def include_attribution?
|
|
54
|
+
at_least?('standard')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @return [Boolean] true if per-file AI involvement should be included
|
|
58
|
+
def include_per_file?
|
|
59
|
+
at_least?('full')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Boolean] true if trailer guidance should be in compiled output
|
|
63
|
+
def include_trailer_guidance?
|
|
64
|
+
at_least?('minimal')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def at_least?(minimum)
|
|
70
|
+
LEVELS.index(@level) >= LEVELS.index(minimum)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def validate_level!(level)
|
|
74
|
+
return if LEVELS.include?(level)
|
|
75
|
+
|
|
76
|
+
raise ArgumentError,
|
|
77
|
+
"Invalid disclosure level '#{level}'. Allowed: #{LEVELS.join(', ')}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
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 Authorship
|
|
8
|
+
# Validates that Human-Reviewed-By differs from the commit author.
|
|
9
|
+
#
|
|
10
|
+
# Self-review (where the submitter is also the reviewer) does not
|
|
11
|
+
# satisfy the review requirement. At advisory level this produces
|
|
12
|
+
# a warning; at strict level it produces an error.
|
|
13
|
+
class ReviewValidator
|
|
14
|
+
# Result of a review validation check.
|
|
15
|
+
#
|
|
16
|
+
# @return [Hash] with +:valid+ (Boolean) and +:message+ (String, nil)
|
|
17
|
+
def validate(author:, reviewer:)
|
|
18
|
+
return success if author.nil? || reviewer.nil?
|
|
19
|
+
return success unless normalize(author) == normalize(reviewer)
|
|
20
|
+
|
|
21
|
+
failure('Human-Reviewed-By must differ from the commit author (self-review detected)')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def normalize(identity)
|
|
27
|
+
identity.to_s.strip.downcase
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def success
|
|
31
|
+
{ valid: true, message: nil }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def failure(message)
|
|
35
|
+
{ valid: false, message: message }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
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 Authorship
|
|
8
|
+
# Generates commit trailer templates for AI authorship.
|
|
9
|
+
#
|
|
10
|
+
# Trailer format follows the convention:
|
|
11
|
+
# Role: Tool Version (Provider) <email>
|
|
12
|
+
#
|
|
13
|
+
# Engine capability manifests declare whether trailer support is
|
|
14
|
+
# available; engines without trailer support get a warning.
|
|
15
|
+
#
|
|
16
|
+
# @author hugo
|
|
17
|
+
# @author claude
|
|
18
|
+
class TrailerGenerator
|
|
19
|
+
TRAILER_ROLES = RosettAi::Provenance::Entry::ALLOWED_ROLES
|
|
20
|
+
|
|
21
|
+
# Generates a trailer string for a given engine.
|
|
22
|
+
#
|
|
23
|
+
# @param engine_name [String] engine display name (e.g. "Claude Opus 4.6")
|
|
24
|
+
# @param provider [String] engine provider (e.g. "Anthropic")
|
|
25
|
+
# @param role [String] one of {TRAILER_ROLES}
|
|
26
|
+
# @return [String] formatted trailer
|
|
27
|
+
def generate(engine_name:, provider:, role:)
|
|
28
|
+
validate_role!(role)
|
|
29
|
+
"#{role}: #{engine_name} (#{provider})"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Generates all four trailer templates for an engine.
|
|
33
|
+
#
|
|
34
|
+
# @param engine_name [String] engine display name
|
|
35
|
+
# @param provider [String] engine provider
|
|
36
|
+
# @return [Array<String>] all trailer templates
|
|
37
|
+
def generate_all(engine_name:, provider:)
|
|
38
|
+
TRAILER_ROLES.map do |role|
|
|
39
|
+
generate(engine_name: engine_name, provider: provider, role: role)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Formats trailers as a block suitable for documentation.
|
|
44
|
+
#
|
|
45
|
+
# @param engine_name [String] engine display name
|
|
46
|
+
# @param provider [String] engine provider
|
|
47
|
+
# @return [String] newline-separated trailer block
|
|
48
|
+
def trailer_block(engine_name:, provider:)
|
|
49
|
+
generate_all(engine_name: engine_name, provider: provider).join("\n")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Validates a tool name against known engine manifests.
|
|
53
|
+
#
|
|
54
|
+
# @param tool_name [String] tool name from a trailer (e.g. "Claude Opus 4.6")
|
|
55
|
+
# @param strict [Boolean] when true, unknown tools raise AuthorshipError
|
|
56
|
+
# @return [Array<String>] warnings (empty if tool is known)
|
|
57
|
+
def validate_tool(tool_name:, strict: false)
|
|
58
|
+
known = known_engine_names
|
|
59
|
+
return [] if known.empty? # no engines installed — skip validation
|
|
60
|
+
return [] if known.any? { |name| tool_name.include?(name) }
|
|
61
|
+
|
|
62
|
+
message = "Unknown AI tool '#{tool_name}' — not found in any engine manifest. " \
|
|
63
|
+
"Known engines: #{known.join(', ')}"
|
|
64
|
+
raise RosettAi::AuthorshipError, message if strict
|
|
65
|
+
|
|
66
|
+
[message]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def validate_role!(role)
|
|
72
|
+
return if TRAILER_ROLES.include?(role)
|
|
73
|
+
|
|
74
|
+
raise ArgumentError,
|
|
75
|
+
"Invalid trailer role '#{role}'. Allowed: #{TRAILER_ROLES.join(', ')}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def known_engine_names
|
|
79
|
+
RosettAi::Engines::Registry.available.filter_map do |engine_name|
|
|
80
|
+
manifest = RosettAi::Engines::Registry.manifest(engine_name)
|
|
81
|
+
manifest['display_name']
|
|
82
|
+
rescue RosettAi::Error
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|