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,157 @@
|
|
|
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 'thor'
|
|
8
|
+
require 'rainbow'
|
|
9
|
+
require 'terminal-table'
|
|
10
|
+
|
|
11
|
+
module RosettAi
|
|
12
|
+
module Thor
|
|
13
|
+
module Tasks
|
|
14
|
+
# Thor task for plugin management (list, install, remove).
|
|
15
|
+
#
|
|
16
|
+
# @author hugo
|
|
17
|
+
# @author claude
|
|
18
|
+
class Plugins < ::Thor
|
|
19
|
+
VALID_TYPES = ['engine', 'gui', 'mcp'].freeze
|
|
20
|
+
|
|
21
|
+
desc 'list TYPE', 'List installed plugins of a given type (engine, gui, mcp)'
|
|
22
|
+
long_desc <<~LONGDESC
|
|
23
|
+
List all installed plugins of the specified type. Valid types are
|
|
24
|
+
`engine` (compilation targets), `gui` (desktop interfaces), and
|
|
25
|
+
`mcp` (MCP server plugins).
|
|
26
|
+
|
|
27
|
+
Example: raictl plugins list engine
|
|
28
|
+
|
|
29
|
+
Related: plugins install, plugins remove, engines list
|
|
30
|
+
LONGDESC
|
|
31
|
+
def list(type)
|
|
32
|
+
validate_type!(type)
|
|
33
|
+
type_sym = type.to_sym
|
|
34
|
+
names = RosettAi::Plugins::Registry.available(type_sym)
|
|
35
|
+
|
|
36
|
+
if names.empty?
|
|
37
|
+
warn t('no_plugins', type: type)
|
|
38
|
+
return
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if tty_output?
|
|
42
|
+
print_list_tty(type_sym, names)
|
|
43
|
+
else
|
|
44
|
+
print_list_json(type_sym, names)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc 'install TYPE NAME', 'Install a plugin (e.g. plugins install engine ollama)'
|
|
49
|
+
long_desc <<~LONGDESC
|
|
50
|
+
Install a plugin package via apt (preferred) or gem fallback.
|
|
51
|
+
|
|
52
|
+
The package name is derived as rosett-ai-TYPE-NAME (e.g., rosett-ai-engine-ollama).
|
|
53
|
+
After installation the plugin registry is refreshed.
|
|
54
|
+
|
|
55
|
+
Example: raictl plugins install engine ollama
|
|
56
|
+
|
|
57
|
+
Exit codes: 0 success, 1 install failed.
|
|
58
|
+
|
|
59
|
+
Related: plugins remove, plugins list
|
|
60
|
+
LONGDESC
|
|
61
|
+
def install(type, name)
|
|
62
|
+
validate_type!(type)
|
|
63
|
+
package = plugin_package_name(type, name)
|
|
64
|
+
|
|
65
|
+
warn t('installing', package: package)
|
|
66
|
+
pm = select_package_manager
|
|
67
|
+
|
|
68
|
+
if pm.install(package)
|
|
69
|
+
RosettAi::Plugins::Registry.reset!
|
|
70
|
+
RosettAi::Plugins::Registry.discover!
|
|
71
|
+
warn Rainbow(t('installed', package: package)).green
|
|
72
|
+
else
|
|
73
|
+
warn Rainbow(t('install_failed', package: package)).red
|
|
74
|
+
exit 1
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
desc 'remove TYPE NAME', 'Remove a plugin (e.g. plugins remove engine ollama)'
|
|
79
|
+
long_desc <<~LONGDESC
|
|
80
|
+
Remove an installed plugin package.
|
|
81
|
+
|
|
82
|
+
Example: raictl plugins remove engine ollama
|
|
83
|
+
|
|
84
|
+
Exit codes: 0 success, 1 removal failed.
|
|
85
|
+
|
|
86
|
+
Related: plugins install, plugins list
|
|
87
|
+
LONGDESC
|
|
88
|
+
def remove(type, name)
|
|
89
|
+
validate_type!(type)
|
|
90
|
+
package = plugin_package_name(type, name)
|
|
91
|
+
|
|
92
|
+
warn t('removing', package: package)
|
|
93
|
+
pm = select_package_manager
|
|
94
|
+
|
|
95
|
+
if pm.remove(package)
|
|
96
|
+
RosettAi::Plugins::Registry.reset!
|
|
97
|
+
RosettAi::Plugins::Registry.discover!
|
|
98
|
+
warn Rainbow(t('removed', package: package)).green
|
|
99
|
+
else
|
|
100
|
+
warn Rainbow(t('remove_failed', package: package)).red
|
|
101
|
+
exit 1
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def t(key, **)
|
|
108
|
+
::I18n.t("rosett_ai.cli.plugins.#{key}", **)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def tty_output?
|
|
112
|
+
$stdout.tty?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def validate_type!(type)
|
|
116
|
+
return if VALID_TYPES.include?(type)
|
|
117
|
+
|
|
118
|
+
raise ::Thor::Error, t('invalid_type', type: type)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def plugin_package_name(type, name)
|
|
122
|
+
"rosett-ai-#{type}-#{name}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def select_package_manager
|
|
126
|
+
apt = RosettAi::PackageManager::Apt.new
|
|
127
|
+
return apt if apt.available?
|
|
128
|
+
|
|
129
|
+
RosettAi::PackageManager::GemBackend.new
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def print_list_tty(type_sym, names)
|
|
133
|
+
rows = names.map do |name|
|
|
134
|
+
mod = RosettAi::Plugins::Registry.plugin_module(type_sym, name)
|
|
135
|
+
[name, mod.display_name, mod.version]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
table = ::Terminal::Table.new(
|
|
139
|
+
title: t('title'),
|
|
140
|
+
headings: [t('name'), t('display_name'), t('version')],
|
|
141
|
+
rows: rows,
|
|
142
|
+
style: { border: :unicode_round }
|
|
143
|
+
)
|
|
144
|
+
puts table
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def print_list_json(type_sym, names)
|
|
148
|
+
data = names.map do |name|
|
|
149
|
+
mod = RosettAi::Plugins::Registry.plugin_module(type_sym, name)
|
|
150
|
+
{ name: name, display_name: mod.display_name, version: mod.version }
|
|
151
|
+
end
|
|
152
|
+
puts JSON.generate(data)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'terminal-table'
|
|
7
|
+
require 'rainbow'
|
|
8
|
+
require 'json'
|
|
9
|
+
|
|
10
|
+
module RosettAi
|
|
11
|
+
module Thor
|
|
12
|
+
module Tasks
|
|
13
|
+
# CLI task for `rai project` — project lifecycle management.
|
|
14
|
+
#
|
|
15
|
+
# Provides commands for template application, drift detection,
|
|
16
|
+
# upstream synchronization, and project health reporting.
|
|
17
|
+
#
|
|
18
|
+
# @author hugo
|
|
19
|
+
# @author claude
|
|
20
|
+
class Project < ::Thor
|
|
21
|
+
desc 'status', 'Show project status and health'
|
|
22
|
+
long_desc <<~LONGDESC
|
|
23
|
+
Displays project metadata, drift status, and overall health.
|
|
24
|
+
Uses TTY-aware output (table when interactive, plain when piped).
|
|
25
|
+
|
|
26
|
+
EXAMPLES
|
|
27
|
+
|
|
28
|
+
raictl project status
|
|
29
|
+
raictl project status --format json
|
|
30
|
+
LONGDESC
|
|
31
|
+
method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
|
|
32
|
+
desc: 'Output format'
|
|
33
|
+
def status
|
|
34
|
+
manager = RosettAi::Project::Manager.new(project_root: resolve_project_root)
|
|
35
|
+
result = manager.status
|
|
36
|
+
|
|
37
|
+
if options[:format] == 'json'
|
|
38
|
+
puts JSON.pretty_generate(result)
|
|
39
|
+
else
|
|
40
|
+
render_status(result)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc 'info', 'Show project metadata'
|
|
45
|
+
long_desc <<~LONGDESC
|
|
46
|
+
Displays project name, default engine, template origin,
|
|
47
|
+
and counts of behaviours and design documents.
|
|
48
|
+
|
|
49
|
+
EXAMPLES
|
|
50
|
+
|
|
51
|
+
raictl project info
|
|
52
|
+
LONGDESC
|
|
53
|
+
def info
|
|
54
|
+
manager = RosettAi::Project::Manager.new(project_root: resolve_project_root)
|
|
55
|
+
result = manager.info
|
|
56
|
+
|
|
57
|
+
rows = result.map { |key, value| [key.to_s.tr('_', ' ').capitalize, value.to_s] }
|
|
58
|
+
table = ::Terminal::Table.new(
|
|
59
|
+
title: ::I18n.t('rosett_ai.project.info_title'),
|
|
60
|
+
rows: rows
|
|
61
|
+
)
|
|
62
|
+
puts table
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
desc 'apply-template NAME', 'Apply a project template'
|
|
66
|
+
long_desc <<~LONGDESC
|
|
67
|
+
Applies the named template to populate .rosett-ai/ with starter configs.
|
|
68
|
+
Existing files are not overwritten unless --force is used.
|
|
69
|
+
Use --list to see available templates.
|
|
70
|
+
|
|
71
|
+
EXAMPLES
|
|
72
|
+
|
|
73
|
+
raictl project apply-template ruby-gem
|
|
74
|
+
raictl project apply-template --list
|
|
75
|
+
raictl project apply-template python-lib --force
|
|
76
|
+
LONGDESC
|
|
77
|
+
method_option :force, type: :boolean, default: false,
|
|
78
|
+
desc: 'Overwrite existing files'
|
|
79
|
+
method_option :list, type: :boolean, default: false,
|
|
80
|
+
desc: 'List available templates'
|
|
81
|
+
def apply_template(name = nil)
|
|
82
|
+
if options[:list]
|
|
83
|
+
list_templates
|
|
84
|
+
return
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
raise RosettAi::ProjectError, 'Template name required' unless name
|
|
88
|
+
|
|
89
|
+
applier = RosettAi::Project::TemplateApplier.new(
|
|
90
|
+
project_root: resolve_project_root,
|
|
91
|
+
template_name: name,
|
|
92
|
+
force: options[:force]
|
|
93
|
+
)
|
|
94
|
+
results = applier.apply
|
|
95
|
+
|
|
96
|
+
render_apply_results(results)
|
|
97
|
+
exit 1 if results[:errors].any?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
desc 'drift', 'Detect configuration drift'
|
|
101
|
+
long_desc <<~LONGDESC
|
|
102
|
+
Detects drift between source YAML and compiled configs (forward)
|
|
103
|
+
or between project configs and template baseline (template).
|
|
104
|
+
|
|
105
|
+
Exit codes: 0 = no drift, 4 = drift detected.
|
|
106
|
+
|
|
107
|
+
EXAMPLES
|
|
108
|
+
|
|
109
|
+
raictl project drift
|
|
110
|
+
raictl project drift --type forward
|
|
111
|
+
raictl project drift --format json
|
|
112
|
+
LONGDESC
|
|
113
|
+
method_option :type, type: :string, enum: ['forward', 'template'],
|
|
114
|
+
desc: 'Drift type to detect (default: both)'
|
|
115
|
+
method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
|
|
116
|
+
desc: 'Output format'
|
|
117
|
+
def drift
|
|
118
|
+
root = resolve_project_root
|
|
119
|
+
validate_project!(root)
|
|
120
|
+
|
|
121
|
+
detector = RosettAi::Project::DriftDetector.new(project_root: root)
|
|
122
|
+
results = detector.detect(type: options[:type])
|
|
123
|
+
|
|
124
|
+
if options[:format] == 'json'
|
|
125
|
+
puts JSON.pretty_generate(results)
|
|
126
|
+
else
|
|
127
|
+
render_drift_results(results)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
total_drift = results.values.flatten.size
|
|
131
|
+
exit 4 if total_drift.positive?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
desc 'sync', 'Synchronize with upstream template updates'
|
|
135
|
+
long_desc <<~LONGDESC
|
|
136
|
+
Updates project configs from upstream template changes.
|
|
137
|
+
Detects breaking changes and requires --force to apply.
|
|
138
|
+
Use --simulate for a dry-run preview.
|
|
139
|
+
|
|
140
|
+
Exit codes: 0 = success, 5 = unresolved conflicts.
|
|
141
|
+
|
|
142
|
+
EXAMPLES
|
|
143
|
+
|
|
144
|
+
raictl project sync
|
|
145
|
+
raictl project sync --simulate
|
|
146
|
+
raictl project sync --force
|
|
147
|
+
LONGDESC
|
|
148
|
+
method_option :force, type: :boolean, default: false,
|
|
149
|
+
desc: 'Apply breaking changes without prompting'
|
|
150
|
+
method_option :simulate, type: :boolean, default: false,
|
|
151
|
+
desc: 'Preview changes without applying'
|
|
152
|
+
def sync
|
|
153
|
+
sync_mgr = RosettAi::Project::SyncManager.new(
|
|
154
|
+
project_root: resolve_project_root,
|
|
155
|
+
force: options[:force]
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
results = options[:simulate] ? sync_mgr.simulate : sync_mgr.sync
|
|
159
|
+
render_sync_results(results, simulate: options[:simulate])
|
|
160
|
+
|
|
161
|
+
if results[:error]
|
|
162
|
+
warn Rainbow(" Error: #{results[:error]}").red
|
|
163
|
+
exit 1
|
|
164
|
+
end
|
|
165
|
+
exit 5 if results[:conflicts].any?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
def resolve_project_root
|
|
171
|
+
Pathname.new(ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd))
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Validates that the given root contains an .rosett-ai/ project directory.
|
|
175
|
+
#
|
|
176
|
+
# @param root [Pathname] project root path
|
|
177
|
+
# @raise [RosettAi::ProjectError] if .rosett-ai/ directory is missing
|
|
178
|
+
def validate_project!(root)
|
|
179
|
+
return if root.join('.rosett-ai').directory?
|
|
180
|
+
|
|
181
|
+
raise RosettAi::ProjectError,
|
|
182
|
+
"No .rosett-ai/ directory found in #{root}. Run 'rai init --project' first."
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def list_templates
|
|
186
|
+
templates = RosettAi::Project::TemplateApplier.available_templates
|
|
187
|
+
if templates.empty?
|
|
188
|
+
warn ::I18n.t('rosett_ai.project.no_templates')
|
|
189
|
+
return
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
puts ::I18n.t('rosett_ai.project.templates_title')
|
|
193
|
+
templates.each { |name| puts " - #{name}" }
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def render_status(result)
|
|
197
|
+
info = result[:info]
|
|
198
|
+
health = result[:health]
|
|
199
|
+
|
|
200
|
+
puts ::I18n.t('rosett_ai.project.status_title')
|
|
201
|
+
puts " #{::I18n.t('rosett_ai.project.name_label')}: #{info[:project_name]}"
|
|
202
|
+
puts " #{::I18n.t('rosett_ai.project.engine_label')}: #{info[:default_engine]}"
|
|
203
|
+
puts " #{::I18n.t('rosett_ai.project.template_label')}: #{info[:template] || '-'}"
|
|
204
|
+
puts " #{::I18n.t('rosett_ai.project.behaviours_label')}: #{info[:behaviours]}"
|
|
205
|
+
puts " #{::I18n.t('rosett_ai.project.designs_label')}: #{info[:designs]}"
|
|
206
|
+
puts
|
|
207
|
+
|
|
208
|
+
if health[:healthy]
|
|
209
|
+
puts Rainbow(::I18n.t('rosett_ai.project.healthy')).green
|
|
210
|
+
else
|
|
211
|
+
puts Rainbow(::I18n.t('rosett_ai.project.unhealthy')).yellow
|
|
212
|
+
health[:issues].each { |issue| puts " - #{issue}" }
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def render_apply_results(results)
|
|
217
|
+
results[:applied].each { |file| puts Rainbow(" + #{file}").green }
|
|
218
|
+
results[:skipped].each { |file| puts Rainbow(" ~ #{file} (skipped)").yellow }
|
|
219
|
+
results[:errors].each { |err| warn Rainbow(" ! #{err}").red }
|
|
220
|
+
|
|
221
|
+
puts ::I18n.t('rosett_ai.project.apply_summary',
|
|
222
|
+
applied: results[:applied].size,
|
|
223
|
+
skipped: results[:skipped].size)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def render_drift_results(results)
|
|
227
|
+
total = 0
|
|
228
|
+
|
|
229
|
+
results.each do |type, drifts|
|
|
230
|
+
next if drifts.empty?
|
|
231
|
+
|
|
232
|
+
puts "#{type.to_s.capitalize} drift:"
|
|
233
|
+
drifts.each do |drift|
|
|
234
|
+
puts " - #{drift[:file]}"
|
|
235
|
+
total += 1
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
if total.zero?
|
|
240
|
+
puts Rainbow(::I18n.t('rosett_ai.project.no_drift')).green
|
|
241
|
+
else
|
|
242
|
+
puts ::I18n.t('rosett_ai.project.drift_detected', count: total)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def render_sync_results(results, simulate: false)
|
|
247
|
+
prefix = simulate ? ::I18n.t('rosett_ai.project.simulate_prefix') : ''
|
|
248
|
+
|
|
249
|
+
results[:updated].each { |file| puts Rainbow(" #{prefix}+ #{file}").green }
|
|
250
|
+
results[:conflicts].each { |file| puts Rainbow(" ! #{file} (conflict)").yellow }
|
|
251
|
+
results[:skipped].each { |file| puts " ~ #{file} (unchanged)" }
|
|
252
|
+
|
|
253
|
+
puts ::I18n.t('rosett_ai.project.sync_summary',
|
|
254
|
+
updated: results[:updated].size,
|
|
255
|
+
conflicts: results[:conflicts].size)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
|
|
5
|
+
|
|
6
|
+
require 'thor'
|
|
7
|
+
require 'terminal-table'
|
|
8
|
+
require 'rainbow'
|
|
9
|
+
|
|
10
|
+
module RosettAi
|
|
11
|
+
module Thor
|
|
12
|
+
module Tasks
|
|
13
|
+
# Thor task for AI provenance tracking commands.
|
|
14
|
+
#
|
|
15
|
+
# @author hugo
|
|
16
|
+
# @author claude
|
|
17
|
+
class Provenance < ::Thor
|
|
18
|
+
desc 'init', 'Create .ai-provenance.yml in the current project'
|
|
19
|
+
long_desc <<~LONGDESC
|
|
20
|
+
Initializes an AI provenance tracking file in the current project root.
|
|
21
|
+
Creates .ai-provenance.yml with the necessary structure for tracking AI-assisted changes.
|
|
22
|
+
|
|
23
|
+
Exit codes:
|
|
24
|
+
0 - Success
|
|
25
|
+
1 - Initialization error
|
|
26
|
+
|
|
27
|
+
EXAMPLES
|
|
28
|
+
|
|
29
|
+
raictl provenance init
|
|
30
|
+
|
|
31
|
+
Related commands: validate, show, log
|
|
32
|
+
LONGDESC
|
|
33
|
+
def init
|
|
34
|
+
store = build_store
|
|
35
|
+
store.init
|
|
36
|
+
say "Created #{store.path}", :green
|
|
37
|
+
rescue RosettAi::ProvenanceError => e
|
|
38
|
+
say_status 'error', e.message, :red
|
|
39
|
+
exit RosettAi::ExitCodes::GENERAL
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc 'validate', 'Validate provenance entries and hash chain integrity'
|
|
43
|
+
long_desc <<~LONGDESC
|
|
44
|
+
Validates all provenance entries against the schema and verifies hash chain integrity.
|
|
45
|
+
Ensures that the provenance file has not been tampered with.
|
|
46
|
+
|
|
47
|
+
Exit codes:
|
|
48
|
+
0 - All entries valid
|
|
49
|
+
2 - Validation failures
|
|
50
|
+
1 - Other errors
|
|
51
|
+
|
|
52
|
+
EXAMPLES
|
|
53
|
+
|
|
54
|
+
raictl provenance validate
|
|
55
|
+
|
|
56
|
+
Related commands: init, show, log
|
|
57
|
+
LONGDESC
|
|
58
|
+
def validate
|
|
59
|
+
store = build_store
|
|
60
|
+
data = store.read
|
|
61
|
+
entry_count = data['entries'].size
|
|
62
|
+
|
|
63
|
+
violations = store.verify_chain
|
|
64
|
+
if violations.empty?
|
|
65
|
+
say "#{entry_count} entries validated, hash chain intact", :green
|
|
66
|
+
else
|
|
67
|
+
violations.each { |v| say_status 'error', v, :red }
|
|
68
|
+
exit RosettAi::ExitCodes::VALIDATION
|
|
69
|
+
end
|
|
70
|
+
rescue RosettAi::ProvenanceError => e
|
|
71
|
+
say_status 'error', e.message, :red
|
|
72
|
+
exit RosettAi::ExitCodes::GENERAL
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
desc 'show COMMIT', 'Show provenance for a specific commit'
|
|
76
|
+
long_desc <<~LONGDESC
|
|
77
|
+
Displays provenance information for a specific commit or file.
|
|
78
|
+
Shows AI tool, role, and affected files for the specified commit.
|
|
79
|
+
|
|
80
|
+
Flags:
|
|
81
|
+
--file: Show provenance for a specific file instead of a commit
|
|
82
|
+
|
|
83
|
+
Exit codes:
|
|
84
|
+
0 - Success
|
|
85
|
+
1 - Error or no entries found
|
|
86
|
+
|
|
87
|
+
EXAMPLES
|
|
88
|
+
|
|
89
|
+
raictl provenance show abc1234
|
|
90
|
+
raictl provenance show --file lib/rosett_ai.rb
|
|
91
|
+
|
|
92
|
+
Related commands: init, validate, log
|
|
93
|
+
LONGDESC
|
|
94
|
+
method_option :file, type: :string, desc: 'Show provenance for a specific file'
|
|
95
|
+
def show(commit = nil)
|
|
96
|
+
store = build_store
|
|
97
|
+
entries = if options[:file]
|
|
98
|
+
store.entries_for_file(options[:file])
|
|
99
|
+
elsif commit
|
|
100
|
+
store.entries_for_commit(commit)
|
|
101
|
+
else
|
|
102
|
+
say_status 'error', 'Provide a commit SHA or --file path', :red
|
|
103
|
+
exit RosettAi::ExitCodes::GENERAL
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if entries.empty?
|
|
107
|
+
say 'No provenance entries found', :yellow
|
|
108
|
+
else
|
|
109
|
+
print_entries(entries)
|
|
110
|
+
end
|
|
111
|
+
rescue RosettAi::ProvenanceError => e
|
|
112
|
+
say_status 'error', e.message, :red
|
|
113
|
+
exit RosettAi::ExitCodes::GENERAL
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
desc 'log', 'Show all provenance entries (reverse chronological)'
|
|
117
|
+
long_desc <<~LONGDESC
|
|
118
|
+
Lists all provenance entries in reverse chronological order.
|
|
119
|
+
Can be filtered by AI role for focused analysis.
|
|
120
|
+
|
|
121
|
+
Flags:
|
|
122
|
+
--role: Filter entries by AI role (e.g., AI-Co-Author)
|
|
123
|
+
|
|
124
|
+
Exit codes:
|
|
125
|
+
0 - Success
|
|
126
|
+
1 - Error
|
|
127
|
+
|
|
128
|
+
EXAMPLES
|
|
129
|
+
|
|
130
|
+
raictl provenance log
|
|
131
|
+
raictl provenance log --role AI-Co-Author
|
|
132
|
+
|
|
133
|
+
Related commands: init, validate, show
|
|
134
|
+
LONGDESC
|
|
135
|
+
method_option :role, type: :string, desc: 'Filter by AI role'
|
|
136
|
+
def log
|
|
137
|
+
store = build_store
|
|
138
|
+
entries = store.read['entries'].reverse
|
|
139
|
+
|
|
140
|
+
entries = entries.select { |e| e['ai_role'] == options[:role] } if options[:role]
|
|
141
|
+
|
|
142
|
+
if entries.empty?
|
|
143
|
+
say 'No provenance entries found', :yellow
|
|
144
|
+
else
|
|
145
|
+
print_entries(entries)
|
|
146
|
+
end
|
|
147
|
+
rescue RosettAi::ProvenanceError => e
|
|
148
|
+
say_status 'error', e.message, :red
|
|
149
|
+
exit RosettAi::ExitCodes::GENERAL
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
def build_store
|
|
155
|
+
root = RosettAi.context.project? ? RosettAi.context.project_root : Pathname.pwd
|
|
156
|
+
RosettAi::Provenance::Store.new(root: root)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def print_entries(entries)
|
|
160
|
+
if RosettAi::Ui::TtyHelper.interactive?
|
|
161
|
+
print_table_format(entries)
|
|
162
|
+
else
|
|
163
|
+
print_plain_format(entries)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def print_table_format(entries)
|
|
168
|
+
rows = entries.map do |entry|
|
|
169
|
+
files = Array(entry['files']).map { |f| f['path'] }.join(', ')
|
|
170
|
+
[
|
|
171
|
+
entry['commit'].to_s[0, 8],
|
|
172
|
+
entry['ai_tool'].to_s,
|
|
173
|
+
entry['ai_role'].to_s,
|
|
174
|
+
files.length > 40 ? "#{files[0, 37]}..." : files
|
|
175
|
+
]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
table = ::Terminal::Table.new(
|
|
179
|
+
headings: ['Commit', 'Tool', 'Role', 'Files'],
|
|
180
|
+
rows: rows,
|
|
181
|
+
style: { border: :unicode_round }
|
|
182
|
+
)
|
|
183
|
+
puts table
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def print_plain_format(entries)
|
|
187
|
+
entries.each do |entry|
|
|
188
|
+
files = Array(entry['files']).map { |f| f['path'] }.join(', ')
|
|
189
|
+
puts "#{entry['commit'].to_s[0, 8]}\t#{entry['ai_tool']}\t#{entry['ai_role']}\t#{files}"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|