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,535 @@
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
+ # Thor CLI for MCP server and administration.
12
+ #
13
+ # Provides serve, list, validate, status, audit, key management,
14
+ # and trust source subcommands for MCP server management.
15
+ #
16
+ # @author hugo
17
+ # @author claude
18
+ class Mcp < ::Thor
19
+ namespace 'mcp'
20
+
21
+ desc 'serve', 'Start Rosett-AI as an MCP server'
22
+ long_desc <<~LONGDESC
23
+ Start Rosett-AI as a Model Context Protocol server.
24
+
25
+ By default, uses stdio transport. With --http, starts an HTTP server
26
+ on the specified port (default 8484) with the full security middleware
27
+ stack (request size, content type, origin validation, CORS,
28
+ authentication, rate limiting).
29
+
30
+ Exit codes: 0 success, 5 MCP error.
31
+
32
+ EXAMPLES
33
+
34
+ raictl mcp serve
35
+ raictl mcp serve --http
36
+ raictl mcp serve --http --port 9090
37
+ raictl mcp serve --http --tls-cert cert.pem --tls-key key.pem
38
+ LONGDESC
39
+ method_option :http, type: :boolean, default: false, desc: 'Use HTTP transport instead of stdio'
40
+ method_option :port, type: :numeric, default: 8484, desc: 'HTTP port'
41
+ method_option :host, type: :string, default: 'localhost', desc: 'HTTP bind address'
42
+ method_option :tls_cert, type: :string, desc: 'Path to TLS certificate'
43
+ method_option :tls_key, type: :string, desc: 'Path to TLS private key'
44
+ method_option :allow_remote, type: :boolean, default: false, desc: 'Allow non-localhost binding'
45
+ method_option :config, type: :string, desc: 'Path to server config YAML'
46
+ method_option :plugin, type: :array, default: [], desc: 'Plugin names to load'
47
+ def serve
48
+ server = RosettAi::Mcp::Server.new(
49
+ config_path: options[:config],
50
+ plugins: options[:plugin]
51
+ )
52
+
53
+ if options[:http]
54
+ unless ENV.fetch('RAI_MCP_API_KEY', nil)
55
+ warn Rainbow('[rosett-ai-mcp] WARNING: Starting HTTP server with no API key configured. ' \
56
+ 'Set RAI_MCP_API_KEY or use a keyfile for authentication.').yellow
57
+ end
58
+ server.serve_http(
59
+ port: options[:port],
60
+ host: options[:host],
61
+ tls_cert: options[:tls_cert],
62
+ tls_key: options[:tls_key],
63
+ allow_remote: options[:allow_remote]
64
+ )
65
+ else
66
+ server.serve
67
+ end
68
+ rescue RosettAi::McpError => e
69
+ warn Rainbow(e.message).red
70
+ exit(5)
71
+ end
72
+
73
+ desc 'list', 'List configured MCP servers'
74
+ long_desc <<~LONGDESC
75
+ List all MCP servers registered in the rai configuration.
76
+
77
+ Displays name, transport type, and command/URL for each server.
78
+ Use --format json for machine-readable output.
79
+
80
+ EXAMPLES
81
+
82
+ raictl mcp list
83
+ raictl mcp list --format json
84
+
85
+ Related: mcp status, mcp validate
86
+ LONGDESC
87
+ method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
88
+ def list
89
+ registry = build_registry
90
+ servers = registry.list
91
+
92
+ if servers.empty?
93
+ say(t('no_servers'))
94
+ return
95
+ end
96
+
97
+ render_server_list(servers)
98
+ end
99
+
100
+ desc 'validate', 'Validate MCP server configurations'
101
+ long_desc <<~LONGDESC
102
+ Validate all MCP server configurations against the MCP schema.
103
+
104
+ Checks transport settings, command paths, and required fields.
105
+ Exit codes: 0 all valid, 2 validation errors found.
106
+
107
+ EXAMPLES
108
+
109
+ raictl mcp validate
110
+ raictl mcp validate --format json
111
+
112
+ Related: mcp list, mcp status
113
+ LONGDESC
114
+ method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
115
+ def validate
116
+ registry = build_registry
117
+ validator = RosettAi::Mcp::Admin::SchemaValidator.new
118
+ results = validator.validate_all(registry)
119
+
120
+ render_validation_results(results)
121
+ all_valid = results.all? { |r| r[:valid] }
122
+ exit(all_valid ? 0 : 2)
123
+ end
124
+
125
+ desc 'status', 'Show MCP server health status'
126
+ long_desc <<~LONGDESC
127
+ Check the health of all configured MCP servers.
128
+
129
+ Probes each server's transport endpoint and reports availability.
130
+ Use --format json for machine-readable output.
131
+
132
+ EXAMPLES
133
+
134
+ raictl mcp status
135
+ raictl mcp status --format json
136
+
137
+ Related: mcp list, mcp validate
138
+ LONGDESC
139
+ method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
140
+ def status
141
+ registry = build_registry
142
+ checker = RosettAi::Mcp::Admin::HealthChecker.new
143
+ results = checker.check_all(registry)
144
+
145
+ if results.empty?
146
+ say(t('no_servers'))
147
+ return
148
+ end
149
+
150
+ render_health_results(results)
151
+ end
152
+
153
+ desc 'audit', 'Generate MCP compliance audit report'
154
+ long_desc <<~LONGDESC
155
+ Generate a compliance audit report for all configured MCP servers.
156
+
157
+ Combines health checks and schema validation into a single report
158
+ with summary statistics. Default output is JSON for integration
159
+ with CI/CD pipelines.
160
+
161
+ EXAMPLES
162
+
163
+ raictl mcp audit
164
+ raictl mcp audit --format table
165
+
166
+ Related: mcp validate, mcp status
167
+ LONGDESC
168
+ method_option :format, type: :string, default: 'json', enum: ['table', 'json'], desc: 'Output format'
169
+ def audit
170
+ registry = build_registry
171
+ checker = RosettAi::Mcp::Admin::HealthChecker.new
172
+ validator = RosettAi::Mcp::Admin::SchemaValidator.new
173
+ auditor = RosettAi::Mcp::Admin::Auditor.new(
174
+ registry: registry, health_checker: checker, schema_validator: validator
175
+ )
176
+
177
+ report = auditor.audit
178
+ render_audit_report(report)
179
+ end
180
+
181
+ desc 'add NAME URI', 'Install an MCP server from a trusted source'
182
+ long_desc <<~LONGDESC
183
+ Install an MCP server from a URI. The domain must be in the trust
184
+ sources list. Use `mcp trust-add` to add domains first.
185
+
186
+ Example: raictl mcp add my-server npx:@example/mcp-server
187
+
188
+ Exit codes: 0 success, 1 untrusted source, 2 validation error.
189
+
190
+ Related: mcp remove, mcp trust-sources
191
+ LONGDESC
192
+ method_option :transport, type: :string, default: 'stdio',
193
+ enum: ['stdio', 'http', 'streamable-http'],
194
+ desc: 'Transport type'
195
+ def add(name, uri)
196
+ installer = build_installer
197
+ result = installer.install(name: name, uri: uri, transport: options[:transport])
198
+
199
+ if result[:success]
200
+ say(Rainbow(result[:message]).green)
201
+ else
202
+ warn(Rainbow(result[:message]).red)
203
+ exit(result[:trusted] == false ? 1 : 2)
204
+ end
205
+ end
206
+
207
+ desc 'remove NAME', 'Remove a configured MCP server'
208
+ long_desc <<~LONGDESC
209
+ Remove a configured MCP server by name.
210
+
211
+ User-installed servers are removed directly. Managed servers
212
+ (from content packs) require --force.
213
+
214
+ EXAMPLES
215
+
216
+ raictl mcp remove my-server
217
+ raictl mcp remove managed-server --force
218
+
219
+ Related: mcp add, mcp list
220
+ LONGDESC
221
+ method_option :force, type: :boolean, default: false, desc: 'Force removal of managed servers'
222
+ def remove(name)
223
+ installer = build_installer
224
+ result = installer.remove(name: name, force: options[:force])
225
+
226
+ if result[:success]
227
+ say(Rainbow(result[:message]).green)
228
+ else
229
+ warn(Rainbow(result[:message]).red)
230
+ exit(1)
231
+ end
232
+ end
233
+
234
+ desc 'trust-sources', 'List configured trust sources'
235
+ long_desc <<~LONGDESC
236
+ List all configured trust sources for MCP server installation.
237
+
238
+ Shows built-in domains (e.g., modelcontextprotocol.io) and
239
+ user-added domains. Servers can only be installed from trusted sources.
240
+
241
+ EXAMPLES
242
+
243
+ raictl mcp trust-sources
244
+ raictl mcp trust-sources --format json
245
+
246
+ Related: mcp trust-add, mcp trust-remove
247
+ LONGDESC
248
+ method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format'
249
+ map 'trust-sources' => :trust_sources
250
+ def trust_sources
251
+ manager = build_trust_manager
252
+ sources = manager.list
253
+
254
+ render_trust_sources(sources)
255
+ end
256
+
257
+ desc 'trust-add DOMAIN', 'Add a trusted domain for MCP server installation'
258
+ long_desc <<~LONGDESC
259
+ Add a domain to the user trust sources list.
260
+
261
+ Once trusted, MCP servers from this domain can be installed with
262
+ `mcp add`. Use --description to document why the domain is trusted.
263
+
264
+ Example: raictl mcp trust-add example.com --description "Internal MCP server"
265
+
266
+ Related: mcp trust-remove, mcp trust-sources
267
+ LONGDESC
268
+ method_option :description, type: :string, default: 'User-trusted domain', desc: 'Trust source description'
269
+ map 'trust-add' => :trust_add
270
+ def trust_add(domain)
271
+ manager = build_trust_manager
272
+ manager.add_trust(domain, description: options[:description])
273
+ say(Rainbow("Trust added for: #{domain}").green)
274
+ end
275
+
276
+ desc 'trust-remove DOMAIN', 'Remove a trusted domain'
277
+ long_desc <<~LONGDESC
278
+ Remove a domain from the user trust sources list.
279
+
280
+ Built-in trust sources cannot be removed. Only user-added domains
281
+ can be removed.
282
+
283
+ EXAMPLES
284
+
285
+ raictl mcp trust-remove example.com
286
+
287
+ Related: mcp trust-add, mcp trust-sources
288
+ LONGDESC
289
+ map 'trust-remove' => :trust_remove
290
+ def trust_remove(domain)
291
+ manager = build_trust_manager
292
+ if manager.remove_trust(domain)
293
+ say(Rainbow("Trust removed for: #{domain}").green)
294
+ else
295
+ warn(Rainbow("Domain not found in user trust sources: #{domain}").red)
296
+ exit(1)
297
+ end
298
+ end
299
+
300
+ desc 'tools', 'List available MCP tools'
301
+ long_desc <<~LONGDESC
302
+ List all tools exposed by the Rosett-AI MCP server.
303
+
304
+ Shows tool name, description, and read-only hint for each tool.
305
+ These are the tools available to AI engines via the MCP protocol.
306
+
307
+ EXAMPLES
308
+
309
+ raictl mcp tools
310
+ LONGDESC
311
+ def tools
312
+ server = RosettAi::Mcp::Server.new
313
+ tool_list = server.tools
314
+
315
+ table = ::Terminal::Table.new(
316
+ headings: ['Tool', 'Description', 'Read-Only'],
317
+ rows: tool_list.map { |t| [t[:name], t[:description], t[:annotations]['readOnlyHint']] }
318
+ )
319
+ say(table)
320
+ end
321
+
322
+ desc 'generate-key', 'Generate a new MCP API key'
323
+ long_desc <<~LONGDESC
324
+ Generate a new API key for MCP server authentication.
325
+
326
+ The key is printed to stdout. The salted hash is stored in the
327
+ keyfile. Use --name to identify the key.
328
+
329
+ EXAMPLES
330
+
331
+ raictl mcp generate-key --name claude-code
332
+ raictl mcp generate-key --name ci-runner --keyfile /etc/rosett-ai/mcp/keys.yml
333
+ LONGDESC
334
+ method_option :name, type: :string, required: true, desc: 'Key name'
335
+ method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
336
+ map 'generate-key' => :generate_key
337
+ def generate_key
338
+ path = RosettAi::Mcp::Keyfile.create_if_missing(options[:keyfile])
339
+ plaintext = RosettAi::Mcp::KeyHasher.generate_key
340
+ key_hash = RosettAi::Mcp::KeyHasher.hash_key(plaintext)
341
+
342
+ keyfile = RosettAi::Mcp::Keyfile.new(path)
343
+ keyfile.load!
344
+ keyfile.add_key(name: options[:name], key_hash: key_hash)
345
+
346
+ say(Rainbow("API key generated for '#{options[:name]}':").green)
347
+ say(plaintext)
348
+ say(Rainbow('Store this key securely — it cannot be recovered.').yellow)
349
+ end
350
+
351
+ desc 'list-keys', 'List all MCP API keys'
352
+ long_desc <<~LONGDESC
353
+ List all API keys in the keyfile.
354
+
355
+ Shows key name, creation date, enabled status, and expiry.
356
+ Key hashes are never displayed.
357
+
358
+ EXAMPLES
359
+
360
+ raictl mcp list-keys
361
+ raictl mcp list-keys --keyfile /etc/rosett-ai/mcp/keys.yml
362
+ LONGDESC
363
+ method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
364
+ map 'list-keys' => :list_keys
365
+ def list_keys
366
+ path = File.expand_path(options[:keyfile])
367
+ unless File.exist?(path)
368
+ say(Rainbow('No keyfile found.').yellow)
369
+ return
370
+ end
371
+
372
+ keyfile = RosettAi::Mcp::Keyfile.new(path)
373
+ keyfile.load!
374
+ keys = keyfile.enabled_keys
375
+
376
+ if keys.empty?
377
+ say('No keys configured.')
378
+ return
379
+ end
380
+
381
+ table = ::Terminal::Table.new(
382
+ headings: ['Name', 'Created', 'Enabled', 'Expires'],
383
+ rows: keys.map { |k| [k[:name], k[:created_at], k[:enabled], k[:expires_at] || 'never'] }
384
+ )
385
+ say(table)
386
+ end
387
+
388
+ desc 'revoke-key', 'Revoke an MCP API key'
389
+ long_desc <<~LONGDESC
390
+ Disable an API key by name.
391
+
392
+ The key remains in the keyfile but is marked as disabled.
393
+ Revoked keys cannot be used for authentication.
394
+
395
+ EXAMPLES
396
+
397
+ raictl mcp revoke-key --name old-key
398
+ LONGDESC
399
+ method_option :name, type: :string, required: true, desc: 'Key name to revoke'
400
+ method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
401
+ map 'revoke-key' => :revoke_key
402
+ def revoke_key
403
+ keyfile = RosettAi::Mcp::Keyfile.new(File.expand_path(options[:keyfile]))
404
+ keyfile.load!
405
+
406
+ if keyfile.revoke_key(options[:name])
407
+ say(Rainbow("Key '#{options[:name]}' revoked.").green)
408
+ else
409
+ warn(Rainbow("Key '#{options[:name]}' not found.").red)
410
+ exit(1)
411
+ end
412
+ end
413
+
414
+ desc 'rotate-key', 'Rotate an MCP API key'
415
+ long_desc <<~LONGDESC
416
+ Revoke an existing key and generate a replacement.
417
+
418
+ The old key is disabled and a new key with the same name is
419
+ created. The new plaintext key is printed to stdout.
420
+
421
+ EXAMPLES
422
+
423
+ raictl mcp rotate-key --name claude-code
424
+ LONGDESC
425
+ method_option :name, type: :string, required: true, desc: 'Key name to rotate'
426
+ method_option :keyfile, type: :string, default: RosettAi::Mcp::Keyfile::DEFAULT_FILENAME, desc: 'Keyfile path'
427
+ map 'rotate-key' => :rotate_key
428
+ def rotate_key
429
+ keyfile = RosettAi::Mcp::Keyfile.new(File.expand_path(options[:keyfile]))
430
+ keyfile.load!
431
+
432
+ result = keyfile.rotate_key(options[:name])
433
+ if result
434
+ say(Rainbow("Key '#{options[:name]}' rotated. New key:").green)
435
+ say(result[:plaintext])
436
+ say(Rainbow('Store this key securely — it cannot be recovered.').yellow)
437
+ else
438
+ warn(Rainbow("Key '#{options[:name]}' not found.").red)
439
+ exit(1)
440
+ end
441
+ end
442
+
443
+ private
444
+
445
+ def t(key, **args)
446
+ ::I18n.t("rosett_ai.mcp.#{key}", **args)
447
+ end
448
+
449
+ def build_registry
450
+ RosettAi::Mcp::Admin::Registry.new
451
+ end
452
+
453
+ def build_trust_manager
454
+ RosettAi::Mcp::Settings::TrustManager.new
455
+ end
456
+
457
+ def build_installer
458
+ RosettAi::Mcp::Settings::ServerInstaller.new(
459
+ trust_manager: build_trust_manager,
460
+ registry: build_registry,
461
+ schema_validator: RosettAi::Mcp::Admin::SchemaValidator.new
462
+ )
463
+ end
464
+
465
+ def render_trust_sources(sources)
466
+ if options[:format] == 'json'
467
+ say(JSON.pretty_generate(sources))
468
+ return
469
+ end
470
+
471
+ table = ::Terminal::Table.new(
472
+ headings: ['Domain', 'Type', 'Description'],
473
+ rows: sources.map { |s| [s[:domain], s[:type], s[:description]] }
474
+ )
475
+ say(table)
476
+ end
477
+
478
+ def render_server_list(servers)
479
+ if options[:format] == 'json'
480
+ say(JSON.pretty_generate(servers))
481
+ return
482
+ end
483
+
484
+ table = ::Terminal::Table.new(
485
+ headings: ['Name', 'Transport', 'Command/URL'],
486
+ rows: servers.map { |s| [s[:name], s[:transport], s[:command] || s[:url]] }
487
+ )
488
+ say(table)
489
+ end
490
+
491
+ def render_validation_results(results)
492
+ if options[:format] == 'json'
493
+ say(JSON.pretty_generate(results))
494
+ return
495
+ end
496
+
497
+ results.each do |r|
498
+ status = r[:valid] ? Rainbow('valid').green : Rainbow('invalid').red
499
+ say(" #{r[:name]}: #{status}")
500
+ r[:errors]&.each { |e| say(Rainbow(" #{e}").red) }
501
+ end
502
+ end
503
+
504
+ def render_health_results(results)
505
+ if options[:format] == 'json'
506
+ say(JSON.pretty_generate(results))
507
+ return
508
+ end
509
+
510
+ table = ::Terminal::Table.new(
511
+ headings: ['Name', 'Transport', 'Status', 'Message'],
512
+ rows: results.map do |r|
513
+ status = r[:status] == :available ? Rainbow('available').green : Rainbow(r[:status].to_s).red
514
+ [r[:name], r[:transport], status, r[:message]]
515
+ end
516
+ )
517
+ say(table)
518
+ end
519
+
520
+ def render_audit_report(report)
521
+ if options[:format] == 'json'
522
+ say(JSON.pretty_generate(report))
523
+ return
524
+ end
525
+
526
+ say("Audit timestamp: #{report[:timestamp]}")
527
+ say("Servers: #{report[:summary][:total]}")
528
+ say("Healthy: #{report[:summary][:healthy]}")
529
+ say("Schema valid: #{report[:summary][:schema_valid]}")
530
+ say("Secure transport: #{report[:summary][:secure_transport]}")
531
+ end
532
+ end
533
+ end
534
+ end
535
+ end
@@ -0,0 +1,121 @@
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 'rainbow'
7
+
8
+ module RosettAi
9
+ module Thor
10
+ module Tasks
11
+ # CLI task for `raictl migrate` — legacy namespace migration.
12
+ #
13
+ # Handles migration of:
14
+ # - ~/.config/nncc/ → ~/.config/rosett-ai/ (XDG global config)
15
+ # - .nncc/ → .rosett-ai/ (per-project marker directories)
16
+ #
17
+ # @author hugo
18
+ # @author claude
19
+ class Migrate < ::Thor
20
+ default_task :run_migrate
21
+
22
+ desc 'run_migrate', 'Migrate legacy .nncc and ~/.config/nncc paths'
23
+ long_desc <<~LONGDESC
24
+ Migrates legacy nncc namespace artifacts to rosett-ai:
25
+
26
+ - ~/.config/nncc/ → ~/.config/rosett-ai/ (global XDG config)
27
+ - .nncc/ → .rosett-ai/ (project marker in current directory)
28
+
29
+ With --workspace, scans the given path recursively for projects
30
+ containing .nncc/ directories and migrates them all.
31
+
32
+ EXAMPLES
33
+
34
+ raictl migrate
35
+ raictl migrate --workspace ~/git
36
+ LONGDESC
37
+ option :workspace, type: :string, desc: 'Scan and migrate all .nncc/ dirs under PATH'
38
+ option :dry_run, type: :boolean, default: false, desc: 'Show what would be migrated'
39
+ def run_migrate
40
+ migrate_xdg_config
41
+ migrate_projects
42
+ end
43
+
44
+ private
45
+
46
+ def migrate_xdg_config
47
+ migrator = RosettAi::Migration::NnccConfigMigrator.new
48
+ return report_no_config unless migrator.migration_needed?
49
+ return show_config_plan(migrator) if options[:dry_run]
50
+
51
+ result = migrator.migrate!
52
+ puts Rainbow("Migrated #{result.size} file(s) from ~/.config/nncc/").green
53
+ result.each { |file| puts " #{file}" }
54
+ end
55
+
56
+ def report_no_config
57
+ puts Rainbow('~/.config/nncc/ not found — nothing to migrate').green
58
+ end
59
+
60
+ def migrate_projects
61
+ migrator = RosettAi::Migration::NnccProjectMigrator.new
62
+
63
+ if options[:workspace]
64
+ migrate_workspace(migrator)
65
+ else
66
+ migrate_current_project(migrator)
67
+ end
68
+ end
69
+
70
+ def migrate_workspace(migrator)
71
+ workspace = options[:workspace]
72
+ return show_workspace_plan(migrator, workspace) if options[:dry_run]
73
+
74
+ migrator.migrate_workspace(workspace)
75
+ show_project_results(migrator.results)
76
+ end
77
+
78
+ def show_workspace_plan(migrator, workspace)
79
+ projects = migrator.detect(workspace)
80
+ if projects.empty?
81
+ puts Rainbow("No .nncc/ directories found under #{workspace}").green
82
+ else
83
+ puts "Found #{projects.size} project(s) with .nncc/:"
84
+ projects.each { |project| puts " #{project}" }
85
+ end
86
+ end
87
+
88
+ def migrate_current_project(migrator)
89
+ pwd = Dir.pwd
90
+ return show_current_plan(pwd) if options[:dry_run]
91
+
92
+ migrator.migrate_project(pwd)
93
+ show_project_results(migrator.results)
94
+ end
95
+
96
+ def show_current_plan(pwd)
97
+ nncc = Pathname.new(pwd).join('.nncc')
98
+ if nncc.directory?
99
+ puts "Would migrate: #{nncc} → #{pwd}/.rosett-ai/"
100
+ else
101
+ puts Rainbow('No .nncc/ in current directory').green
102
+ end
103
+ end
104
+
105
+ def show_config_plan(migrator)
106
+ plan = migrator.plan
107
+ puts "Would migrate #{plan.size} file(s) from ~/.config/nncc/:"
108
+ plan.each { |file| puts " #{file[:path]} (#{file[:size]} bytes)" }
109
+ end
110
+
111
+ def show_project_results(results)
112
+ color_map = { migrated: :green, skipped: :yellow }
113
+ results.each do |entry|
114
+ color = color_map.fetch(entry[:status], :red)
115
+ puts Rainbow(" #{entry[:path]}: #{entry[:message]}").send(color)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end