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,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 'json'
7
+ require 'thor'
8
+ require 'rainbow'
9
+ require 'terminal-table'
10
+
11
+ module RosettAi
12
+ module Thor
13
+ module Tasks
14
+ # Thor task for D-Bus service management.
15
+ #
16
+ # Exit codes:
17
+ # 0 — success
18
+ # 1 — D-Bus connection failure
19
+ # 5 — missing ruby-dbus dependency
20
+ #
21
+ # @author hugo
22
+ # @author claude
23
+ class DBus < ::Thor
24
+ EXIT_SUCCESS = 0
25
+ EXIT_DBUS_FAILURE = 1
26
+ EXIT_MISSING_DEPENDENCY = 5
27
+
28
+ ENV_VARS = [
29
+ 'SWAYSOCK', 'I3SOCK', 'HYPRLAND_INSTANCE_SIGNATURE', 'DISPLAY', 'WAYLAND_DISPLAY', 'XDG_RUNTIME_DIR', 'XDG_SESSION_TYPE'
30
+ ].freeze
31
+
32
+ ADAPTER_MAP = {
33
+ sway: 'I3Adapter (i3ipc)',
34
+ i3: 'I3Adapter (i3ipc)',
35
+ hyprland: 'HyprlandAdapter (socket2)',
36
+ gnome: 'GnomeAdapter (Shell D-Bus)',
37
+ kwin: 'KwinAdapter (KWin D-Bus)',
38
+ x11: 'X11Adapter (xprop)'
39
+ }.freeze
40
+
41
+ desc 'status', 'Show D-Bus service status'
42
+ long_desc <<~LONGDESC
43
+ Checks if the be.neatnerds.rosettai D-Bus service is running and displays status.
44
+ Output format depends on TTY detection (table or JSON).
45
+
46
+ Exit codes:
47
+ 0: success
48
+ 1: D-Bus connection failure
49
+ 5: missing ruby-dbus gem
50
+
51
+ EXAMPLES
52
+
53
+ raictl dbus status
54
+
55
+ Related commands: raictl dbus start, rai dbus detect
56
+ LONGDESC
57
+ def status
58
+ require_desktop_gems!
59
+ check_dbus_availability
60
+
61
+ if service_running?
62
+ print_running_status
63
+ else
64
+ print_not_running
65
+ end
66
+ end
67
+
68
+ desc 'start', 'Start the D-Bus service (foreground)'
69
+ long_desc <<~LONGDESC
70
+ Starts the be.neatnerds.rosettai D-Bus service for focus tracking and IPC.
71
+ By default runs in foreground. Use --daemonize to run as background daemon.
72
+
73
+ Flags:
74
+ --daemonize, -d: Run as daemon (background)
75
+
76
+ Exit codes:
77
+ 0: success
78
+ 1: D-Bus connection failure or service already running
79
+ 5: missing ruby-dbus gem
80
+
81
+ EXAMPLES
82
+
83
+ raictl dbus start
84
+ raictl dbus start --daemonize
85
+
86
+ Related commands: raictl dbus status, rai dbus stop
87
+ LONGDESC
88
+ method_option :daemonize,
89
+ type: :boolean,
90
+ default: false,
91
+ aliases: '-d',
92
+ desc: 'Run as daemon (background)'
93
+ def start
94
+ require_desktop_gems!
95
+ check_dbus_availability
96
+
97
+ if service_running?
98
+ warn Rainbow(t('already_running')).yellow
99
+ return
100
+ end
101
+
102
+ if options[:daemonize]
103
+ start_daemon
104
+ else
105
+ start_foreground
106
+ end
107
+ end
108
+
109
+ desc 'stop', 'Stop the D-Bus service'
110
+ long_desc <<~LONGDESC
111
+ Stops the running be.neatnerds.rosettai D-Bus service via D-Bus shutdown signal.
112
+
113
+ Exit codes:
114
+ 0: success
115
+ 1: D-Bus connection failure
116
+ 5: missing ruby-dbus gem
117
+
118
+ EXAMPLES
119
+
120
+ raictl dbus stop
121
+
122
+ Related commands: raictl dbus start, rai dbus status
123
+ LONGDESC
124
+ def stop
125
+ require_desktop_gems!
126
+ unless service_running?
127
+ warn Rainbow(t('not_running')).yellow
128
+ return
129
+ end
130
+
131
+ stop_via_dbus
132
+ warn Rainbow(t('stopped')).green
133
+ end
134
+
135
+ desc 'detect', 'Detect compositor and show focus adapter'
136
+ long_desc <<~LONGDESC
137
+ Detects the running compositor (Sway, i3, Hyprland, GNOME, KWin, X11) and displays
138
+ the corresponding focus tracking adapter. Shows environment variables used for detection.
139
+
140
+ Output format depends on TTY detection (table or JSON).
141
+
142
+ EXAMPLES
143
+
144
+ raictl dbus detect
145
+
146
+ Related commands: raictl dbus status, rai dbus start
147
+ LONGDESC
148
+ def detect
149
+ require_desktop_gems!
150
+ compositor = RosettAi::DBus::CompositorDetector.detect
151
+
152
+ if tty_output?
153
+ print_detect_tty(compositor)
154
+ else
155
+ print_detect_json(compositor)
156
+ end
157
+ end
158
+
159
+ private
160
+
161
+ def t(key, **)
162
+ ::I18n.t("rosett_ai.cli.dbus.#{key}", **)
163
+ end
164
+
165
+ def tty_output?
166
+ $stdout.tty?
167
+ end
168
+
169
+ def require_desktop_gems!
170
+ require 'dbus'
171
+ rescue LoadError
172
+ warn Rainbow(::I18n.t('rosett_ai.cli.dbus.missing_dependency',
173
+ default: 'Desktop features require the ruby-dbus gem. ' \
174
+ 'Install with: apt install rosett-ai-gtk4')).red
175
+ exit EXIT_MISSING_DEPENDENCY
176
+ end
177
+
178
+ def check_dbus_availability
179
+ return if RosettAi::DBus::Service.dbus_available?
180
+
181
+ warn Rainbow(t('bus_unavailable')).red
182
+ warn t('bus_unavailable_hint')
183
+ exit EXIT_DBUS_FAILURE
184
+ end
185
+
186
+ def service_running?
187
+ bus = ::DBus::SessionBus.instance
188
+ bus.service(RosettAi::DBus::Service::BUS_NAME).exists?
189
+ rescue ::DBus::Error
190
+ false
191
+ end
192
+
193
+ def print_not_running
194
+ if tty_output?
195
+ warn Rainbow(t('not_running')).yellow
196
+ warn t('start_hint', command: 'bin/raictl dbus start')
197
+ else
198
+ puts JSON.generate(running: false)
199
+ end
200
+ end
201
+
202
+ # -- TTY vs JSON branching
203
+ def print_running_status
204
+ status_data = fetch_service_status
205
+ return unless status_data
206
+
207
+ if tty_output?
208
+ print_status_table(status_data)
209
+ else
210
+ puts JSON.generate(status_data)
211
+ end
212
+ end
213
+
214
+ def fetch_service_status
215
+ bus = ::DBus::SessionBus.instance
216
+ service = bus.service(RosettAi::DBus::Service::BUS_NAME)
217
+ object = service.object(RosettAi::DBus::Service::OBJECT_PATH)
218
+ object.introspect
219
+ object.default_iface = 'be.neatnerds.rosettai.Manager'
220
+ object.GetStatus.first
221
+ rescue ::DBus::Error => e
222
+ warn Rainbow(t('error_querying', message: e.message)).red
223
+ nil
224
+ end
225
+
226
+ def print_status_table(status_data)
227
+ puts Rainbow(t('status_title')).bright
228
+ puts
229
+
230
+ rows = status_data.map { |k, v| [k, v.to_s] }
231
+ table = ::Terminal::Table.new(
232
+ headings: [t('property'), t('value')],
233
+ rows: rows,
234
+ style: { border: :unicode_round }
235
+ )
236
+ puts table
237
+ end
238
+
239
+ def stop_via_dbus
240
+ bus = ::DBus::SessionBus.instance
241
+ service = bus.service(RosettAi::DBus::Service::BUS_NAME)
242
+ object = service.object(RosettAi::DBus::Service::OBJECT_PATH)
243
+ object.introspect
244
+ object.default_iface = 'be.neatnerds.rosettai.Manager'
245
+ object.Shutdown
246
+ rescue ::DBus::Error => e
247
+ warn Rainbow("Error stopping service: #{e.message}").red
248
+ exit EXIT_DBUS_FAILURE
249
+ end
250
+
251
+ def start_foreground
252
+ warn Rainbow(t('starting')).cyan
253
+ warn t('press_ctrl_c')
254
+ $stderr.puts
255
+
256
+ service = RosettAi::DBus::Service.new
257
+ unless service.start
258
+ warn Rainbow(t('start_failed')).red
259
+ exit EXIT_DBUS_FAILURE
260
+ end
261
+
262
+ service.run
263
+ end
264
+
265
+ def start_daemon
266
+ pid = fork do
267
+ Process.daemon(true, false)
268
+ service = RosettAi::DBus::Service.new
269
+ service.start && service.run
270
+ end
271
+ Process.detach(pid)
272
+ warn Rainbow(t('started_background', pid: pid)).green
273
+ end
274
+
275
+ def adapter_for(compositor)
276
+ ADAPTER_MAP.fetch(compositor, nil)
277
+ end
278
+
279
+ def print_detect_tty(compositor)
280
+ adapter_name = adapter_for(compositor)
281
+
282
+ puts Rainbow(t('detect_title')).bright
283
+ puts
284
+ puts " #{t('detected')}: #{Rainbow(compositor.to_s).cyan}"
285
+ puts " #{t('adapter')}: #{adapter_name ? Rainbow(adapter_name).green : Rainbow(t('adapter_none')).yellow}"
286
+ puts
287
+
288
+ print_environment_table
289
+ end
290
+
291
+ def print_detect_json(compositor)
292
+ env_data = ENV_VARS.to_h do |var|
293
+ [var, ENV.fetch(var, nil)]
294
+ end
295
+
296
+ puts JSON.generate(
297
+ compositor: compositor.to_s,
298
+ adapter: adapter_for(compositor),
299
+ environment: env_data
300
+ )
301
+ end
302
+
303
+ def print_environment_table
304
+ rows = ENV_VARS.map do |var|
305
+ value = ENV.fetch(var, nil)
306
+ [var, value ? Rainbow(value).cyan : Rainbow(t('not_set')).faint]
307
+ end
308
+
309
+ puts Rainbow(t('env_title')).bright
310
+ puts
311
+ table = ::Terminal::Table.new(
312
+ headings: [t('variable'), t('value')],
313
+ rows: rows,
314
+ style: { border: :unicode_round }
315
+ )
316
+ puts table
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,258 @@
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 design document management
14
+ class Design < ::Thor
15
+ desc 'validate [NAME]', 'Validate design document(s) against the design schema'
16
+ long_desc <<~LONGDESC
17
+ Validates design document(s) against JSON schema. If NAME is provided, validates
18
+ only that document; otherwise validates all design documents.
19
+
20
+ Exit codes: 1 if any validation fails
21
+
22
+ EXAMPLES
23
+
24
+ raictl design validate
25
+ raictl design validate architecture
26
+
27
+ Related commands: raictl design list, rai design show
28
+ LONGDESC
29
+ def validate(name = nil)
30
+ name = RosettAi::TextSanitizer.normalize_nfc(name) if name
31
+ files = files_to_validate(name)
32
+ return puts Rainbow(t('no_documents')).yellow if files.empty?
33
+
34
+ all_valid = validate_files(files)
35
+ raise ::Thor::Error, t('validation_failed') unless all_valid
36
+ end
37
+
38
+ desc 'show NAME', 'Display a design document summary'
39
+ long_desc <<~LONGDESC
40
+ Displays formatted design document with metadata, intent, constraints, and acceptance criteria.
41
+ By default shows essential sections only. Use --full to include examples and preferences.
42
+
43
+ Flags:
44
+ --full: Show all sections including examples, anti-patterns, and preferences
45
+
46
+ EXAMPLES
47
+
48
+ raictl design show architecture
49
+ raictl design show security --full
50
+
51
+ Related commands: raictl design list, rai design validate
52
+ LONGDESC
53
+ method_option :full, type: :boolean, default: false,
54
+ desc: 'Show all sections including examples and preferences'
55
+
56
+ def show(name)
57
+ name = RosettAi::TextSanitizer.normalize_nfc(name)
58
+ file_path = find_design_file(name)
59
+ raise ::Thor::Error, Rainbow(t('not_found', name: name)).red unless file_path
60
+
61
+ data = RosettAi::YamlLoader.load_file(file_path)
62
+ raise ::Thor::Error, Rainbow(t('empty_file', path: file_path)).red if data.nil?
63
+
64
+ sanitized = RosettAi::TextSanitizer.sanitize_for_display(data)
65
+
66
+ print_design_header(sanitized)
67
+ print_section(t('intent'), sanitized['intent'])
68
+ print_numbered_section(t('constraints'), sanitized['constraints'])
69
+ print_numbered_section(t('acceptance_criteria'), sanitized['acceptance_criteria'])
70
+ print_dependencies(sanitized['depends_on'])
71
+
72
+ return unless options[:full]
73
+
74
+ print_examples(sanitized['examples'])
75
+ print_numbered_section(t('anti_patterns'), sanitized['anti_patterns'])
76
+ print_preferences(sanitized['preferences'])
77
+ end
78
+
79
+ desc 'list', 'List all design documents'
80
+ long_desc <<~LONGDESC
81
+ Lists all design documents in a table with name, version, status, domain, and priority.
82
+ Results are sorted by priority then name.
83
+
84
+ EXAMPLES
85
+
86
+ raictl design list
87
+
88
+ Related commands: raictl design show, rai design validate
89
+ LONGDESC
90
+ def list
91
+ files = design_files
92
+ if files.empty?
93
+ puts Rainbow(t('no_documents_found')).yellow
94
+ return
95
+ end
96
+
97
+ print_design_list(files)
98
+ end
99
+
100
+ private
101
+
102
+ def t(key, **)
103
+ ::I18n.t("rosett_ai.cli.design.#{key}", **)
104
+ end
105
+
106
+ def design_path
107
+ RosettAi.conf_root.join('conf', 'design')
108
+ end
109
+
110
+ def design_files
111
+ Dir.glob(design_path.join('*.yml'))
112
+ end
113
+
114
+ def find_design_file(name)
115
+ path = design_path.join("#{name}.yml")
116
+ path.exist? ? path.to_s : nil
117
+ end
118
+
119
+ def files_to_validate(name)
120
+ return design_files unless name
121
+
122
+ file = find_design_file(name)
123
+ unless file
124
+ puts Rainbow(t('not_found', name: name)).red
125
+ return []
126
+ end
127
+ [file]
128
+ end
129
+
130
+ def validate_files(files)
131
+ validator = RosettAi::Validators::DesignValidator.new
132
+ files.all? { |file| file_valid?(validator, file) }
133
+ end
134
+
135
+ def file_valid?(validator, file)
136
+ basename = File.basename(file)
137
+ if validator.valid?(file)
138
+ puts "#{Rainbow(t('valid')).green} #{basename}"
139
+ true
140
+ else
141
+ display_validation_errors(basename, validator.errors)
142
+ false
143
+ end
144
+ end
145
+
146
+ def display_validation_errors(basename, errors)
147
+ puts "#{Rainbow(t('invalid')).red} #{basename}"
148
+ errors.each { |err| puts " #{err}" }
149
+ end
150
+
151
+ def print_design_header(data)
152
+ rows = [
153
+ [t('name'), data['name']],
154
+ [t('design_version'), data['version']],
155
+ [t('status'), format_status(data['status'])],
156
+ [t('domain'), data['domain']],
157
+ [t('priority'), data['priority']],
158
+ ['Author', data['author']],
159
+ ['Created', data['created_at']],
160
+ ['Modified', data['modified_at']],
161
+ ['Modified By', data['modified_by']]
162
+ ]
163
+
164
+ table = ::Terminal::Table.new(
165
+ title: Rainbow(data['name'] || 'Design Document').bright,
166
+ rows: rows,
167
+ style: { border: :unicode_round }
168
+ )
169
+ puts table
170
+ puts
171
+ end
172
+
173
+ def print_section(title, content)
174
+ return unless content
175
+
176
+ puts Rainbow(title).bright.underline
177
+ puts content.to_s.strip
178
+ puts
179
+ end
180
+
181
+ def print_numbered_section(title, items)
182
+ return unless items.is_a?(Array) && !items.empty?
183
+
184
+ puts Rainbow(title).bright.underline
185
+ items.each_with_index do |item, index|
186
+ puts " #{index + 1}. #{item}"
187
+ end
188
+ puts
189
+ end
190
+
191
+ def print_dependencies(deps)
192
+ return unless deps.is_a?(Array) && !deps.empty?
193
+
194
+ puts Rainbow(t('dependencies')).bright.underline
195
+ deps.each { |dep| puts " - #{dep}" }
196
+ puts
197
+ end
198
+
199
+ def print_examples(examples)
200
+ return unless examples.is_a?(Array) && !examples.empty?
201
+
202
+ puts Rainbow(t('examples')).bright.underline
203
+ examples.each do |example|
204
+ puts " #{Rainbow(t('scenario')).cyan} #{example['scenario']}"
205
+ puts " #{Rainbow(t('expected')).green} #{example['expected']&.strip}"
206
+ puts " #{Rainbow(t('not_expected')).red} #{example['not']&.strip}" if example['not']
207
+ puts
208
+ end
209
+ end
210
+
211
+ def print_preferences(prefs)
212
+ return unless prefs.is_a?(Hash) && !prefs.empty?
213
+
214
+ puts Rainbow(t('preferences')).bright.underline
215
+ prefs.each do |key, value|
216
+ formatted = value.is_a?(Array) ? value.join(', ') : value.to_s
217
+ puts " #{key}: #{formatted}"
218
+ end
219
+ puts
220
+ end
221
+
222
+ def print_design_list(files)
223
+ rows = files.map { |file| build_design_list_row(file) }
224
+ rows.sort_by! { |row| [row[4].to_i, row[0].to_s] }
225
+
226
+ table = ::Terminal::Table.new(
227
+ title: Rainbow(t('title')).bright,
228
+ headings: [t('name'), t('design_version'), t('status'), t('domain'), t('priority')],
229
+ rows: rows,
230
+ style: { border: :unicode_round }
231
+ )
232
+ puts table
233
+ end
234
+
235
+ def build_design_list_row(file)
236
+ data = RosettAi::YamlLoader.load_file(file)
237
+ [
238
+ data['name'],
239
+ data['version'],
240
+ format_status(data['status']),
241
+ data['domain'],
242
+ data['priority']
243
+ ]
244
+ rescue StandardError
245
+ [File.basename(file, '.yml'), '-', Rainbow('error').red, '-', '-']
246
+ end
247
+
248
+ def format_status(status)
249
+ case status
250
+ when 'approved' then Rainbow(status).green
251
+ when 'draft' then Rainbow(status).yellow
252
+ else status.to_s
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ require 'json'
7
+ require 'thor'
8
+ require 'rainbow'
9
+
10
+ module RosettAi
11
+ module Thor
12
+ module Tasks
13
+ # Thor task for launching desktop applications.
14
+ #
15
+ # @author hugo
16
+ # @author claude
17
+ class Desktop < ::Thor
18
+ desc 'gtk4', 'Launch the GTK4/Adwaita desktop application'
19
+ long_desc <<~LONGDESC
20
+ Launches the GTK4/Adwaita desktop GUI for Rosett-AI. Requires rosett-ai-gtk4 package.
21
+
22
+ Exit codes:
23
+ 0: success
24
+ 1: GTK4 not available or missing dependencies
25
+
26
+ EXAMPLES
27
+
28
+ raictl desktop gtk4
29
+
30
+ Related commands: raictl desktop status, rai dbus start
31
+ LONGDESC
32
+ def gtk4
33
+ require_desktop_gems!
34
+ unless RosettAi::Desktop::Gtk4App.available?
35
+ msg = "#{t('unavailable')}\n#{t('install_adwaita')}\n#{t('install_pkg')}"
36
+ raise ::Thor::Error, msg
37
+ end
38
+
39
+ app = RosettAi::Desktop::Gtk4App.new
40
+ exit_code = app.run(ARGV)
41
+ exit exit_code
42
+ end
43
+
44
+ desc 'status', 'Show desktop integration status'
45
+ long_desc <<~LONGDESC
46
+ Shows availability status of GTK4 application and D-Bus service.
47
+ Output format depends on TTY detection (table or JSON).
48
+
49
+ EXAMPLES
50
+
51
+ raictl desktop status
52
+
53
+ Related commands: raictl desktop gtk4, rai dbus status
54
+ LONGDESC
55
+ def status
56
+ require_desktop_gems!
57
+ gtk4_available = RosettAi::Desktop::Gtk4App.available?
58
+ dbus_running = check_dbus_service
59
+
60
+ if tty_output?
61
+ print_status_tty(gtk4_available, dbus_running)
62
+ else
63
+ print_status_json(gtk4_available, dbus_running)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def t(key, **)
70
+ ::I18n.t("rosett_ai.cli.desktop.#{key}", **)
71
+ end
72
+
73
+ def tty_output?
74
+ $stdout.tty?
75
+ end
76
+
77
+ def require_desktop_gems!
78
+ require 'dbus'
79
+ rescue LoadError
80
+ raise ::Thor::Error, ::I18n.t('rosett_ai.cli.dbus.missing_dependency',
81
+ default: 'Desktop features require the ruby-dbus gem. ' \
82
+ 'Install with: apt install rosett-ai-gtk4')
83
+ end
84
+
85
+ def check_dbus_service
86
+ client = RosettAi::Desktop::DbusClient.new
87
+ client.service_available?
88
+ rescue StandardError
89
+ false
90
+ end
91
+
92
+ # -- user-facing display logic
93
+ def print_status_tty(gtk4_available, dbus_running)
94
+ puts Rainbow(t('status_title')).bright
95
+ puts
96
+
97
+ gtk4_label = gtk4_available ? t('available') : t('not_installed')
98
+ gtk4_color = gtk4_available ? :green : :yellow
99
+ puts " #{t('gtk4_label')}: #{Rainbow(gtk4_label).color(gtk4_color)}"
100
+
101
+ dbus_label = dbus_running ? t('running') : t('not_running')
102
+ dbus_color = dbus_running ? :green : :yellow
103
+ puts " #{t('dbus_label')}: #{Rainbow(dbus_label).color(dbus_color)}"
104
+ puts
105
+
106
+ unless gtk4_available
107
+ puts Rainbow(t('to_enable_gtk4')).cyan
108
+ puts " #{t('install_adwaita')}"
109
+ puts " #{t('install_pkg')}"
110
+ puts
111
+ end
112
+
113
+ return if dbus_running
114
+
115
+ puts Rainbow(t('to_start_dbus')).cyan
116
+ puts ' bin/raictl dbus start'
117
+ puts
118
+ end
119
+
120
+ def print_status_json(gtk4_available, dbus_running)
121
+ puts JSON.generate(
122
+ gtk4: { available: gtk4_available },
123
+ dbus: { running: dbus_running }
124
+ )
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end