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,308 @@
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 'rainbow'
8
+ require 'terminal-table'
9
+
10
+ module RosettAi
11
+ module Thor
12
+ module Tasks
13
+ # Thor tasks for CI/CD tooling validation
14
+ class Tooling < ::Thor
15
+ desc 'validate-ci-yaml', 'Validate GitLab CI YAML files for syntax errors'
16
+ long_desc <<~LONGDESC
17
+ Validates all GitLab CI YAML files (.gitlab-ci.yml and includes) for syntax errors.
18
+ Reports line and column numbers for any detected issues.
19
+
20
+ Flags:
21
+ --verbose: Show all validated files, not just errors
22
+ --project-dir: Project directory (defaults to current directory)
23
+
24
+ Exit codes:
25
+ 0 - All files valid
26
+ 1 - Validation failures
27
+
28
+ EXAMPLES
29
+
30
+ raictl tooling validate-ci-yaml
31
+ raictl tooling validate-ci-yaml --verbose
32
+ raictl tooling validate-ci-yaml --project-dir /path/to/project
33
+
34
+ Related commands: validate, check-versions
35
+ LONGDESC
36
+ method_option :verbose,
37
+ type: :boolean,
38
+ default: false,
39
+ desc: 'Show all validated files, not just errors'
40
+ method_option :project_dir,
41
+ type: :string,
42
+ default: nil,
43
+ desc: 'Project directory (defaults to current directory)'
44
+
45
+ def validate_ci_yaml
46
+ validator = RosettAi::Gitlab::Validators::SupplementaryGitlabCiYamlValidator.new(
47
+ project_dir: options[:project_dir] || ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd)
48
+ )
49
+
50
+ results = validator.validate
51
+
52
+ if results[:valid].empty? && results[:invalid].empty?
53
+ puts Rainbow(t('no_ci_files')).yellow
54
+ return
55
+ end
56
+
57
+ print_ci_results(results)
58
+ raise ::Thor::Error, t('ci_validation_failed') unless results[:invalid].empty?
59
+ end
60
+
61
+ desc 'validate [NAME]', 'Validate tooling configuration file(s) against the tooling schema'
62
+ long_desc <<~LONGDESC
63
+ Validates tooling configuration files in conf/tooling/ against the tooling schema.
64
+ If NAME is provided, validates only that file; otherwise validates all tooling files.
65
+
66
+ Exit codes:
67
+ 0 - All files valid
68
+ 1 - Validation failures
69
+
70
+ EXAMPLES
71
+
72
+ raictl tooling validate
73
+ raictl tooling validate ci_pipeline
74
+
75
+ Related commands: validate-ci-yaml, check-versions
76
+ LONGDESC
77
+ def validate(name = nil)
78
+ name = RosettAi::TextSanitizer.normalize_nfc(name) if name
79
+ files = files_to_validate(name)
80
+ return puts Rainbow(t('no_tooling_files')).yellow if files.empty?
81
+
82
+ all_valid = validate_tooling_files(files)
83
+ raise ::Thor::Error, t('validation_failed') unless all_valid
84
+ end
85
+
86
+ desc 'check-versions', 'Check Ruby and gem version consistency across the codebase'
87
+ long_desc <<~LONGDESC
88
+ Checks that Ruby version references are consistent across all configuration files.
89
+ Optionally also checks gem version consistency when --gems flag is used.
90
+
91
+ Flags:
92
+ --verbose: Show all version references, not just mismatches
93
+ --gems: Also check gem version consistency
94
+ --project-dir: Project directory (defaults to current directory)
95
+
96
+ Exit codes:
97
+ 0 - All versions consistent
98
+ 1 - Version mismatches detected
99
+
100
+ EXAMPLES
101
+
102
+ raictl tooling check-versions
103
+ raictl tooling check-versions --verbose --gems
104
+
105
+ Related commands: validate-ci-yaml, validate
106
+ LONGDESC
107
+ method_option :verbose,
108
+ type: :boolean,
109
+ default: false,
110
+ desc: 'Show all version references, not just mismatches'
111
+ method_option :gems,
112
+ type: :boolean,
113
+ default: false,
114
+ desc: 'Also check gem version consistency'
115
+ method_option :project_dir,
116
+ type: :string,
117
+ default: nil,
118
+ desc: 'Project directory (defaults to current directory)'
119
+
120
+ def check_versions
121
+ project_dir = options[:project_dir] || ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd)
122
+ all_consistent = check_ruby_versions(project_dir)
123
+
124
+ if options[:gems]
125
+ gem_consistent = check_gem_versions(project_dir)
126
+ all_consistent &&= gem_consistent
127
+ end
128
+
129
+ raise ::Thor::Error, t('version_check_failed') unless all_consistent
130
+ end
131
+
132
+ private
133
+
134
+ def t(key, **)
135
+ ::I18n.t("rosett_ai.cli.tooling.#{key}", **)
136
+ end
137
+
138
+ def tooling_path
139
+ RosettAi.conf_root.join('conf', 'tooling')
140
+ end
141
+
142
+ def tooling_files
143
+ Dir.glob(tooling_path.join('*.yml'))
144
+ end
145
+
146
+ def find_tooling_file(name)
147
+ path = tooling_path.join("#{name}.yml")
148
+ path.exist? ? path.to_s : nil
149
+ end
150
+
151
+ def files_to_validate(name)
152
+ return tooling_files unless name
153
+
154
+ file = find_tooling_file(name)
155
+ unless file
156
+ puts Rainbow(t('not_found', name: name)).red
157
+ return []
158
+ end
159
+ [file]
160
+ end
161
+
162
+ def validate_tooling_files(files)
163
+ validator = RosettAi::Validators::ToolingValidator.new
164
+ files.all? { |file| tooling_file_valid?(validator, file) }
165
+ end
166
+
167
+ def tooling_file_valid?(validator, file)
168
+ basename = File.basename(file)
169
+ if validator.valid?(file)
170
+ puts "#{Rainbow(t('valid')).green} #{basename}"
171
+ true
172
+ else
173
+ display_tooling_errors(basename, validator.errors)
174
+ false
175
+ end
176
+ end
177
+
178
+ def display_tooling_errors(basename, errors)
179
+ puts "#{Rainbow(t('invalid')).red} #{basename}"
180
+ errors.each { |err| puts " #{err}" }
181
+ end
182
+
183
+ def print_ci_results(results)
184
+ if options[:verbose]
185
+ results[:valid].each do |entry|
186
+ puts " #{Rainbow(t('ok')).green} #{entry[:file]}"
187
+ end
188
+ end
189
+
190
+ results[:invalid].each do |entry|
191
+ puts " #{Rainbow(t('fail')).red} #{entry[:file]}"
192
+ puts " line #{entry[:line]}, column #{entry[:column]}: #{entry[:message]}"
193
+ end
194
+
195
+ puts
196
+ print_ci_summary(results)
197
+ end
198
+
199
+ def print_ci_summary(results)
200
+ rows = [
201
+ [t('ci_valid'), results[:valid].size],
202
+ [t('ci_invalid'), results[:invalid].size]
203
+ ]
204
+
205
+ table = ::Terminal::Table.new(
206
+ title: Rainbow(t('ci_title')).bright,
207
+ headings: ['Status', 'Count'],
208
+ rows: rows,
209
+ style: { border: :unicode_round }
210
+ )
211
+ puts table
212
+ end
213
+
214
+ def check_ruby_versions(project_dir)
215
+ checker = RosettAi::VersionConsistencyChecker.new(project_dir: project_dir)
216
+ results = checker.check
217
+ print_version_results(results)
218
+ results[:consistent]
219
+ end
220
+
221
+ def check_gem_versions(project_dir)
222
+ checker = RosettAi::GemConsistencyChecker.new(project_dir: project_dir)
223
+ results = checker.check
224
+ print_gem_results(results)
225
+ results[:consistent]
226
+ end
227
+
228
+ def print_gem_results(results)
229
+ if options[:verbose]
230
+ results[:gems].each do |entry|
231
+ status = entry[:status] == :ok ? Rainbow(t('ok')).green : Rainbow(t(entry[:status].to_s)).red
232
+ resolved = entry[:resolved] || '-'
233
+ puts " #{status} #{entry[:name]} #{entry[:constraint]} #{resolved}"
234
+ end
235
+ puts
236
+ end
237
+
238
+ results[:stale_references].each do |ref|
239
+ puts " #{Rainbow(t('gem_stale_reference')).red} #{ref[:file]}:#{ref[:line]} " \
240
+ "#{ref[:gem]} found #{ref[:found]}, expected #{ref[:expected]}"
241
+ end
242
+
243
+ results[:gems].reject { |entry| entry[:status] == :ok }.each do |entry|
244
+ label = t("gem_status_#{entry[:status]}")
245
+ puts " #{Rainbow(label).red} #{entry[:name]} #{entry[:constraint]} #{entry[:resolved] || '-'}"
246
+ end
247
+
248
+ puts
249
+ print_gem_summary(results)
250
+ end
251
+
252
+ def print_gem_summary(results)
253
+ ok_count = results[:gems].count { |entry| entry[:status] == :ok }
254
+ problem_count = results[:gems].size - ok_count
255
+
256
+ rows = [
257
+ [t('gem_total'), results[:gems].size],
258
+ [t('gem_ok'), ok_count],
259
+ [t('gem_problems'), problem_count],
260
+ [t('gem_stale_refs'), results[:stale_references].size]
261
+ ]
262
+
263
+ table = ::Terminal::Table.new(
264
+ title: Rainbow(t('gem_title')).bright,
265
+ headings: ['Check', 'Result'],
266
+ rows: rows,
267
+ style: { border: :unicode_round }
268
+ )
269
+ puts table
270
+ end
271
+
272
+ def print_version_results(results)
273
+ if options[:verbose]
274
+ results[:references].each do |ref|
275
+ status = ref[:found] == results[:expected_version] ? Rainbow(t('ok')).green : Rainbow(t('mismatch')).red
276
+ puts " #{status} #{ref[:file]}:#{ref[:line]} #{ref[:found]}"
277
+ end
278
+ puts
279
+ end
280
+
281
+ results[:mismatches].each do |ref|
282
+ puts " #{Rainbow(t('mismatch')).red} #{ref[:file]}:#{ref[:line]} " \
283
+ "found #{ref[:found]}, expected #{results[:expected_version]}"
284
+ end
285
+
286
+ puts
287
+ print_version_summary(results)
288
+ end
289
+
290
+ def print_version_summary(results)
291
+ rows = [
292
+ [t('expected_version'), results[:expected_version]],
293
+ [t('total_references'), results[:references].size],
294
+ [t('mismatches'), results[:mismatches].size]
295
+ ]
296
+
297
+ table = ::Terminal::Table.new(
298
+ title: Rainbow(t('version_title')).bright,
299
+ headings: ['Check', 'Result'],
300
+ rows: rows,
301
+ style: { border: :unicode_round }
302
+ )
303
+ puts table
304
+ end
305
+ end
306
+ end
307
+ end
308
+ end
@@ -0,0 +1,108 @@
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 'rainbow'
8
+ require 'terminal-table'
9
+
10
+ module RosettAi
11
+ module Thor
12
+ module Tasks
13
+ # Unified validation command — runs all schema validators sequentially
14
+ # and reports aggregated results.
15
+ class Validate < ::Thor
16
+ default_task :all
17
+
18
+ CATEGORIES = [
19
+ { name: 'behaviour', dir: 'conf/behaviour', validator: 'BehaviourValidator' },
20
+ { name: 'design', dir: 'conf/design', validator: 'DesignValidator' },
21
+ { name: 'tooling', dir: 'conf/tooling', validator: 'ToolingValidator' }
22
+ ].freeze
23
+
24
+ desc 'all', 'Validate all configuration files across all categories'
25
+ long_desc <<~LONGDESC
26
+ Validates all configuration files across behaviour, design, and tooling categories.
27
+ Runs schema validation for each category and reports aggregated results in a summary table.
28
+
29
+ Exit codes:
30
+ 0 - All files valid
31
+ 1 - Validation failures detected
32
+
33
+ EXAMPLES
34
+
35
+ raictl validate
36
+
37
+ Related commands: behaviour validate, design validate, tooling validate
38
+ LONGDESC
39
+ def all
40
+ results = CATEGORIES.map { |cat| validate_category(cat) }
41
+ print_summary(results)
42
+
43
+ failed = results.any? { |r| r[:failed].positive? }
44
+ raise ::Thor::Error, t('validation_failed') if failed
45
+ end
46
+
47
+ private
48
+
49
+ def t(key, **)
50
+ ::I18n.t("rosett_ai.cli.validate.#{key}", **)
51
+ end
52
+
53
+ def validate_category(category)
54
+ dir = RosettAi.conf_root.join(category[:dir])
55
+ files = Dir.glob(dir.join('*.yml'))
56
+
57
+ result = { category: category[:name], total: files.size, passed: 0, failed: 0, errors: [] }
58
+ return result if files.empty?
59
+
60
+ validator = RosettAi::Validators.const_get(category[:validator]).new
61
+ files.each { |file| validate_file(validator, file, result) }
62
+ result
63
+ rescue RosettAi::ConfigurationError => e
64
+ puts " #{Rainbow(t('invalid')).red} #{category[:name]}: #{e.message}"
65
+ { category: category[:name], total: 0, passed: 0, failed: 1, errors: [e.message] }
66
+ end
67
+
68
+ def validate_file(validator, file, result)
69
+ basename = File.basename(file)
70
+ if validator.valid?(file)
71
+ result[:passed] += 1
72
+ puts " #{Rainbow(t('valid')).green} #{result[:category]}/#{basename}"
73
+ else
74
+ result[:failed] += 1
75
+ result[:errors] << { file: basename, messages: validator.errors.dup }
76
+ puts " #{Rainbow(t('invalid')).red} #{result[:category]}/#{basename}"
77
+ validator.errors.each { |err| puts " #{err}" }
78
+ end
79
+ end
80
+
81
+ def print_summary(results)
82
+ puts
83
+
84
+ rows = results.map do |r|
85
+ status = r[:failed].positive? ? Rainbow(t('fail')).red : Rainbow(t('ok')).green
86
+ [r[:category], r[:total], r[:passed], r[:failed], status]
87
+ end
88
+
89
+ total_files = results.sum { |r| r[:total] }
90
+ total_passed = results.sum { |r| r[:passed] }
91
+ total_failed = results.sum { |r| r[:failed] }
92
+ total_status = total_failed.positive? ? Rainbow(t('fail')).red : Rainbow(t('ok')).green
93
+
94
+ rows << :separator
95
+ rows << [Rainbow(t('total')).bright, total_files, total_passed, total_failed, total_status]
96
+
97
+ table = ::Terminal::Table.new(
98
+ title: Rainbow(t('summary_title')).bright,
99
+ headings: [t('category'), t('files'), t('passed'), t('failed_label'), t('status')],
100
+ rows: rows,
101
+ style: { border: :unicode_round }
102
+ )
103
+ puts table
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,196 @@
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 workflow` — declarative workflow execution.
14
+ #
15
+ # Provides commands to list, validate, simulate, and run workflows.
16
+ # Workflows are YAML files defining multi-step automation sequences.
17
+ #
18
+ # @author hugo
19
+ # @author claude
20
+ class Workflow < ::Thor
21
+ desc 'list', 'List available workflow files'
22
+ long_desc <<~LONGDESC
23
+ Lists all workflow YAML files found in conf/workflows/ and
24
+ the project .rosett-ai/workflows/ directory.
25
+
26
+ EXAMPLES
27
+
28
+ raictl workflow list
29
+ LONGDESC
30
+ def list
31
+ files = discover_workflows
32
+ if files.empty?
33
+ warn ::I18n.t('rosett_ai.workflow.no_files')
34
+ return
35
+ end
36
+
37
+ rows = files.filter_map do |path|
38
+ data = load_workflow_safe(path)
39
+ next unless data
40
+
41
+ [data['name'] || File.basename(path, '.yml'), data['version'] || '-',
42
+ data.fetch('steps', []).size, path.to_s]
43
+ end
44
+
45
+ table = ::Terminal::Table.new(
46
+ title: ::I18n.t('rosett_ai.workflow.list_title'),
47
+ headings: [::I18n.t('rosett_ai.workflow.name'), ::I18n.t('rosett_ai.workflow.version'),
48
+ ::I18n.t('rosett_ai.workflow.steps_count'), ::I18n.t('rosett_ai.workflow.path')],
49
+ rows: rows
50
+ )
51
+ puts table
52
+ end
53
+
54
+ desc 'validate NAME', 'Validate a workflow definition'
55
+ long_desc <<~LONGDESC
56
+ Validates the workflow YAML file against the workflow schema
57
+ and runs security checks (array-form commands, max step count).
58
+
59
+ EXAMPLES
60
+
61
+ raictl workflow validate release
62
+ LONGDESC
63
+ def validate(name)
64
+ path = resolve_workflow(name)
65
+ engine = RosettAi::Workflow::Engine.new(workflow_path: path)
66
+ errors = engine.validate
67
+
68
+ if errors.empty?
69
+ puts Rainbow(::I18n.t('rosett_ai.workflow.valid', name: name)).green
70
+ else
71
+ warn Rainbow(::I18n.t('rosett_ai.workflow.invalid', name: name)).red
72
+ errors.each { |err| warn " - #{err}" }
73
+ exit 2
74
+ end
75
+ end
76
+
77
+ desc 'simulate NAME', 'Dry-run a workflow (show steps without executing)'
78
+ long_desc <<~LONGDESC
79
+ Shows what each step would do without actually executing.
80
+ Useful for reviewing a workflow before running it.
81
+
82
+ EXAMPLES
83
+
84
+ raictl workflow simulate release
85
+ LONGDESC
86
+ def simulate(name)
87
+ path = resolve_workflow(name)
88
+ engine = RosettAi::Workflow::Engine.new(workflow_path: path)
89
+ descriptions = engine.simulate
90
+
91
+ puts ::I18n.t('rosett_ai.workflow.simulate_header', name: name)
92
+ descriptions.each_with_index do |desc, idx|
93
+ puts " #{idx + 1}. #{desc}"
94
+ end
95
+ end
96
+
97
+ desc 'execute NAME', 'Execute a workflow'
98
+ long_desc <<~LONGDESC
99
+ Executes all steps in the named workflow sequentially.
100
+
101
+ Use --resume to skip steps already completed in a previous run
102
+ (detected via the audit log). Use --format json for CI output.
103
+
104
+ Exit codes: 0 = all pass, 1 = any failure.
105
+
106
+ EXAMPLES
107
+
108
+ raictl workflow execute release
109
+ raictl workflow execute deploy --resume
110
+ raictl workflow execute ci-check --format json
111
+ LONGDESC
112
+ method_option :resume, type: :boolean, default: false,
113
+ desc: 'Resume from last completed step'
114
+ method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
115
+ desc: 'Output format'
116
+ def execute(name)
117
+ path = resolve_workflow(name)
118
+ audit_path = resolve_audit_log_path(name)
119
+ engine = RosettAi::Workflow::Engine.new(workflow_path: path, audit_log_path: audit_path)
120
+ summary = engine.execute(resume: options[:resume])
121
+
122
+ if options[:format] == 'json'
123
+ puts JSON.pretty_generate(summary)
124
+ else
125
+ render_table(summary)
126
+ end
127
+
128
+ exit 1 if summary[:status] == 'fail'
129
+ end
130
+
131
+ private
132
+
133
+ def discover_workflows
134
+ paths = []
135
+ conf_dir = RosettAi.root.join('conf', 'workflows')
136
+ paths += Dir.glob(conf_dir.join('*.yml').to_s).map { |path| Pathname.new(path) } if conf_dir.directory?
137
+
138
+ project_dir = Pathname.new(ENV.fetch('RAI_ORIGINAL_PWD', Dir.pwd)).join('.rosett-ai', 'workflows')
139
+ paths += Dir.glob(project_dir.join('*.yml').to_s).map { |path| Pathname.new(path) } if project_dir.directory?
140
+
141
+ paths
142
+ end
143
+
144
+ def resolve_workflow(name)
145
+ candidates = discover_workflows
146
+ match = candidates.find { |path| File.basename(path, '.yml') == name }
147
+ raise RosettAi::WorkflowError, ::I18n.t('rosett_ai.workflow.not_found', name: name) unless match
148
+
149
+ match
150
+ end
151
+
152
+ def resolve_audit_log_path(name)
153
+ RosettAi.root.join('tmp', 'workflow_audit', "#{name}.jsonl")
154
+ end
155
+
156
+ def load_workflow_safe(path)
157
+ YAML.safe_load(path.read, permitted_classes: [Date])
158
+ rescue StandardError
159
+ nil
160
+ end
161
+
162
+ def render_table(summary)
163
+ rows = summary[:results].map do |result|
164
+ status_text = colorize_status(result[:status])
165
+ duration = result[:duration_ms] ? "#{result[:duration_ms]}ms" : '-'
166
+ [result[:step], status_text, result[:message], duration]
167
+ end
168
+
169
+ table = ::Terminal::Table.new(
170
+ title: ::I18n.t('rosett_ai.workflow.run_title', name: summary[:workflow]),
171
+ headings: [::I18n.t('rosett_ai.workflow.step_label'), ::I18n.t('rosett_ai.workflow.status_label'),
172
+ ::I18n.t('rosett_ai.workflow.message_label'), ::I18n.t('rosett_ai.workflow.duration_label')],
173
+ rows: rows
174
+ )
175
+ puts table
176
+ puts summary_line(summary)
177
+ end
178
+
179
+ def colorize_status(status)
180
+ case status
181
+ when 'pass' then Rainbow(status).green
182
+ when 'fail' then Rainbow(status).red
183
+ when 'skip' then Rainbow(status).yellow
184
+ else status
185
+ end
186
+ end
187
+
188
+ def summary_line(summary)
189
+ ::I18n.t('rosett_ai.workflow.run_summary',
190
+ total: summary[:total], pass: summary[:pass],
191
+ fail: summary[:fail], skip: summary[:skip])
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Tooling
8
+ # MCP-facing facade for CI YAML validation.
9
+ #
10
+ # Delegates to the GitLab CI YAML validators, providing the
11
+ # simplified interface expected by {Mcp::Tools::ToolingTool}.
12
+ #
13
+ # @author hugo
14
+ # @author claude
15
+ class CiYamlValidator
16
+ def initialize(project_dir: Dir.pwd)
17
+ @project_dir = project_dir
18
+ end
19
+
20
+ # Validates all CI YAML files in the project.
21
+ #
22
+ # @return [Hash] with :errors and :files keys
23
+ def validate
24
+ validator = RosettAi::Gitlab::Validators::SupplementaryGitlabCiYamlValidator.new(
25
+ project_dir: @project_dir
26
+ )
27
+ result = validator.validate
28
+ valid_files = result.fetch(:valid, []).map { |entry| entry[:file] }
29
+ invalid_entries = result.fetch(:invalid, [])
30
+ {
31
+ errors: invalid_entries.map { |entry| entry[:message] },
32
+ files: valid_files + invalid_entries.map { |entry| entry[:file] }
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Tooling
8
+ # MCP-facing facade for tool version checking.
9
+ #
10
+ # Delegates to {VersionConsistencyChecker} and {GemConsistencyChecker}
11
+ # for the actual version checks, providing the simplified interface
12
+ # expected by {Mcp::Tools::ToolingTool}.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class VersionChecker
17
+ def initialize(project_dir: Dir.pwd)
18
+ @project_dir = project_dir
19
+ end
20
+
21
+ # Checks tool and gem versions for consistency.
22
+ #
23
+ # @return [Hash] with :outdated and :tools keys
24
+ def check
25
+ checker = RosettAi::VersionConsistencyChecker.new(project_dir: @project_dir)
26
+ result = checker.check
27
+ outdated = result[:mismatches] || []
28
+ {
29
+ outdated: outdated,
30
+ tools: result[:references] || []
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end