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.
Files changed (527) hide show
  1. checksums.yaml +7 -0
  2. data/.ai-provenance.yml +119 -0
  3. data/.debride_whitelist +186 -0
  4. data/.fasterer.yml +29 -0
  5. data/.mdl_style.rb +10 -0
  6. data/.mdlrc +3 -0
  7. data/.mutant.yml +49 -0
  8. data/.namespace-allowlist +42 -0
  9. data/.reek.yml +1040 -0
  10. data/.rosett-ai/config.yml +3 -0
  11. data/.rspec +5 -0
  12. data/.rubocop.yml +380 -0
  13. data/.ruby-version +1 -0
  14. data/.yamllint +51 -0
  15. data/.yardopts +12 -0
  16. data/AI-DISCLOSURE.md +48 -0
  17. data/CHANGELOG.md +519 -0
  18. data/CLAUDE.md +141 -0
  19. data/CONTRIBUTING.md +734 -0
  20. data/INSTALL.md +154 -0
  21. data/LICENSE +674 -0
  22. data/LICENSE.md +675 -0
  23. data/QUICKSTART.md +73 -0
  24. data/README.md +366 -0
  25. data/Rakefile +200 -0
  26. data/SECURITY.md +114 -0
  27. data/bin/rai +1 -0
  28. data/cliff.toml +52 -0
  29. data/conf/adopt_redactions.yml +8 -0
  30. data/conf/behaviour/.gitkeep +0 -0
  31. data/conf/compliance/cra_rules.yml +25 -0
  32. data/conf/compliance/license_rules.yml +20 -0
  33. data/conf/design/aaif_alignment.yml +181 -0
  34. data/conf/design/ab_testing.yml +172 -0
  35. data/conf/design/accessibility.yml +84 -0
  36. data/conf/design/ai_authorship.yml +210 -0
  37. data/conf/design/ai_provenance.yml +224 -0
  38. data/conf/design/ai_tool_configuration.yml +207 -0
  39. data/conf/design/architecture.yml +139 -0
  40. data/conf/design/autocompletion.yml +115 -0
  41. data/conf/design/backward_compatibility.yml +112 -0
  42. data/conf/design/behaviour_composition.yml +246 -0
  43. data/conf/design/build_rake_extraction.yml +57 -0
  44. data/conf/design/ci_pipeline.yml +100 -0
  45. data/conf/design/claude_code_configuration.yml +157 -0
  46. data/conf/design/compiler.yml +128 -0
  47. data/conf/design/comply.yml +153 -0
  48. data/conf/design/content_packs.yml +84 -0
  49. data/conf/design/desktop_integration.yml +289 -0
  50. data/conf/design/distribution.yml +216 -0
  51. data/conf/design/doctor.yml +184 -0
  52. data/conf/design/documentation.yml +152 -0
  53. data/conf/design/engine_architecture.yml +257 -0
  54. data/conf/design/error_handling.yml +103 -0
  55. data/conf/design/feature_flags.yml +142 -0
  56. data/conf/design/git_hooks.yml +165 -0
  57. data/conf/design/gui_plugins.yml +475 -0
  58. data/conf/design/i18n.yml +84 -0
  59. data/conf/design/integration_testing.yml +56 -0
  60. data/conf/design/licensing_system.yml +88 -0
  61. data/conf/design/lifecycle_management.yml +208 -0
  62. data/conf/design/mcp_integration.yml +207 -0
  63. data/conf/design/mcp_settings.yml +126 -0
  64. data/conf/design/migration.yml +56 -0
  65. data/conf/design/monitoring_observability.yml +194 -0
  66. data/conf/design/namespace_cleanup.yml +145 -0
  67. data/conf/design/plugin_test_segregation.yml +145 -0
  68. data/conf/design/policy_management.yml +229 -0
  69. data/conf/design/project_management.yml +183 -0
  70. data/conf/design/rai_mcp_asset_discovery.yml +164 -0
  71. data/conf/design/rai_mcp_server.yml +605 -0
  72. data/conf/design/release_management.yml +117 -0
  73. data/conf/design/retrofit.yml +199 -0
  74. data/conf/design/retrospective_analyzer.yml +79 -0
  75. data/conf/design/scope_hierarchy.yml +352 -0
  76. data/conf/design/security.yml +115 -0
  77. data/conf/design/session_retrospective.yml +85 -0
  78. data/conf/design/smart_ui_feedback.yml +89 -0
  79. data/conf/design/structured_logging.yml +148 -0
  80. data/conf/design/styles.yml +123 -0
  81. data/conf/design/test_peer_review.yml +89 -0
  82. data/conf/design/testing.yml +136 -0
  83. data/conf/design/threat_model.yml +108 -0
  84. data/conf/design/ui_framework.yml +111 -0
  85. data/conf/design/usage_optimization.yml +122 -0
  86. data/conf/design/version_management.yml +60 -0
  87. data/conf/design/workflow.yml +227 -0
  88. data/conf/mcp/server_defaults.yml +42 -0
  89. data/conf/mcp/trust.yml +21 -0
  90. data/conf/packaging/core.yml +12 -0
  91. data/conf/packaging/gtk4.yml +11 -0
  92. data/conf/packaging/qt6.yml +11 -0
  93. data/conf/policy/default_deny_list.yml +197 -0
  94. data/conf/review/cli-command-audit.yml +857 -0
  95. data/conf/review/design-docs.yml +1064 -0
  96. data/conf/review/design-questionnaire.yml +153 -0
  97. data/conf/review/questionnaire.yml +146 -0
  98. data/conf/review/rosett-ai-core.yml +2919 -0
  99. data/conf/schemas/ai_config_schema.json +73 -0
  100. data/conf/schemas/behaviour_schema.json +132 -0
  101. data/conf/schemas/compliance_rule_schema.json +63 -0
  102. data/conf/schemas/content_pack_manifest_schema.json +51 -0
  103. data/conf/schemas/design_schema.json +210 -0
  104. data/conf/schemas/engine_manifest_schema.json +144 -0
  105. data/conf/schemas/lockfile_schema.json +74 -0
  106. data/conf/schemas/mcp_server_schema.json +48 -0
  107. data/conf/schemas/packaging_schema.json +70 -0
  108. data/conf/schemas/policy_schema.json +85 -0
  109. data/conf/schemas/provenance_schema.json +84 -0
  110. data/conf/schemas/rai_config_schema.json +56 -0
  111. data/conf/schemas/rai_project_schema.json +20 -0
  112. data/conf/schemas/scope_hierarchy_schema.json +49 -0
  113. data/conf/schemas/target_schema.json +67 -0
  114. data/conf/schemas/tooling_schema.json +65 -0
  115. data/conf/schemas/workflow_schema.json +112 -0
  116. data/conf/targets/agents_md.yml +17 -0
  117. data/conf/targets/claude.yml +12 -0
  118. data/conf/tooling/tools.yml +58 -0
  119. data/dist/rosett-ai-mcp.service +48 -0
  120. data/dist/rosett-ai-mcp.yml.default +45 -0
  121. data/doc/AAIF_POSITIONING.md +58 -0
  122. data/doc/ADOPT.md +224 -0
  123. data/doc/AI_PROVENANCE.md +139 -0
  124. data/doc/ARCHITECTURE.md +920 -0
  125. data/doc/BEHAVIOUR.md +409 -0
  126. data/doc/BUILD.md +138 -0
  127. data/doc/CI_CD_RECIPES.md +171 -0
  128. data/doc/CLAUDE_SESSIONS_MOVED.md +16 -0
  129. data/doc/COMMAND_ANALYSIS.md +229 -0
  130. data/doc/CONFIGURATION.md +281 -0
  131. data/doc/DESIGN_AUDIT.md +235 -0
  132. data/doc/DESIGN_PEER_REVIEW.md +771 -0
  133. data/doc/DESKTOP.md +447 -0
  134. data/doc/ENGINES.md +567 -0
  135. data/doc/ENGINE_DEVELOPMENT_GUIDE.md +417 -0
  136. data/doc/FEATURE_AUDIT.md +218 -0
  137. data/doc/IMPLEMENTATION_PLAN.md +669 -0
  138. data/doc/INCIDENT_REPORT_2026-02-02.md +251 -0
  139. data/doc/MIGRATION_GUIDE.md +88 -0
  140. data/doc/PACKAGING.md +232 -0
  141. data/doc/PROJECT_DASHBOARD.md +153 -0
  142. data/doc/PULP_DEPLOYMENT.md +164 -0
  143. data/doc/QUALITY_FIX_SUMMARY.md +110 -0
  144. data/doc/QUICK_START.md +162 -0
  145. data/doc/REEK_CONFIGURATION.md +166 -0
  146. data/doc/REFERENCE.md +253 -0
  147. data/doc/REFERENCES.md +324 -0
  148. data/doc/SECURITY_REVIEW_CHECKLIST.md +72 -0
  149. data/doc/SESSION_2026-02-28_GTK4_HARDENING.md +359 -0
  150. data/doc/SETUP.md +202 -0
  151. data/doc/TEST_PEER_REVIEW.md +152 -0
  152. data/doc/THREAT_MODEL.md +230 -0
  153. data/doc/USAGE.md +545 -0
  154. data/doc/USER_MANUAL.md +585 -0
  155. data/doc/ai_test_review_checklist.md +110 -0
  156. data/doc/changes/2026-02-18-packaging-fpm.md +155 -0
  157. data/doc/changes/2026-02-19-testing-infrastructure.md +221 -0
  158. data/doc/changes/2026-02-20-security-implementation.md +281 -0
  159. data/doc/changes/2026-02-20-styles-implementation.md +220 -0
  160. data/doc/changes/2026-02-21-architecture-completion.md +95 -0
  161. data/doc/changes/2026-02-21-architecture-ui-layer.md +253 -0
  162. data/doc/changes/2026-02-21-cc-config-implementation.md +108 -0
  163. data/doc/changes/2026-02-21-ci-pipeline-implementation.md +214 -0
  164. data/doc/changes/2026-02-21-compiler-multi-target-pipeline.md +241 -0
  165. data/doc/changes/2026-02-21-config-design-show-commands.md +61 -0
  166. data/doc/changes/2026-02-21-design-implementation-overview.md +455 -0
  167. data/doc/changes/2026-02-21-lifecycle-management.md +196 -0
  168. data/doc/changes/2026-02-21-path-resolver.md +128 -0
  169. data/doc/changes/2026-02-24-ci-tmpdir-mutant-fetch.md +45 -0
  170. data/doc/changes/2026-03-01-ci-bundler-strategy.md +120 -0
  171. data/doc/changes/2026-03-20-security-hardening-phase2.md +163 -0
  172. data/doc/context/SESSION-HANDOFF.md +69 -0
  173. data/doc/context/ai-engine-usage-trends-2026.md +80 -0
  174. data/doc/context/plan-pluggable-engines.md +590 -0
  175. data/doc/decisions/001-flog-deferred.md +32 -0
  176. data/doc/decisions/002-path-resolution-strategy.md +158 -0
  177. data/doc/decisions/003-ui-adapter-selection.md +193 -0
  178. data/doc/decisions/004-design-document-validation.md +179 -0
  179. data/doc/decisions/005-package-splitting-strategy.md +200 -0
  180. data/doc/decisions/006-multi-engine-architecture.md +147 -0
  181. data/doc/decisions/007-engine-agnostic-pivot.md +219 -0
  182. data/doc/decisions/008-ci-bundler-strategy.md +129 -0
  183. data/doc/decisions/009-core-only-v1-release.md +60 -0
  184. data/doc/decisions/010-engine-debian-packaging.md +66 -0
  185. data/doc/decisions/011-context-aware-cli.md +71 -0
  186. data/doc/dependency_decisions.yml +247 -0
  187. data/doc/issues/001-wrapper-missing-environment-variables.md +197 -0
  188. data/doc/issues/002-embedded-ruby-wrong-prefix.md +217 -0
  189. data/doc/issues/003-smoke-test-false-positive.md +127 -0
  190. data/doc/issues/004-market-research-design-updates.md +109 -0
  191. data/doc/issues/005-compile-scope-coexistence.md +161 -0
  192. data/doc/locales/.gitkeep +0 -0
  193. data/doc/man/rai.1.ronn +505 -0
  194. data/doc/operations/packaging.md +133 -0
  195. data/doc/operations/rosett-ai-release.md +65 -0
  196. data/doc/reference/error-catalog.md +107 -0
  197. data/doc/reference/rosett-ai-technical-reference.pdf +0 -0
  198. data/doc/reference/src/Pictures/cover.jpg +0 -0
  199. data/doc/reference/src/Pictures/head1.jpg +0 -0
  200. data/doc/reference/src/Pictures/head2.jpg +0 -0
  201. data/doc/reference/src/Pictures/head3.jpg +0 -0
  202. data/doc/reference/src/Pictures/head4.jpg +0 -0
  203. data/doc/reference/src/Pictures/head5.jpg +0 -0
  204. data/doc/reference/src/Pictures/head6.jpg +0 -0
  205. data/doc/reference/src/Pictures/head7.jpg +0 -0
  206. data/doc/reference/src/Pictures/head8.jpg +0 -0
  207. data/doc/reference/src/StyleInd.ist +4 -0
  208. data/doc/reference/src/bibliography.bib +79 -0
  209. data/doc/reference/src/main.tex +1288 -0
  210. data/doc/reference/src/structure.tex +303 -0
  211. data/doc/rosett-ai-bookmarks.html +301 -0
  212. data/kitchen.yml +46 -0
  213. data/lib/rosett_ai/adopter/executor_resolver.rb +77 -0
  214. data/lib/rosett_ai/adopter/local_analysis_collector.rb +154 -0
  215. data/lib/rosett_ai/adopter/rule_adopter.rb +254 -0
  216. data/lib/rosett_ai/ai_config/config_compiler.rb +111 -0
  217. data/lib/rosett_ai/ai_config/context_window.rb +55 -0
  218. data/lib/rosett_ai/ai_config/cost_controls.rb +44 -0
  219. data/lib/rosett_ai/ai_config/fallback_chain.rb +64 -0
  220. data/lib/rosett_ai/ai_config/model_router.rb +121 -0
  221. data/lib/rosett_ai/ai_config/validator.rb +45 -0
  222. data/lib/rosett_ai/authorship/attribution_compiler.rb +99 -0
  223. data/lib/rosett_ai/authorship/disclosure_policy.rb +81 -0
  224. data/lib/rosett_ai/authorship/review_validator.rb +39 -0
  225. data/lib/rosett_ai/authorship/trailer_generator.rb +88 -0
  226. data/lib/rosett_ai/backup/compressor.rb +180 -0
  227. data/lib/rosett_ai/backup/destination.rb +91 -0
  228. data/lib/rosett_ai/behaviour/manager.rb +156 -0
  229. data/lib/rosett_ai/compiler/backend.rb +86 -0
  230. data/lib/rosett_ai/compiler/backends/agents_md_backend.rb +80 -0
  231. data/lib/rosett_ai/compiler/backends/claude_backend.rb +88 -0
  232. data/lib/rosett_ai/compiler/backends/generic_backend.rb +15 -0
  233. data/lib/rosett_ai/compiler/behaviour_compiler.rb +40 -0
  234. data/lib/rosett_ai/compiler/capability_checker.rb +104 -0
  235. data/lib/rosett_ai/compiler/compilation_pipeline.rb +361 -0
  236. data/lib/rosett_ai/compiler/compiled_output.rb +39 -0
  237. data/lib/rosett_ai/compiler/locale_compiler.rb +250 -0
  238. data/lib/rosett_ai/compiler/target_profile.rb +112 -0
  239. data/lib/rosett_ai/completion/generator.rb +101 -0
  240. data/lib/rosett_ai/completion/shells/bash_generator.rb +126 -0
  241. data/lib/rosett_ai/completion/shells/fish_generator.rb +78 -0
  242. data/lib/rosett_ai/completion/shells/zsh_generator.rb +126 -0
  243. data/lib/rosett_ai/comply/checkers/cra_checker.rb +102 -0
  244. data/lib/rosett_ai/comply/checkers/license_checker.rb +85 -0
  245. data/lib/rosett_ai/comply/checkers/spdx_header_checker.rb +98 -0
  246. data/lib/rosett_ai/comply/reporter.rb +113 -0
  247. data/lib/rosett_ai/comply/runner.rb +50 -0
  248. data/lib/rosett_ai/composition/circular_dependency_detector.rb +56 -0
  249. data/lib/rosett_ai/composition/composer.rb +158 -0
  250. data/lib/rosett_ai/composition/composition_result.rb +64 -0
  251. data/lib/rosett_ai/composition/conflict_detector.rb +53 -0
  252. data/lib/rosett_ai/composition/lockfile.rb +103 -0
  253. data/lib/rosett_ai/composition/merge_strategy.rb +131 -0
  254. data/lib/rosett_ai/composition/priority_sorter.rb +29 -0
  255. data/lib/rosett_ai/composition/scope_resolver.rb +55 -0
  256. data/lib/rosett_ai/config/compile_result.rb +37 -0
  257. data/lib/rosett_ai/config/compiler.rb +13 -0
  258. data/lib/rosett_ai/config/domain_transformer.rb +13 -0
  259. data/lib/rosett_ai/config/key_map.rb +13 -0
  260. data/lib/rosett_ai/config/masking_secret_resolver.rb +40 -0
  261. data/lib/rosett_ai/config/scope_router.rb +13 -0
  262. data/lib/rosett_ai/config/secret_resolver.rb +125 -0
  263. data/lib/rosett_ai/configuration.rb +119 -0
  264. data/lib/rosett_ai/content/content_client.rb +60 -0
  265. data/lib/rosett_ai/content/pack_installer.rb +117 -0
  266. data/lib/rosett_ai/content/pack_manifest.rb +50 -0
  267. data/lib/rosett_ai/content/pack_registry.rb +68 -0
  268. data/lib/rosett_ai/content_packs/manager.rb +50 -0
  269. data/lib/rosett_ai/dbus/compositor_detector.rb +77 -0
  270. data/lib/rosett_ai/dbus/focus_adapters/base.rb +59 -0
  271. data/lib/rosett_ai/dbus/focus_adapters/gnome_adapter.rb +172 -0
  272. data/lib/rosett_ai/dbus/focus_adapters/hyprland_adapter.rb +77 -0
  273. data/lib/rosett_ai/dbus/focus_adapters/i3_adapter.rb +65 -0
  274. data/lib/rosett_ai/dbus/focus_adapters/kwin_adapter.rb +103 -0
  275. data/lib/rosett_ai/dbus/focus_adapters/x11_adapter.rb +105 -0
  276. data/lib/rosett_ai/dbus/focus_monitor_interface.rb +103 -0
  277. data/lib/rosett_ai/dbus/manager_interface.rb +213 -0
  278. data/lib/rosett_ai/dbus/plugin_manager_interface.rb +169 -0
  279. data/lib/rosett_ai/dbus/rate_limiter.rb +89 -0
  280. data/lib/rosett_ai/dbus/service.rb +121 -0
  281. data/lib/rosett_ai/dbus/status_notifier_interface.rb +79 -0
  282. data/lib/rosett_ai/deprecation.rb +79 -0
  283. data/lib/rosett_ai/desktop/dbus_client.rb +259 -0
  284. data/lib/rosett_ai/desktop/gtk4_app.rb +371 -0
  285. data/lib/rosett_ai/desktop/gtk4_preferences.rb +331 -0
  286. data/lib/rosett_ai/desktop/gui_logger.rb +236 -0
  287. data/lib/rosett_ai/doctor/check.rb +92 -0
  288. data/lib/rosett_ai/doctor/checks/cache_health_check.rb +50 -0
  289. data/lib/rosett_ai/doctor/checks/dbus_availability_check.rb +39 -0
  290. data/lib/rosett_ai/doctor/checks/engine_detection_check.rb +46 -0
  291. data/lib/rosett_ai/doctor/checks/file_permission_check.rb +44 -0
  292. data/lib/rosett_ai/doctor/checks/gem_dependency_check.rb +55 -0
  293. data/lib/rosett_ai/doctor/checks/ruby_version_check.rb +50 -0
  294. data/lib/rosett_ai/doctor/checks/stale_config_nncc_check.rb +57 -0
  295. data/lib/rosett_ai/doctor/checks/stale_home_nncc_check.rb +59 -0
  296. data/lib/rosett_ai/doctor.rb +81 -0
  297. data/lib/rosett_ai/documentation/reference_compiler.rb +122 -0
  298. data/lib/rosett_ai/documentation/translator.rb +62 -0
  299. data/lib/rosett_ai/engines/base_config_compiler.rb +203 -0
  300. data/lib/rosett_ai/engines/detector.rb +63 -0
  301. data/lib/rosett_ai/engines/registry.rb +50 -0
  302. data/lib/rosett_ai/error_handler.rb +139 -0
  303. data/lib/rosett_ai/exit_codes.rb +76 -0
  304. data/lib/rosett_ai/feature_flags.rb +102 -0
  305. data/lib/rosett_ai/formatting.rb +33 -0
  306. data/lib/rosett_ai/gem_consistency_checker.rb +199 -0
  307. data/lib/rosett_ai/git_hooks/chain_detector.rb +86 -0
  308. data/lib/rosett_ai/git_hooks/installer.rb +175 -0
  309. data/lib/rosett_ai/git_hooks/script_generator.rb +125 -0
  310. data/lib/rosett_ai/gitlab/validators/supplementary_gitlab_ci_yaml_validator.rb +79 -0
  311. data/lib/rosett_ai/i18n/locale_resolver.rb +46 -0
  312. data/lib/rosett_ai/i18n/utf8_checker.rb +32 -0
  313. data/lib/rosett_ai/init/config_file_writer.rb +24 -0
  314. data/lib/rosett_ai/init/directory_builder.rb +38 -0
  315. data/lib/rosett_ai/init/file_copier.rb +95 -0
  316. data/lib/rosett_ai/init/global_initializer.rb +28 -0
  317. data/lib/rosett_ai/init/local_initializer.rb +27 -0
  318. data/lib/rosett_ai/init/mcp_registrar.rb +109 -0
  319. data/lib/rosett_ai/init/project_initializer.rb +38 -0
  320. data/lib/rosett_ai/licensing/license_key.rb +139 -0
  321. data/lib/rosett_ai/licensing/license_store.rb +64 -0
  322. data/lib/rosett_ai/licensing/license_validator.rb +60 -0
  323. data/lib/rosett_ai/licensing/tier.rb +42 -0
  324. data/lib/rosett_ai/mcp/admin/auditor.rb +88 -0
  325. data/lib/rosett_ai/mcp/admin/health_checker.rb +81 -0
  326. data/lib/rosett_ai/mcp/admin/registry.rb +100 -0
  327. data/lib/rosett_ai/mcp/admin/schema_validator.rb +63 -0
  328. data/lib/rosett_ai/mcp/enforcement/.gitkeep +0 -0
  329. data/lib/rosett_ai/mcp/enforcement/hook_generator.rb +197 -0
  330. data/lib/rosett_ai/mcp/enforcement/validator.rb +215 -0
  331. data/lib/rosett_ai/mcp/governance.rb +160 -0
  332. data/lib/rosett_ai/mcp/http_security_config.rb +158 -0
  333. data/lib/rosett_ai/mcp/instructions.rb +266 -0
  334. data/lib/rosett_ai/mcp/key_hasher.rb +66 -0
  335. data/lib/rosett_ai/mcp/keyfile.rb +221 -0
  336. data/lib/rosett_ai/mcp/middleware/authentication.rb +146 -0
  337. data/lib/rosett_ai/mcp/middleware/content_type.rb +56 -0
  338. data/lib/rosett_ai/mcp/middleware/cors.rb +83 -0
  339. data/lib/rosett_ai/mcp/middleware/origin_validation.rb +73 -0
  340. data/lib/rosett_ai/mcp/middleware/rate_limit.rb +106 -0
  341. data/lib/rosett_ai/mcp/middleware/request_size.rb +51 -0
  342. data/lib/rosett_ai/mcp/plugins.rb +143 -0
  343. data/lib/rosett_ai/mcp/prompts/compilation_prompt.rb +40 -0
  344. data/lib/rosett_ai/mcp/prompts/compliance_prompt.rb +41 -0
  345. data/lib/rosett_ai/mcp/prompts/diagnostics_prompt.rb +41 -0
  346. data/lib/rosett_ai/mcp/prompts/validation_prompt.rb +41 -0
  347. data/lib/rosett_ai/mcp/resources/behaviour_resource.rb +127 -0
  348. data/lib/rosett_ai/mcp/resources/config_resource.rb +72 -0
  349. data/lib/rosett_ai/mcp/resources/design_resource.rb +58 -0
  350. data/lib/rosett_ai/mcp/resources/hooks_resource.rb +74 -0
  351. data/lib/rosett_ai/mcp/resources/provenance_resource.rb +51 -0
  352. data/lib/rosett_ai/mcp/resources/rules_resource.rb +60 -0
  353. data/lib/rosett_ai/mcp/resources/schema_resource.rb +72 -0
  354. data/lib/rosett_ai/mcp/response_helper.rb +46 -0
  355. data/lib/rosett_ai/mcp/security_logger.rb +60 -0
  356. data/lib/rosett_ai/mcp/server.rb +212 -0
  357. data/lib/rosett_ai/mcp/settings/server_installer.rb +112 -0
  358. data/lib/rosett_ai/mcp/settings/trust_manager.rb +142 -0
  359. data/lib/rosett_ai/mcp/tools/adopt_tool.rb +70 -0
  360. data/lib/rosett_ai/mcp/tools/backup_tool.rb +64 -0
  361. data/lib/rosett_ai/mcp/tools/behaviour_display_tool.rb +72 -0
  362. data/lib/rosett_ai/mcp/tools/behaviour_list_tool.rb +56 -0
  363. data/lib/rosett_ai/mcp/tools/behaviour_manage_tool.rb +114 -0
  364. data/lib/rosett_ai/mcp/tools/behaviour_show_tool.rb +62 -0
  365. data/lib/rosett_ai/mcp/tools/compile_status_tool.rb +122 -0
  366. data/lib/rosett_ai/mcp/tools/compile_tool.rb +191 -0
  367. data/lib/rosett_ai/mcp/tools/comply_tool.rb +79 -0
  368. data/lib/rosett_ai/mcp/tools/config_compile_tool.rb +71 -0
  369. data/lib/rosett_ai/mcp/tools/config_status_tool.rb +79 -0
  370. data/lib/rosett_ai/mcp/tools/content_tool.rb +78 -0
  371. data/lib/rosett_ai/mcp/tools/context_query_tool.rb +156 -0
  372. data/lib/rosett_ai/mcp/tools/design_list_tool.rb +57 -0
  373. data/lib/rosett_ai/mcp/tools/design_show_tool.rb +69 -0
  374. data/lib/rosett_ai/mcp/tools/doctor_tool.rb +62 -0
  375. data/lib/rosett_ai/mcp/tools/documentation_status_tool.rb +45 -0
  376. data/lib/rosett_ai/mcp/tools/engines_tool.rb +84 -0
  377. data/lib/rosett_ai/mcp/tools/hook_install_tool.rb +190 -0
  378. data/lib/rosett_ai/mcp/tools/hook_preview_tool.rb +173 -0
  379. data/lib/rosett_ai/mcp/tools/hooks_status_tool.rb +84 -0
  380. data/lib/rosett_ai/mcp/tools/init_tool.rb +87 -0
  381. data/lib/rosett_ai/mcp/tools/license_status_tool.rb +44 -0
  382. data/lib/rosett_ai/mcp/tools/project_tool.rb +117 -0
  383. data/lib/rosett_ai/mcp/tools/provenance_tool.rb +97 -0
  384. data/lib/rosett_ai/mcp/tools/provenance_write_tool.rb +40 -0
  385. data/lib/rosett_ai/mcp/tools/retrofit_tool.rb +81 -0
  386. data/lib/rosett_ai/mcp/tools/rule_search_tool.rb +163 -0
  387. data/lib/rosett_ai/mcp/tools/schema_get_tool.rb +94 -0
  388. data/lib/rosett_ai/mcp/tools/tooling_tool.rb +86 -0
  389. data/lib/rosett_ai/mcp/tools/validate_tool.rb +105 -0
  390. data/lib/rosett_ai/mcp/tools/workflow_execute_tool.rb +74 -0
  391. data/lib/rosett_ai/mcp/tools/workflow_tool.rb +78 -0
  392. data/lib/rosett_ai/migration/detector.rb +117 -0
  393. data/lib/rosett_ai/migration/nncc_config_migrator.rb +94 -0
  394. data/lib/rosett_ai/migration/nncc_project_migrator.rb +90 -0
  395. data/lib/rosett_ai/migration/xdg_migrator.rb +123 -0
  396. data/lib/rosett_ai/package_manager/apt.rb +108 -0
  397. data/lib/rosett_ai/package_manager/base.rb +68 -0
  398. data/lib/rosett_ai/package_manager/gem_backend.rb +90 -0
  399. data/lib/rosett_ai/packaging/variant_config.rb +92 -0
  400. data/lib/rosett_ai/path_resolver.rb +115 -0
  401. data/lib/rosett_ai/plugins/contract.rb +43 -0
  402. data/lib/rosett_ai/plugins/engine_contract.rb +60 -0
  403. data/lib/rosett_ai/plugins/gui_contract.rb +74 -0
  404. data/lib/rosett_ai/plugins/mcp_contract.rb +48 -0
  405. data/lib/rosett_ai/plugins/registry.rb +150 -0
  406. data/lib/rosett_ai/policy/auditor.rb +41 -0
  407. data/lib/rosett_ai/policy/deny_list.rb +71 -0
  408. data/lib/rosett_ai/policy/opt_out_scanner.rb +37 -0
  409. data/lib/rosett_ai/policy/policy_compiler.rb +84 -0
  410. data/lib/rosett_ai/policy/protected_files.rb +47 -0
  411. data/lib/rosett_ai/policy/tier_hierarchy.rb +48 -0
  412. data/lib/rosett_ai/policy/validator.rb +35 -0
  413. data/lib/rosett_ai/profiler.rb +79 -0
  414. data/lib/rosett_ai/project/drift_detector.rb +126 -0
  415. data/lib/rosett_ai/project/manager.rb +115 -0
  416. data/lib/rosett_ai/project/sync_manager.rb +138 -0
  417. data/lib/rosett_ai/project/template_applier.rb +105 -0
  418. data/lib/rosett_ai/project_context.rb +82 -0
  419. data/lib/rosett_ai/provenance/entry.rb +63 -0
  420. data/lib/rosett_ai/provenance/file_source.rb +32 -0
  421. data/lib/rosett_ai/provenance/source.rb +62 -0
  422. data/lib/rosett_ai/provenance/store.rb +153 -0
  423. data/lib/rosett_ai/provenance/tracker.rb +62 -0
  424. data/lib/rosett_ai/provenance/trailer_generator.rb +43 -0
  425. data/lib/rosett_ai/provenance/validator.rb +45 -0
  426. data/lib/rosett_ai/quorum/collector.rb +59 -0
  427. data/lib/rosett_ai/quorum/comparator.rb +81 -0
  428. data/lib/rosett_ai/quorum/dispatcher.rb +57 -0
  429. data/lib/rosett_ai/quorum/strategies/adopt.rb +56 -0
  430. data/lib/rosett_ai/rai_config.rb +107 -0
  431. data/lib/rosett_ai/retrofit/base_parser.rb +66 -0
  432. data/lib/rosett_ai/retrofit/engine.rb +171 -0
  433. data/lib/rosett_ai/retrofit/parsers/agents_md_parser.rb +50 -0
  434. data/lib/rosett_ai/retrofit/parsers/claude_parser.rb +69 -0
  435. data/lib/rosett_ai/retrofit/parsers/cursor_parser.rb +82 -0
  436. data/lib/rosett_ai/retrofit/round_trip_validator.rb +65 -0
  437. data/lib/rosett_ai/retrofit/scanner.rb +47 -0
  438. data/lib/rosett_ai/retrofit/secret_detector.rb +87 -0
  439. data/lib/rosett_ai/secrets_resolver.rb +71 -0
  440. data/lib/rosett_ai/smart_feedback/suggester.rb +83 -0
  441. data/lib/rosett_ai/smart_feedback/thor_middleware.rb +84 -0
  442. data/lib/rosett_ai/structured_logger.rb +110 -0
  443. data/lib/rosett_ai/telemetry/json_lines_writer.rb +50 -0
  444. data/lib/rosett_ai/telemetry/log_rotator.rb +67 -0
  445. data/lib/rosett_ai/telemetry/provider.rb +26 -0
  446. data/lib/rosett_ai/telemetry/reporter.rb +144 -0
  447. data/lib/rosett_ai/telemetry.rb +47 -0
  448. data/lib/rosett_ai/text_sanitizer.rb +62 -0
  449. data/lib/rosett_ai/thor/cli.rb +269 -0
  450. data/lib/rosett_ai/thor/tasks/adopt.rb +250 -0
  451. data/lib/rosett_ai/thor/tasks/backup.rb +420 -0
  452. data/lib/rosett_ai/thor/tasks/behaviour.rb +474 -0
  453. data/lib/rosett_ai/thor/tasks/build.rb +1162 -0
  454. data/lib/rosett_ai/thor/tasks/compile.rb +415 -0
  455. data/lib/rosett_ai/thor/tasks/completion.rb +123 -0
  456. data/lib/rosett_ai/thor/tasks/comply.rb +82 -0
  457. data/lib/rosett_ai/thor/tasks/config.rb +265 -0
  458. data/lib/rosett_ai/thor/tasks/content.rb +193 -0
  459. data/lib/rosett_ai/thor/tasks/dbus.rb +321 -0
  460. data/lib/rosett_ai/thor/tasks/design.rb +258 -0
  461. data/lib/rosett_ai/thor/tasks/desktop.rb +129 -0
  462. data/lib/rosett_ai/thor/tasks/doctor.rb +127 -0
  463. data/lib/rosett_ai/thor/tasks/documentation.rb +321 -0
  464. data/lib/rosett_ai/thor/tasks/engines.rb +167 -0
  465. data/lib/rosett_ai/thor/tasks/hooks.rb +219 -0
  466. data/lib/rosett_ai/thor/tasks/init.rb +259 -0
  467. data/lib/rosett_ai/thor/tasks/license.rb +120 -0
  468. data/lib/rosett_ai/thor/tasks/mcp.rb +535 -0
  469. data/lib/rosett_ai/thor/tasks/migrate.rb +121 -0
  470. data/lib/rosett_ai/thor/tasks/plugins.rb +157 -0
  471. data/lib/rosett_ai/thor/tasks/project.rb +260 -0
  472. data/lib/rosett_ai/thor/tasks/provenance.rb +195 -0
  473. data/lib/rosett_ai/thor/tasks/release.rb +314 -0
  474. data/lib/rosett_ai/thor/tasks/retrofit.rb +90 -0
  475. data/lib/rosett_ai/thor/tasks/tooling.rb +308 -0
  476. data/lib/rosett_ai/thor/tasks/validate.rb +108 -0
  477. data/lib/rosett_ai/thor/tasks/workflow.rb +196 -0
  478. data/lib/rosett_ai/tooling/ci_yaml_validator.rb +37 -0
  479. data/lib/rosett_ai/tooling/version_checker.rb +35 -0
  480. data/lib/rosett_ai/ui/accessible_tui.rb +61 -0
  481. data/lib/rosett_ai/ui/base.rb +46 -0
  482. data/lib/rosett_ai/ui/gtk4.rb +98 -0
  483. data/lib/rosett_ai/ui/kde.rb +40 -0
  484. data/lib/rosett_ai/ui/qt6.rb +40 -0
  485. data/lib/rosett_ai/ui/registry.rb +60 -0
  486. data/lib/rosett_ai/ui/tty_helper.rb +74 -0
  487. data/lib/rosett_ai/ui/tui.rb +59 -0
  488. data/lib/rosett_ai/validators/behaviour_validator.rb +20 -0
  489. data/lib/rosett_ai/validators/design_validator.rb +17 -0
  490. data/lib/rosett_ai/validators/schema_validator.rb +84 -0
  491. data/lib/rosett_ai/validators/tooling_validator.rb +17 -0
  492. data/lib/rosett_ai/version.rb +8 -0
  493. data/lib/rosett_ai/version_consistency_checker.rb +129 -0
  494. data/lib/rosett_ai/workflow/audit_log.rb +86 -0
  495. data/lib/rosett_ai/workflow/engine.rb +142 -0
  496. data/lib/rosett_ai/workflow/manager.rb +82 -0
  497. data/lib/rosett_ai/workflow/schema_validator.rb +71 -0
  498. data/lib/rosett_ai/workflow/step_runner.rb +61 -0
  499. data/lib/rosett_ai/workflow/steps/prompt_step.rb +62 -0
  500. data/lib/rosett_ai/workflow/steps/rai_step.rb +74 -0
  501. data/lib/rosett_ai/workflow/steps/shell_step.rb +53 -0
  502. data/lib/rosett_ai/yaml_loader.rb +78 -0
  503. data/lib/rosett_ai.rb +221 -0
  504. data/lib/rubocop/cop/rosett_ai/shell_interpolation.rb +54 -0
  505. data/lib/rubocop/cop/rosett_ai/unsafe_const_get.rb +60 -0
  506. data/lib/rubocop/cop/rosett_ai/unsafe_send.rb +50 -0
  507. data/lib/rubocop/cop/rosett_ai/unsafe_yaml_load.rb +40 -0
  508. data/lib/rubocop/rosett_ai.rb +9 -0
  509. data/lib/scripts/generated/docker_hub_tags.rb +126 -0
  510. data/locales/.gitkeep +0 -0
  511. data/locales/ar.yml +579 -0
  512. data/locales/en.yml +571 -0
  513. data/locales/fr.yml +567 -0
  514. data/packaging/build-engine-deb.sh +81 -0
  515. data/packaging/scripts/postinst +17 -0
  516. data/packaging/scripts/postrm +19 -0
  517. data/packaging/scripts/prerm +10 -0
  518. data/packaging/wrapper.sh.template +38 -0
  519. data/rosett-ai.gemspec +63 -0
  520. data/rules/.gitkeep +0 -0
  521. data/scripts/publish/pulp_upload.sh +123 -0
  522. data/settings.json +29 -0
  523. data/share/applications/be.neatnerds.rosettai.desktop +29 -0
  524. data/share/dbus-1/interfaces/be.neatnerds.rosettai.xml +103 -0
  525. data/share/dbus-1/services/be.neatnerds.rosettai.service +3 -0
  526. data/share/templates/behaviour/criticalthinking.yml +69 -0
  527. 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