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,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Mcp
8
+ module Resources
9
+ # MCP resource provider for AI provenance log.
10
+ #
11
+ # Exposes the full provenance log as an MCP resource.
12
+ #
13
+ # @author hugo
14
+ # @author claude
15
+ class ProvenanceResource
16
+ URI_PREFIX = 'rosett-ai://provenance/'
17
+ PROVENANCE_FILE = '.ai-provenance.yml'
18
+
19
+ # Lists available provenance resources.
20
+ #
21
+ # @return [Array<Hash>] resource entries
22
+ def list
23
+ path = RosettAi.root.join(PROVENANCE_FILE)
24
+ return [] unless path.exist?
25
+
26
+ [{
27
+ uri: "#{URI_PREFIX}log",
28
+ name: 'provenance-log',
29
+ description: 'Full AI provenance log',
30
+ mime_type: 'application/x-yaml'
31
+ }]
32
+ end
33
+
34
+ # Reads the provenance log.
35
+ #
36
+ # @param _name [String] resource name (only 'log' supported)
37
+ # @return [Hash, nil] resource content
38
+ def read(_name = 'log')
39
+ path = RosettAi.root.join(PROVENANCE_FILE)
40
+ return nil unless path.exist?
41
+
42
+ {
43
+ uri: "#{URI_PREFIX}log",
44
+ content: File.read(path),
45
+ mime_type: 'application/x-yaml'
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Mcp
8
+ module Resources
9
+ # MCP resource provider for compiled rule markdown files.
10
+ #
11
+ # Exposes compiled rules (output of raictl compile) as MCP
12
+ # resources with URIs in the format rosett-ai://rules/{name}.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class RulesResource
17
+ URI_PREFIX = 'rosett-ai://rules/'
18
+
19
+ # Lists all available compiled rule resources.
20
+ #
21
+ # @return [Array<Hash>] resource entries
22
+ def list
23
+ dir = RosettAi.paths.rules_dir
24
+ return [] unless dir.directory?
25
+
26
+ dir.glob('*.md').map { |path| resource_entry(path) }
27
+ end
28
+
29
+ # Reads a specific compiled rule.
30
+ #
31
+ # @param name [String] rule filename (without .md)
32
+ # @return [Hash, nil] resource content
33
+ def read(name)
34
+ path = RosettAi.paths.rules_dir.join("#{name}.md")
35
+ return nil unless path.exist?
36
+
37
+ {
38
+ uri: "#{URI_PREFIX}#{name}",
39
+ content: File.read(path),
40
+ mime_type: 'text/markdown'
41
+ }
42
+ end
43
+
44
+ private
45
+
46
+ # @param path [Pathname]
47
+ # @return [Hash]
48
+ def resource_entry(path)
49
+ name = path.basename('.md').to_s
50
+ {
51
+ uri: "#{URI_PREFIX}#{name}",
52
+ name: name,
53
+ description: "Compiled rule: #{name}",
54
+ mime_type: 'text/markdown'
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Mcp
8
+ module Resources
9
+ # MCP resource provider for JSON Schema files.
10
+ #
11
+ # Exposes validation schemas as MCP resources with
12
+ # URIs in the format rosett-ai://schema/{name}.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class SchemaResource
17
+ URI_PREFIX = 'rosett-ai://schema/'
18
+
19
+ # Lists all available schema resources.
20
+ #
21
+ # @return [Array<Hash>] resource entries
22
+ def list
23
+ dir = RosettAi.root.join('conf', 'schemas')
24
+ return [] unless dir.directory?
25
+
26
+ dir.glob('*.json').map { |path| resource_entry(path) }
27
+ end
28
+
29
+ # Reads a specific schema resource.
30
+ #
31
+ # @param name [String] schema name (without _schema.json)
32
+ # @return [Hash, nil] resource content
33
+ def read(name)
34
+ path = resolve_path(name)
35
+ return nil unless path&.exist?
36
+
37
+ {
38
+ uri: "#{URI_PREFIX}#{name}",
39
+ content: File.read(path),
40
+ mime_type: 'application/json'
41
+ }
42
+ end
43
+
44
+ private
45
+
46
+ # @param name [String]
47
+ # @return [Pathname, nil]
48
+ def resolve_path(name)
49
+ dir = RosettAi.root.join('conf', 'schemas')
50
+ # Try exact filename first, then with _schema.json suffix
51
+ exact = dir.join("#{name}.json")
52
+ return exact if exact.exist?
53
+
54
+ suffixed = dir.join("#{name}_schema.json")
55
+ suffixed.exist? ? suffixed : nil
56
+ end
57
+
58
+ # @param path [Pathname]
59
+ # @return [Hash]
60
+ def resource_entry(path)
61
+ name = path.basename('.json').to_s
62
+ {
63
+ uri: "#{URI_PREFIX}#{name}",
64
+ name: name,
65
+ description: "JSON Schema: #{name}",
66
+ mime_type: 'application/json'
67
+ }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Mcp
8
+ # Standardized MCP tool response formatting.
9
+ #
10
+ # Provides consistent response structure for all MCP tools
11
+ # with success, finding, and error states.
12
+ #
13
+ # @author hugo
14
+ # @author claude
15
+ module ResponseHelper
16
+ module_function
17
+
18
+ # Build a success response hash.
19
+ #
20
+ # @param message [String] human-readable message
21
+ # @param data [Hash] additional response data
22
+ # @return [Hash] response with success: true
23
+ def success(message, data = {})
24
+ { success: true, message: message }.merge(data)
25
+ end
26
+
27
+ # Build a finding response hash (not an error, but notable).
28
+ #
29
+ # @param message [String] human-readable message
30
+ # @param data [Hash] additional response data
31
+ # @return [Hash] response with finding: true
32
+ def finding(message, data = {})
33
+ { finding: true, message: message }.merge(data)
34
+ end
35
+
36
+ # Build an error response hash.
37
+ #
38
+ # @param message [String] human-readable error message
39
+ # @param data [Hash] additional response data
40
+ # @return [Hash] response with error: true
41
+ def error(message, data = {})
42
+ { error: true, message: message }.merge(data)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Mcp
8
+ # Security event logger for MCP server.
9
+ #
10
+ # Logs authentication, rate limiting, and origin validation
11
+ # events to stderr in structured format.
12
+ #
13
+ # @author hugo
14
+ # @author claude
15
+ module SecurityLogger
16
+ module_function
17
+
18
+ # Log a successful authentication event.
19
+ #
20
+ # @param client_id [String] authenticated client identifier
21
+ # @return [void]
22
+ def auth_success(client_id)
23
+ log(:info, 'auth_success', client_id: client_id)
24
+ end
25
+
26
+ # Log a failed authentication attempt.
27
+ #
28
+ # @param reason [String] failure reason
29
+ # @return [void]
30
+ def auth_failure(reason)
31
+ log(:warn, 'auth_failure', reason: reason)
32
+ end
33
+
34
+ # Log a rate-limited request.
35
+ #
36
+ # @param key [String] bucket key that was limited
37
+ # @return [void]
38
+ def rate_limited(key)
39
+ log(:warn, 'rate_limited', bucket_key: key)
40
+ end
41
+
42
+ # Log a rejected origin.
43
+ #
44
+ # @param origin [String] the rejected origin
45
+ # @return [void]
46
+ def origin_rejected(origin)
47
+ log(:warn, 'origin_rejected', origin: origin)
48
+ end
49
+
50
+ # @param level [Symbol] log level
51
+ # @param event [String] event name
52
+ # @param data [Hash] event data
53
+ # @return [void]
54
+ def log(level, event, **data)
55
+ timestamp = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
56
+ warn "[rai-mcp] #{timestamp} #{level} #{event} #{data.map { |k, v| "#{k}=#{v}" }.join(' ')}"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Mcp
8
+ # MCP server for Rosett-AI — exposes validation, compilation, and
9
+ # configuration management as MCP tools, resources, and prompts.
10
+ #
11
+ # Supports stdio (default) and HTTP (Puma) transports with a
12
+ # 6-layer Rack middleware stack for security.
13
+ #
14
+ # Requires the `mcp` gem (optional dependency, :mcp group).
15
+ #
16
+ # @author hugo
17
+ # @author claude
18
+ class Server
19
+ MCP_SPEC_VERSION = '2025-03-26'
20
+ DEFAULT_PORT = 8484
21
+ DEFAULT_HOST = 'localhost'
22
+
23
+ attr_reader :security_config
24
+
25
+ # @param config_path [String, nil] path to server config YAML
26
+ # @param plugins [Array<String>] explicit plugin names to load
27
+ def initialize(config_path: nil, plugins: [])
28
+ @config_path = config_path
29
+ @plugin_names = plugins
30
+ @security_config = load_security_config(config_path)
31
+ end
32
+
33
+ # @return [Boolean] true if the mcp gem is available
34
+ def self.available?
35
+ require 'mcp'
36
+ true
37
+ rescue LoadError
38
+ false
39
+ end
40
+
41
+ # Starts the MCP server on stdio transport.
42
+ #
43
+ # @return [void]
44
+ # @raise [RosettAi::McpError] if mcp gem is not available
45
+ def serve
46
+ ensure_mcp_available!
47
+ server = build_server
48
+ transport = ::MCP::Server::Transports::StdioTransport.new(server)
49
+ transport.open
50
+ end
51
+
52
+ # Starts the MCP server on HTTP transport via Puma.
53
+ #
54
+ # @param options [Hash] HTTP transport options
55
+ # @option options [Integer] :port HTTP port (default 8484)
56
+ # @option options [String] :host bind address (default localhost)
57
+ # @option options [String, nil] :tls_cert path to TLS certificate
58
+ # @option options [String, nil] :tls_key path to TLS private key
59
+ # @option options [Boolean] :allow_remote allow non-localhost binding
60
+ # @return [void]
61
+ # @raise [RosettAi::McpError] if puma/rackup gems are not available
62
+ def serve_http(**options)
63
+ ensure_mcp_available!
64
+ ensure_http_available!
65
+
66
+ port = options.fetch(:port, DEFAULT_PORT)
67
+ host = options.fetch(:host, DEFAULT_HOST)
68
+ tls_cert = options[:tls_cert]
69
+ tls_key = options[:tls_key]
70
+
71
+ validate_host_binding(host, options.fetch(:allow_remote, false))
72
+
73
+ rack_app = build_rack_app
74
+ start_puma(rack_app, port: port, host: host,
75
+ tls_cert: tls_cert, tls_key: tls_key)
76
+ end
77
+
78
+ # Build the Rack application for HTTP transport.
79
+ #
80
+ # @return [Proc] Rack application
81
+ def http_app
82
+ ensure_mcp_available!
83
+ ensure_http_available!
84
+ build_rack_app
85
+ end
86
+
87
+ # Returns the tool registry for listing available tools.
88
+ #
89
+ # @return [Array<Hash>] tool descriptors
90
+ def tools
91
+ Governance::TOOL_CLASSES.map do |klass|
92
+ {
93
+ name: klass::TOOL_NAME,
94
+ description: klass::DESCRIPTION,
95
+ annotations: klass::ANNOTATIONS
96
+ }
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def ensure_mcp_available!
103
+ return if self.class.available?
104
+
105
+ raise RosettAi::McpError,
106
+ 'MCP gem not available. Install with: bundle install --with mcp'
107
+ end
108
+
109
+ def ensure_http_available!
110
+ require 'puma'
111
+ require 'rackup'
112
+ rescue LoadError => e
113
+ raise RosettAi::McpError,
114
+ "HTTP transport requires puma and rackup gems: #{e.message}"
115
+ end
116
+
117
+ def build_server
118
+ server = ::MCP::Server.new(
119
+ name: 'rosett-ai',
120
+ version: RosettAi::VERSION,
121
+ instructions: Instructions.generate
122
+ )
123
+
124
+ Governance.register(server)
125
+ Plugins.load_all(server, @plugin_names)
126
+ server
127
+ end
128
+
129
+ def build_rack_app
130
+ server = build_server
131
+ transport = ::MCP::Server::Transports::StreamableHTTPTransport.new(server)
132
+ app = transport.method(:call)
133
+ security = @security_config
134
+
135
+ Rack::Builder.new do
136
+ use Middleware::RequestSize, max_size: security.max_request_size
137
+ use Middleware::ContentType if security.content_type_enforcement
138
+ use Middleware::OriginValidation, config: security.origin
139
+ use Middleware::Cors, config: security.cors if security.cors.enabled
140
+ use Middleware::Authentication, config: security.authentication
141
+ use Middleware::RateLimit, config: security.rate_limiting if security.rate_limiting.enabled
142
+ run app
143
+ end.to_app
144
+ end
145
+
146
+ def start_puma(rack_app, port:, host:, tls_cert:, tls_key:)
147
+ puma_config = Puma::Configuration.new do |conf|
148
+ conf.app rack_app
149
+ if tls_cert && tls_key
150
+ conf.bind "ssl://#{host}:#{port}?cert=#{tls_cert}&key=#{tls_key}"
151
+ else
152
+ conf.bind "tcp://#{host}:#{port}"
153
+ end
154
+ conf.quiet
155
+ end
156
+
157
+ launcher = Puma::Launcher.new(puma_config)
158
+ launcher.run
159
+ end
160
+
161
+ def validate_host_binding(host, allow_remote)
162
+ return if allow_remote
163
+ return if ['localhost', '127.0.0.1', '::1'].include?(host)
164
+
165
+ raise RosettAi::McpError,
166
+ "Binding to #{host} requires --allow-remote flag for security"
167
+ end
168
+
169
+ def load_security_config(config_path)
170
+ config = HttpSecurityConfig.new
171
+ yaml = load_config_yaml(config_path)
172
+ config.apply(yaml['http']) if yaml&.dig('http')
173
+ config
174
+ end
175
+
176
+ def load_config_yaml(config_path)
177
+ path = config_path || find_config_file
178
+ return nil unless path && File.exist?(path)
179
+
180
+ YAML.safe_load_file(path, permitted_classes: [Symbol])
181
+ rescue Psych::SyntaxError
182
+ nil
183
+ end
184
+
185
+ def find_config_file
186
+ candidates = [
187
+ ENV.fetch('RAI_MCP_CONFIG', nil),
188
+ find_upward('.rosett-ai-mcp.yml'),
189
+ File.expand_path('~/.config/rosett-ai/mcp/server.yml'),
190
+ '/etc/rosett-ai/mcp/server.yml',
191
+ RosettAi.root.join('conf', 'mcp', 'server_defaults.yml').to_s
192
+ ].compact
193
+
194
+ candidates.find { |path| File.exist?(path) }
195
+ end
196
+
197
+ def find_upward(filename)
198
+ dir = Dir.pwd
199
+ loop do
200
+ candidate = File.join(dir, filename)
201
+ return candidate if File.exist?(candidate)
202
+
203
+ parent = File.dirname(dir)
204
+ break if parent == dir
205
+
206
+ dir = parent
207
+ end
208
+ nil
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ module RosettAi
7
+ module Mcp
8
+ module Settings
9
+ # Installs MCP servers with trust-first validation.
10
+ #
11
+ # All server installations must originate from a trusted source.
12
+ # Untrusted sources are rejected with actionable error messages.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class ServerInstaller
17
+ # @param trust_manager [TrustManager] trust validation
18
+ # @param registry [Admin::Registry] server registry
19
+ # @param schema_validator [Admin::SchemaValidator] config validator
20
+ def initialize(trust_manager:, registry:, schema_validator:)
21
+ @trust_manager = trust_manager
22
+ @registry = registry
23
+ @schema_validator = schema_validator
24
+ end
25
+
26
+ # Installs an MCP server from a URI with trust validation.
27
+ #
28
+ # @param name [String] server name
29
+ # @param uri [String] server URI or command
30
+ # @param transport [String] transport type (stdio, http)
31
+ # @return [Hash] result with :success, :message, :server
32
+ def install(name:, uri:, transport: 'stdio')
33
+ trust_result = validate_trust(uri, transport)
34
+ return trust_result unless trust_result[:trusted]
35
+
36
+ config = build_config(name, uri, transport)
37
+ validation = @schema_validator.validate(config)
38
+ return validation_failure(validation) unless validation[:valid]
39
+
40
+ path = @registry.add(config)
41
+ {
42
+ success: true,
43
+ message: "Server '#{name}' installed from #{trust_result[:source_type]} source",
44
+ server: config,
45
+ path: path.to_s
46
+ }
47
+ end
48
+
49
+ # Removes an MCP server by name.
50
+ #
51
+ # @param name [String] server name
52
+ # @param force [Boolean] skip confirmation for enterprise-managed servers
53
+ # @return [Hash] result with :success, :message
54
+ def remove(name:, force: false)
55
+ server = @registry.find(name)
56
+ return { success: false, message: "Server '#{name}' not found" } unless server
57
+
58
+ if managed_server?(server) && !force
59
+ return {
60
+ success: false,
61
+ message: "Server '#{name}' is enterprise-managed. Use --force to override."
62
+ }
63
+ end
64
+
65
+ @registry.remove(name)
66
+ { success: true, message: "Server '#{name}' removed" }
67
+ end
68
+
69
+ private
70
+
71
+ def validate_trust(uri, transport)
72
+ return { trusted: true, source_type: 'local' } if local_transport?(transport)
73
+
74
+ result = @trust_manager.validate_uri(uri)
75
+ return result if result[:trusted]
76
+
77
+ {
78
+ trusted: false,
79
+ success: false,
80
+ message: "Untrusted source: #{result[:domain]}. " \
81
+ 'Add trust with: rai mcp trust-sources add DOMAIN'
82
+ }
83
+ end
84
+
85
+ def local_transport?(transport)
86
+ transport == 'stdio'
87
+ end
88
+
89
+ def build_config(name, uri, transport)
90
+ config = { name: name, transport: transport }
91
+ if transport == 'stdio'
92
+ config[:command] = uri
93
+ else
94
+ config[:url] = uri
95
+ end
96
+ config
97
+ end
98
+
99
+ def validation_failure(validation)
100
+ {
101
+ success: false,
102
+ message: "Validation failed: #{validation[:errors].join(', ')}"
103
+ }
104
+ end
105
+
106
+ def managed_server?(server)
107
+ server[:source_file].to_s.include?('managed')
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end