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,127 @@
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
+
8
+ module RosettAi
9
+ module Thor
10
+ module Tasks
11
+ # CLI task for `rai doctor` — runtime diagnostics.
12
+ #
13
+ # @author hugo
14
+ # @author claude
15
+ class Doctor < ::Thor
16
+ default_task :status
17
+
18
+ desc 'status', 'Run all diagnostic checks'
19
+ long_desc <<~LONGDESC
20
+ Runs all registered diagnostic checks and displays a summary.
21
+
22
+ Checks include Ruby version, gem dependencies, file permissions,
23
+ D-Bus availability, engine detection, and cache health.
24
+
25
+ Exit codes: 0 = all pass, 1 = any warn, 2 = any fail.
26
+
27
+ EXAMPLES
28
+
29
+ raictl doctor
30
+ raictl doctor --check ruby_version
31
+ raictl doctor --format json
32
+ LONGDESC
33
+ method_option :check, type: :string, desc: 'Run only the named check'
34
+ method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
35
+ desc: 'Output format'
36
+ def status
37
+ validate_check_name!(options[:check]) if options[:check]
38
+ results = RosettAi::Doctor.run_all(only: options[:check])
39
+
40
+ if options[:format] == 'json'
41
+ print_json(results)
42
+ else
43
+ print_header
44
+ print_table_output(results)
45
+ print_summary(results)
46
+ end
47
+
48
+ code = RosettAi::Doctor.exit_code(results)
49
+ exit code unless code.zero?
50
+ end
51
+
52
+ private
53
+
54
+ def validate_check_name!(name)
55
+ return if RosettAi::Doctor.check_names.include?(name)
56
+
57
+ raise ::Thor::Error,
58
+ "Unknown check '#{name}'. Available: #{RosettAi::Doctor.check_names.join(', ')}"
59
+ end
60
+
61
+ def t(key, **args)
62
+ ::I18n.t("rosett_ai.doctor.#{key}", **args)
63
+ end
64
+
65
+ def print_header
66
+ say "raictl #{RosettAi::VERSION} | Ruby #{RUBY_VERSION} | #{RUBY_PLATFORM}"
67
+ say ''
68
+ end
69
+
70
+ def print_table_output(results)
71
+ table = ::Terminal::Table.new(
72
+ headings: [t('header.check'), t('header.status'), t('header.details')],
73
+ rows: results.map { |r| build_row(r) }
74
+ )
75
+ say table
76
+ say ''
77
+ end
78
+
79
+ def build_row(result)
80
+ status_text = format_status(result[:status])
81
+ detail = result[:message]
82
+ detail = "#{detail}\n #{result[:remediation]}" if result[:remediation]
83
+ [result[:name], status_text, detail]
84
+ end
85
+
86
+ def format_status(status)
87
+ case status
88
+ when 'pass' then Rainbow('PASS').green
89
+ when 'warn' then Rainbow('WARN').yellow
90
+ when 'fail' then Rainbow('FAIL').red
91
+ else status.upcase
92
+ end
93
+ end
94
+
95
+ def print_summary(results)
96
+ counts = results.group_by { |r| r[:status] }.transform_values(&:size)
97
+ total = results.size
98
+ warn_count = counts.fetch('warn', 0)
99
+ fail_count = counts.fetch('fail', 0)
100
+
101
+ if fail_count.positive?
102
+ say Rainbow(t('summary.failures', total: total, fail: fail_count)).red
103
+ elsif warn_count.positive?
104
+ say Rainbow(t('summary.warnings', total: total, warn: warn_count)).yellow
105
+ else
106
+ say Rainbow(t('summary.all_pass', total: total)).green
107
+ end
108
+ end
109
+
110
+ def print_json(results)
111
+ output = {
112
+ rai_version: RosettAi::VERSION,
113
+ ruby_version: RUBY_VERSION,
114
+ platform: RUBY_PLATFORM,
115
+ checks: results,
116
+ summary: {
117
+ pass: results.count { |r| r[:status] == 'pass' },
118
+ warn: results.count { |r| r[:status] == 'warn' },
119
+ fail: results.count { |r| r[:status] == 'fail' }
120
+ }
121
+ }
122
+ say JSON.generate(output)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,321 @@
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 'tty-spinner'
8
+ require 'paint'
9
+ require 'pathname'
10
+
11
+ module RosettAi
12
+ module Thor
13
+ module Tasks
14
+ # Documentation management tasks
15
+ class Documentation < ::Thor
16
+ TYPE_COLORS = {
17
+ lib: :cyan, behaviour: :magenta, dependency: :yellow, reference: :blue
18
+ }.freeze
19
+ desc 'update', 'Update documentation based on code/config changes'
20
+ long_desc <<~LONGDESC
21
+ Scans the codebase for undocumented changes and updates documentation files accordingly.
22
+ Checks for new/changed methods, behaviour files, dependencies, and LaTeX reference documents.
23
+
24
+ Flags:
25
+ --locale: Target locale for documentation (default: en)
26
+ --all-locales: Update documentation for all configured locales
27
+ --dry-run: Show what would be updated without making changes
28
+ --skip-reference: Skip LaTeX reference document compilation
29
+
30
+ EXAMPLES
31
+
32
+ raictl documentation update
33
+ raictl documentation update --dry-run
34
+ raictl documentation update --all-locales
35
+ raictl documentation update --locale fr --skip-reference
36
+
37
+ Related commands: status, translate
38
+ LONGDESC
39
+ method_option :locale,
40
+ type: :string,
41
+ default: 'en',
42
+ desc: 'Target locale for documentation'
43
+ method_option :all_locales,
44
+ type: :boolean,
45
+ default: false,
46
+ desc: 'Update documentation for all configured locales'
47
+ method_option :dry_run,
48
+ type: :boolean,
49
+ default: false,
50
+ desc: 'Show what would be updated without making changes'
51
+ method_option :skip_reference,
52
+ type: :boolean,
53
+ default: false,
54
+ desc: 'Skip LaTeX reference document compilation'
55
+
56
+ def update
57
+ changes = scan_with_spinner
58
+ return puts Paint[t('no_updates'), :yellow] if changes.empty?
59
+
60
+ display_changes(changes)
61
+ return if options[:dry_run]
62
+
63
+ reference_changes, locale_changes = changes.partition { |change| change[:type] == :reference }
64
+ reference_changes.each { |_change| compile_reference_document! }
65
+ apply_changes_with_spinner(locale_changes) unless locale_changes.empty?
66
+ end
67
+
68
+ desc 'status', 'Show documentation status and coverage'
69
+ long_desc <<~LONGDESC
70
+ Displays documentation file count, last modified timestamps, and configured locales.
71
+ Provides an overview of the current documentation state without making changes.
72
+
73
+ EXAMPLES
74
+
75
+ raictl documentation status
76
+
77
+ Related commands: update, translate
78
+ LONGDESC
79
+ def status
80
+ display_status_header
81
+ display_doc_files
82
+ display_configured_locales
83
+ end
84
+
85
+ desc 'translate', 'Translate documentation files'
86
+ long_desc <<~LONGDESC
87
+ Translates documentation files from one locale to another.
88
+ If no source file is specified, translates all markdown files in the doc/ directory.
89
+
90
+ Flags:
91
+ --from: Source locale (default: en)
92
+ --to: Target locale (required)
93
+
94
+ EXAMPLES
95
+
96
+ raictl documentation translate --to fr
97
+ raictl documentation translate doc/README.md --from en --to ar
98
+
99
+ Related commands: update, status
100
+ LONGDESC
101
+ method_option :from,
102
+ type: :string,
103
+ default: 'en',
104
+ desc: 'Source locale'
105
+ method_option :to,
106
+ type: :string,
107
+ required: true,
108
+ desc: 'Target locale'
109
+
110
+ def translate(source_file = nil)
111
+ files = source_file ? [source_file] : Dir.glob(RosettAi.root.join('doc', '*.md'))
112
+
113
+ translator = RosettAi::Documentation::Translator.new
114
+
115
+ files.each do |file|
116
+ unless File.exist?(file)
117
+ puts Rainbow(t('translate_source_missing', path: file)).red
118
+ next
119
+ end
120
+
121
+ result = translator.translate(file, from: options[:from], to: options[:to])
122
+ puts Rainbow(t('translate_done', path: result)).green
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def t(key, **)
129
+ ::I18n.t("rosett_ai.cli.documentation.#{key}", **)
130
+ end
131
+
132
+ def scan_with_spinner
133
+ spinner = TTY::Spinner.new('[:spinner] Scanning for changes...', format: :dots)
134
+ spinner.auto_spin
135
+ changes = scan_for_changes
136
+ spinner.success(Paint[t('scan_complete'), :green])
137
+ changes
138
+ end
139
+
140
+ def apply_changes_with_spinner(changes)
141
+ spinner = TTY::Spinner.new("[:spinner] #{t('updating')}", format: :dots)
142
+ spinner.auto_spin
143
+ apply_changes(changes)
144
+ spinner.success(Paint[t('done'), :green])
145
+ end
146
+
147
+ def display_status_header
148
+ puts Paint[t('status_title'), :bright]
149
+ puts '-' * 40
150
+ end
151
+
152
+ def display_doc_files
153
+ doc_files = Dir.glob(RosettAi.root.join('doc', '*.md'))
154
+ puts t('doc_files', count: doc_files.count)
155
+ doc_files.each { |file| display_doc_file_info(file) }
156
+ end
157
+
158
+ def display_doc_file_info(file)
159
+ basename = File.basename(file)
160
+ mtime = File.mtime(file).strftime('%Y-%m-%d %H:%M')
161
+ puts " #{Paint[basename, :cyan]}: #{mtime}"
162
+ end
163
+
164
+ def display_configured_locales
165
+ locale_dirs = Dir.glob(RosettAi.root.join('doc', 'locales', '*'))
166
+ puts "\n#{t('configured_locales', count: locale_dirs.count)}"
167
+ locale_dirs.each { |dir| puts " #{Paint[File.basename(dir), :cyan]}" }
168
+ end
169
+
170
+ def scan_for_changes
171
+ changes = []
172
+
173
+ # Scan for new/changed configuration options in lib/
174
+ changes.concat(scan_lib_changes)
175
+
176
+ # Scan for new behaviour files
177
+ changes.concat(scan_behaviour_changes)
178
+
179
+ # Scan for dependency changes
180
+ changes.concat(scan_dependency_changes)
181
+
182
+ # Scan for stale LaTeX reference document
183
+ changes.concat(scan_reference_changes)
184
+
185
+ changes
186
+ end
187
+
188
+ def scan_lib_changes
189
+ changes = []
190
+ lib_files = Dir.glob(RosettAi.root.join('lib', '**', '*.rb'))
191
+
192
+ lib_files.each do |file|
193
+ # Check for undocumented public methods
194
+ content = File.read(file)
195
+ if content.match?(/^\s*def\s+[^_]/) && !documented?(file)
196
+ changes << { type: :lib, file: file, reason: t('undocumented') }
197
+ end
198
+ end
199
+
200
+ changes
201
+ end
202
+
203
+ def scan_behaviour_changes
204
+ changes = []
205
+ behaviour_files = Dir.glob(RosettAi.conf_root.join('conf', 'behaviour', '*.yml'))
206
+ doc_file = RosettAi.root.join('doc', 'BEHAVIOUR.md')
207
+
208
+ return changes unless doc_file.exist?
209
+
210
+ doc_content = doc_file.read
211
+ behaviour_files.each do |file|
212
+ basename = File.basename(file, '.yml')
213
+ unless doc_content.include?(basename)
214
+ changes << { type: :behaviour, file: file, reason: t('not_in_behaviour_md') }
215
+ end
216
+ end
217
+
218
+ changes
219
+ end
220
+
221
+ def scan_dependency_changes
222
+ changes = []
223
+ gemfile = RosettAi.root.join('Gemfile')
224
+ doc_file = RosettAi.root.join('doc', 'SETUP.md')
225
+
226
+ return changes unless gemfile.exist? && doc_file.exist?
227
+
228
+ if gemfile.mtime > doc_file.mtime
229
+ changes << { type: :dependency, file: gemfile.to_s, reason: t('gemfile_newer') }
230
+ end
231
+
232
+ changes
233
+ end
234
+
235
+ def scan_reference_changes
236
+ changes = []
237
+ return changes if options[:skip_reference]
238
+
239
+ compiler = RosettAi::Documentation::ReferenceCompiler.new
240
+ return changes unless compiler.source_dir.exist?
241
+
242
+ if !compiler.output_path.exist?
243
+ changes << { type: :reference, file: compiler.output_path.to_s,
244
+ reason: t('reference.missing') }
245
+ elsif compiler.stale?
246
+ changes << { type: :reference, file: compiler.output_path.to_s,
247
+ reason: t('reference.stale') }
248
+ end
249
+
250
+ changes
251
+ end
252
+
253
+ def compile_reference_document!
254
+ compiler = RosettAi::Documentation::ReferenceCompiler.new
255
+
256
+ unless compiler.available?
257
+ puts Paint[t('reference.tools_missing', tools: compiler.missing_tools.join(', ')), :yellow]
258
+ return
259
+ end
260
+
261
+ spinner = TTY::Spinner.new("[:spinner] #{t('reference.compiling')}", format: :dots)
262
+ spinner.auto_spin
263
+ compiler.compile!
264
+ spinner.success(Paint[t('reference.compiled', path: compiler.output_path), :green])
265
+ end
266
+
267
+ def documented?(file)
268
+ content = File.read(file)
269
+ # Simple heuristic: check for YARD-style documentation
270
+ content.match?(/^\s*#\s*@|^\s*# [A-Z]/)
271
+ end
272
+
273
+ def display_changes(changes)
274
+ puts Paint["\n#{t('pending_updates')}", :bright]
275
+ changes.each do |change|
276
+ type_color = TYPE_COLORS[change[:type]]
277
+ puts " [#{Paint[change[:type].to_s.upcase, type_color]}] #{change[:file]}"
278
+ puts " Reason: #{change[:reason]}"
279
+ end
280
+ puts
281
+ end
282
+
283
+ def apply_changes(changes)
284
+ target_locales.each do |locale|
285
+ changes.each { |change| apply_single_change(change, locale) }
286
+ end
287
+ end
288
+
289
+ def target_locales
290
+ options[:all_locales] ? available_locales : [options[:locale]]
291
+ end
292
+
293
+ def apply_single_change(change, locale)
294
+ case change[:type]
295
+ when :lib then update_api_documentation(change[:file], locale)
296
+ when :behaviour then update_behaviour_documentation(change[:file], locale)
297
+ when :dependency then update_setup_documentation(locale)
298
+ end
299
+ end
300
+
301
+ def available_locales
302
+ locale_dirs = Dir.glob(RosettAi.root.join('doc', 'locales', '*'))
303
+ locales = locale_dirs.map { |dir| File.basename(dir) }
304
+ locales.empty? ? ['en'] : locales
305
+ end
306
+
307
+ def update_api_documentation(file, locale)
308
+ puts " [not yet implemented] #{t('updating_api', file: File.basename(file), target_locale: locale)}"
309
+ end
310
+
311
+ def update_behaviour_documentation(file, locale)
312
+ puts " [not yet implemented] #{t('updating_behaviour', file: File.basename(file), target_locale: locale)}"
313
+ end
314
+
315
+ def update_setup_documentation(locale)
316
+ puts " [not yet implemented] #{t('updating_setup', target_locale: locale)}"
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,167 @@
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 engine detection and management
14
+ class Engines < ::Thor
15
+ desc 'list', 'List all known engines'
16
+ long_desc <<~LONGDESC
17
+ Lists all engines registered in the Rosett-AI ecosystem.
18
+ Displays engine name, display name, and version in a formatted table.
19
+
20
+ EXAMPLES
21
+
22
+ raictl engines list
23
+
24
+ Related commands: detect, status, plugins
25
+ LONGDESC
26
+ def list
27
+ rows = RosettAi::Engines::Registry.available.map do |name|
28
+ manifest = RosettAi::Engines::Registry.manifest(name)
29
+ [manifest['name'], manifest['display_name'], manifest['version']]
30
+ rescue StandardError => e
31
+ [name, Rainbow("error: #{e.message}").red, '?']
32
+ end
33
+
34
+ table = ::Terminal::Table.new(
35
+ title: Rainbow(t('list_title')).bright,
36
+ headings: [t('name'), t('display_name'), t('version')],
37
+ rows: rows,
38
+ style: { border: :unicode_round }
39
+ )
40
+ puts table
41
+ end
42
+
43
+ desc 'detect', 'Detect installed engines on this system'
44
+ long_desc <<~LONGDESC
45
+ Runs detection checks for all engines that have a detector component.
46
+ Shows which engines are installed and available on the current system.
47
+
48
+ EXAMPLES
49
+
50
+ raictl engines detect
51
+
52
+ Related commands: list, status, plugins
53
+ LONGDESC
54
+ def detect
55
+ rows = RosettAi::Engines::Registry.available.filter_map do |name|
56
+ manifest = RosettAi::Engines::Registry.manifest(name)
57
+ next unless manifest.dig('components', 'detector')
58
+
59
+ result = build_detector(name).detect
60
+ result[:checks].map do |check|
61
+ status = check[:passed] ? Rainbow(t('found')).green : Rainbow(t('not_found')).red
62
+ [result[:display_name], check[:type], check[:name], status]
63
+ end
64
+ rescue StandardError => e
65
+ [[name, 'manifest', '-', Rainbow("error: #{e.message}").red]]
66
+ end.flatten(1)
67
+
68
+ table = ::Terminal::Table.new(
69
+ title: Rainbow(t('detect_title')).bright,
70
+ headings: [t('engine'), t('check_type'), t('check_name'), t('status')],
71
+ rows: rows,
72
+ style: { border: :unicode_round }
73
+ )
74
+ puts table
75
+ end
76
+
77
+ desc 'status', 'Show default engine and detection status'
78
+ long_desc <<~LONGDESC
79
+ Displays the currently configured default engine and detection status for all engines.
80
+ Shows which engines are detected and which one is set as default.
81
+
82
+ EXAMPLES
83
+
84
+ raictl engines status
85
+
86
+ Related commands: list, detect, plugins
87
+ LONGDESC
88
+ def status
89
+ default_engine = RosettAi.rai_config.default_engine
90
+ puts "#{t('default_engine')}: #{Rainbow(default_engine).bright}"
91
+ puts
92
+
93
+ rows = RosettAi::Engines::Registry.available.map do |name|
94
+ manifest = RosettAi::Engines::Registry.manifest(name)
95
+ detected = if manifest.dig('components', 'detector')
96
+ result = build_detector(name).detect
97
+ result[:detected] ? Rainbow(t('yes')).green : Rainbow(t('no')).red
98
+ else
99
+ Rainbow(t('no_detector')).yellow
100
+ end
101
+ is_default = name == default_engine ? Rainbow(t('yes')).green : t('no')
102
+ [manifest['display_name'], detected, is_default]
103
+ rescue StandardError => e
104
+ [name, Rainbow("error: #{e.message}").red, '?']
105
+ end
106
+
107
+ table = ::Terminal::Table.new(
108
+ title: Rainbow(t('status_title')).bright,
109
+ headings: [t('engine'), t('detected'), t('default')],
110
+ rows: rows,
111
+ style: { border: :unicode_round }
112
+ )
113
+ puts table
114
+ end
115
+
116
+ desc 'plugins', 'Show registered plugin engines'
117
+ long_desc <<~LONGDESC
118
+ Lists all third-party plugin engines registered in the Rosett-AI plugin system.
119
+ Shows plugin name, display name, and version in a formatted table.
120
+
121
+ Related commands: list, detect, status
122
+ LONGDESC
123
+ def plugins
124
+ plugin_engines = RosettAi::Plugins::Registry.available(:engine)
125
+
126
+ if plugin_engines.empty?
127
+ puts Rainbow(t('no_plugins')).yellow
128
+ return
129
+ end
130
+
131
+ rows = plugin_engines.map do |name|
132
+ plugin = RosettAi::Plugins::Registry.plugin_module(:engine, name)
133
+ version = plugin.respond_to?(:version) ? plugin.version : '?'
134
+ display = plugin.respond_to?(:display_name) ? plugin.display_name : name
135
+ [name, display, version]
136
+ end
137
+
138
+ table = ::Terminal::Table.new(
139
+ title: Rainbow(t('plugins_title')).bright,
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
+ private
148
+
149
+ def t(key, **)
150
+ ::I18n.t("rosett_ai.cli.engines.#{key}", **)
151
+ end
152
+
153
+ def build_detector(name)
154
+ manifest = RosettAi::Engines::Registry.manifest(name)
155
+ engine_mod = RosettAi::Engines::Registry.engine_module(name)
156
+ detector_klass = engine_mod.detector_class
157
+
158
+ if manifest.dig('components', 'detector') && detector_klass
159
+ detector_klass.new(name)
160
+ else
161
+ RosettAi::Engines::Detector.new(name)
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end