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,156 @@
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 'yaml'
7
+ require 'pathname'
8
+
9
+ module RosettAi
10
+ module Mcp
11
+ module Tools
12
+ # MCP tool: query applicable rules for a given context.
13
+ #
14
+ # Answers "What rules apply to this situation?" by matching rules
15
+ # against a file path, tool name, or action. Returns applicable
16
+ # rules ranked by priority. Read-only operation.
17
+ #
18
+ # @author hugo
19
+ # @author claude
20
+ class ContextQueryTool
21
+ TOOL_NAME = 'rai_context_query'
22
+ DESCRIPTION = 'Query applicable rules for a file path, tool name, or action'
23
+
24
+ ANNOTATIONS = {
25
+ 'readOnlyHint' => true,
26
+ 'destructiveHint' => false,
27
+ 'idempotentHint' => true,
28
+ 'openWorldHint' => false
29
+ }.freeze
30
+
31
+ INPUT_SCHEMA = {
32
+ type: 'object',
33
+ properties: {
34
+ file_path: {
35
+ type: 'string',
36
+ description: 'File path to query applicable rules for'
37
+ },
38
+ tool_name: {
39
+ type: 'string',
40
+ description: 'Tool name to query applicable rules for'
41
+ },
42
+ action: {
43
+ type: 'string',
44
+ description: 'Action to query applicable rules for'
45
+ }
46
+ }
47
+ }.freeze
48
+
49
+ # Queries rules applicable to the given context.
50
+ #
51
+ # @param file_path [String, nil] file path context
52
+ # @param tool_name [String, nil] tool name context (e.g. 'Bash', 'Edit')
53
+ # @param action [String, nil] action context (e.g. 'write', 'delete')
54
+ # @return [Hash] applicable rules with metadata
55
+ def call(file_path: nil, tool_name: nil, action: nil)
56
+ query = build_query(file_path, tool_name, action)
57
+ return ResponseHelper.error('At least one query parameter required') if query.empty?
58
+
59
+ matches = search_rules(query)
60
+ matches.sort_by! { |m| -(m[:priority] || 0) }
61
+
62
+ {
63
+ applicable_rules: matches,
64
+ total: matches.size,
65
+ query: query
66
+ }
67
+ rescue StandardError => e
68
+ ResponseHelper.error("Context query failed: #{e.message}")
69
+ end
70
+
71
+ private
72
+
73
+ # @param file_path [String, nil]
74
+ # @param tool_name [String, nil]
75
+ # @param action [String, nil]
76
+ # @return [Hash]
77
+ def build_query(file_path, tool_name, action)
78
+ query = {}
79
+ query[:file_path] = file_path if file_path
80
+ query[:tool_name] = tool_name if tool_name
81
+ query[:action] = action if action
82
+ query
83
+ end
84
+
85
+ # @param query [Hash]
86
+ # @return [Array<Hash>]
87
+ def search_rules(query)
88
+ matches = []
89
+ each_behaviour do |data|
90
+ match_rules(data, query, matches)
91
+ end
92
+ matches
93
+ end
94
+
95
+ # @param data [Hash]
96
+ # @param query [Hash]
97
+ # @param matches [Array<Hash>]
98
+ def match_rules(data, query, matches)
99
+ rules = data['rules']
100
+ return unless rules.is_a?(Array)
101
+
102
+ behaviour_name = data['name']
103
+ rules.each do |rule|
104
+ next unless rule.is_a?(Hash) && rule.fetch('enabled', true)
105
+ next unless rule_matches_query?(rule, query)
106
+
107
+ matches << format_match(behaviour_name, rule)
108
+ end
109
+ end
110
+
111
+ # @param rule [Hash]
112
+ # @param query [Hash]
113
+ # @return [Boolean]
114
+ def rule_matches_query?(rule, query)
115
+ description = rule['description'].to_s.downcase
116
+
117
+ query.any? do |_key, value|
118
+ description.include?(value.to_s.downcase)
119
+ end
120
+ end
121
+
122
+ # @param behaviour_name [String]
123
+ # @param rule [Hash]
124
+ # @return [Hash]
125
+ def format_match(behaviour_name, rule)
126
+ {
127
+ rule_id: rule['id'],
128
+ behaviour: behaviour_name,
129
+ priority: rule['priority'] || 0,
130
+ description: rule['description'].to_s.strip[0, 200]
131
+ }
132
+ end
133
+
134
+ # @yield [Hash] parsed behaviour data
135
+ def each_behaviour
136
+ behaviour_dirs.each do |dir|
137
+ next unless dir.directory?
138
+
139
+ dir.glob('*.yml').each do |path|
140
+ data = YAML.safe_load_file(path, permitted_classes: [Symbol])
141
+ yield data if data.is_a?(Hash)
142
+ end
143
+ end
144
+ end
145
+
146
+ # @return [Array<Pathname>]
147
+ def behaviour_dirs
148
+ dirs = [RosettAi.root.join('conf', 'behaviour')]
149
+ xdg = RosettAi.paths.rai_conf_dir.join('behaviour')
150
+ dirs << xdg if xdg.directory?
151
+ dirs
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,57 @@
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 Tools
9
+ # MCP tool: list rai design documents.
10
+ #
11
+ # Returns a structured list of all design documents with
12
+ # metadata (name, status, priority, domain).
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class DesignListTool
17
+ TOOL_NAME = 'rai_design_list'
18
+ DESCRIPTION = 'List all rai design documents'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => true,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ # Executes the listing.
28
+ #
29
+ # @return [Hash] result with :designs array
30
+ def call
31
+ dir = RosettAi.root.join('conf', 'design')
32
+ return { designs: [] } unless dir.directory?
33
+
34
+ designs = dir.glob('*.yml').filter_map { |path| load_design(path) }
35
+ { designs: designs }
36
+ end
37
+
38
+ private
39
+
40
+ def load_design(path)
41
+ data = YAML.safe_load_file(path, permitted_classes: [Symbol])
42
+ return nil unless data.is_a?(Hash)
43
+
44
+ {
45
+ name: data['name'],
46
+ status: data['status'],
47
+ priority: data['priority'],
48
+ domain: data['domain'],
49
+ file: path.basename.to_s
50
+ }
51
+ rescue Psych::SyntaxError
52
+ nil
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,69 @@
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 Tools
9
+ # MCP tool: show a specific design document.
10
+ #
11
+ # Returns the full content and metadata of a named design document.
12
+ # Read-only operation.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class DesignShowTool
17
+ TOOL_NAME = 'rai_design_show'
18
+ DESCRIPTION = 'Show details of a specific rai design document'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => true,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ INPUT_SCHEMA = {
28
+ type: 'object',
29
+ properties: {
30
+ name: {
31
+ type: 'string',
32
+ description: 'Design document name'
33
+ },
34
+ full: {
35
+ type: 'boolean',
36
+ description: 'Include full content (default: false)'
37
+ }
38
+ },
39
+ required: ['name']
40
+ }.freeze
41
+
42
+ # Executes the show operation.
43
+ #
44
+ # @param name [String] design document name (without .yml)
45
+ # @param full [Boolean] include acceptance criteria
46
+ # @return [Hash] design data or error
47
+ def call(name:, full: false)
48
+ path = RosettAi.root.join('conf', 'design', "#{name}.yml")
49
+ return ResponseHelper.error("Design document not found: #{name}") unless path.exist?
50
+
51
+ data = YAML.safe_load_file(path, permitted_classes: [Symbol])
52
+ result = {
53
+ name: data['name'],
54
+ description: data['description'],
55
+ status: data['status'],
56
+ priority: data['priority'],
57
+ domain: data['domain'],
58
+ version: data['version'],
59
+ file: path.basename.to_s
60
+ }
61
+ result[:acceptance_criteria] = Array(data['acceptance_criteria']) if full
62
+ result
63
+ rescue Psych::SyntaxError => e
64
+ ResponseHelper.error("YAML parse error in #{name}: #{e.message}")
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,62 @@
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 Tools
9
+ # MCP tool: run rosett-ai diagnostics.
10
+ #
11
+ # Executes system diagnostic checks and reports status.
12
+ # Read-only operation.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class DoctorTool
17
+ TOOL_NAME = 'rai_doctor'
18
+ DESCRIPTION = 'Run rosett-ai diagnostic checks'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => true,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ INPUT_SCHEMA = {
28
+ type: 'object',
29
+ properties: {
30
+ check: {
31
+ type: 'string',
32
+ description: 'Run a specific diagnostic check (default: all)'
33
+ },
34
+ format: {
35
+ type: 'string',
36
+ enum: ['json', 'text'],
37
+ description: 'Output format (default: json)'
38
+ }
39
+ }
40
+ }.freeze
41
+
42
+ # Executes diagnostic checks.
43
+ #
44
+ # @param check [String, nil] specific check to run
45
+ # @param format [String] output format ('json' or 'text')
46
+ # @return [Hash] diagnostic results
47
+ def call(check: nil, format: 'json')
48
+ results = RosettAi::Doctor.run_all(only: check)
49
+ failures = results.select { |r| r[:status] == 'fail' }
50
+ {
51
+ healthy: failures.empty?,
52
+ checks: results,
53
+ failures: failures,
54
+ format: format
55
+ }
56
+ rescue StandardError => e
57
+ ResponseHelper.error("Doctor check failed: #{e.message}")
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,45 @@
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 Tools
9
+ # MCP tool: show documentation status.
10
+ #
11
+ # Returns the current state of project documentation.
12
+ # Read-only operation.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class DocumentationStatusTool
17
+ TOOL_NAME = 'rai_documentation_status'
18
+ DESCRIPTION = 'Show rai documentation status'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => true,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ # Executes the documentation status check.
28
+ #
29
+ # @return [Hash] documentation status
30
+ def call
31
+ root = RosettAi.root
32
+ doc_dir = root.join('doc')
33
+ man_dir = root.join('doc', 'man')
34
+ {
35
+ doc_files: doc_dir.directory? ? doc_dir.glob('*.md').size : 0,
36
+ man_pages: man_dir.directory? ? man_dir.glob('*.ronn').size : 0,
37
+ changelog_exists: root.join('CHANGELOG.md').exist?,
38
+ readme_exists: root.join('README.md').exist?,
39
+ install_guide_exists: root.join('INSTALL.md').exist?
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,84 @@
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 Tools
9
+ # MCP tool: manage and query rai engines.
10
+ #
11
+ # Lists, detects, and shows status of installed engines.
12
+ # Read-only operation.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class EnginesTool
17
+ TOOL_NAME = 'rosett_ai_engines'
18
+ DESCRIPTION = 'List, detect, or show status of rai engines'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => true,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ VALID_ACTIONS = ['list', 'detect', 'status'].freeze
28
+
29
+ INPUT_SCHEMA = {
30
+ type: 'object',
31
+ properties: {
32
+ action: {
33
+ type: 'string',
34
+ enum: ['list', 'detect', 'status'],
35
+ description: 'Engine action (default: list)'
36
+ }
37
+ }
38
+ }.freeze
39
+
40
+ # Executes the engine query.
41
+ #
42
+ # @param action [String] one of 'list', 'detect', 'status'
43
+ # @return [Hash] engine information
44
+ def call(action: 'list')
45
+ return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
46
+
47
+ case action
48
+ when 'list' then action_list
49
+ when 'detect' then action_detect
50
+ when 'status' then action_status
51
+ end
52
+ rescue StandardError => e
53
+ ResponseHelper.error("Engine #{action} failed: #{e.message}")
54
+ end
55
+
56
+ private
57
+
58
+ def action_list
59
+ engines = RosettAi::Plugins::Registry.available(:engine)
60
+ { engines: engines.map { |name| { name: name } } }
61
+ end
62
+
63
+ def action_detect
64
+ RosettAi::Plugins::Registry.discover!
65
+ detected = RosettAi::Plugins::Registry.available(:engine)
66
+ { detected: detected }
67
+ end
68
+
69
+ def action_status
70
+ default = resolve_default_engine
71
+ installed = RosettAi::Plugins::Registry.available(:engine)
72
+ { default_engine: default, installed: installed.map { |name| { name: name } } }
73
+ end
74
+
75
+ # @return [String] default engine name
76
+ def resolve_default_engine
77
+ RosettAi.rai_config.default_engine
78
+ rescue StandardError => e
79
+ "unknown (#{e.message})"
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-License-Identifier: GPL-3.0-only
4
+ # Copyright (C) 2026 Hugo Antonio Sepulveda Manriquez / NeatNerds
5
+
6
+ require 'json'
7
+ require 'fileutils'
8
+ require 'time'
9
+
10
+ module RosettAi
11
+ module Mcp
12
+ module Tools
13
+ # MCP tool: install enforcement hook + register in settings.json.
14
+ #
15
+ # MUTATION tool — writes hook script to disk and updates Claude Code
16
+ # settings.json. Auto-backs up affected files before writing.
17
+ # Confirmation is required by default.
18
+ #
19
+ # @author hugo
20
+ # @author claude
21
+ class HookInstallTool
22
+ TOOL_NAME = 'rai_hook_install'
23
+ DESCRIPTION = 'Install enforcement hook: generate script, write to disk, register in settings'
24
+
25
+ ANNOTATIONS = {
26
+ 'readOnlyHint' => false,
27
+ 'destructiveHint' => true,
28
+ 'idempotentHint' => true,
29
+ 'openWorldHint' => false
30
+ }.freeze
31
+
32
+ INPUT_SCHEMA = {
33
+ type: 'object',
34
+ properties: {
35
+ behaviour_name: {
36
+ type: 'string',
37
+ description: 'Behaviour to generate hook for (default: all enforceable)'
38
+ },
39
+ rule_id: {
40
+ type: 'string',
41
+ description: 'Specific rule ID to generate hook for'
42
+ },
43
+ scope: {
44
+ type: 'string',
45
+ enum: ['global', 'local'],
46
+ description: 'Installation scope (default: global)'
47
+ },
48
+ confirm: {
49
+ type: 'boolean',
50
+ description: 'Must be true to actually install — safety gate (default: false)'
51
+ }
52
+ }
53
+ }.freeze
54
+
55
+ HOOK_DIR_NAME = 'hooks'
56
+ HOOK_FILENAME = 'rai-enforce.rb'
57
+ BACKUP_SUFFIX = '.rai-backup'
58
+
59
+ # Installs the enforcement hook.
60
+ #
61
+ # @param behaviour_name [String, nil] specific behaviour
62
+ # @param rule_id [String, nil] specific rule
63
+ # @param scope [String] installation scope ('global' or 'project')
64
+ # @param confirm [Boolean] must be true to proceed (default false)
65
+ # @return [Hash] installation result with paths and backup info
66
+ def call(behaviour_name: nil, rule_id: nil, scope: 'global', confirm: false)
67
+ return confirmation_required_response unless confirm
68
+
69
+ preview = HookPreviewTool.new.call(
70
+ behaviour_name: behaviour_name, rule_id: rule_id, scope: scope
71
+ )
72
+ return preview if preview[:error]
73
+
74
+ hook_path = resolve_hook_path(scope)
75
+ backup_path = backup_existing(hook_path)
76
+ write_hook(hook_path, preview[:script])
77
+ settings_diff = register_in_settings(hook_path, scope)
78
+
79
+ {
80
+ installed: true,
81
+ hook_path: hook_path.to_s,
82
+ backup_path: backup_path&.to_s,
83
+ rules_count: preview[:rules_count],
84
+ behaviours: preview[:behaviours],
85
+ scope: scope,
86
+ settings_updated: settings_diff[:updated],
87
+ message: "Hook installed at #{hook_path} with #{preview[:rules_count]} rule(s)"
88
+ }
89
+ rescue StandardError => e
90
+ ResponseHelper.error("Hook install failed: #{e.message}")
91
+ end
92
+
93
+ private
94
+
95
+ # @return [Hash] response requiring confirmation
96
+ def confirmation_required_response
97
+ {
98
+ error: true,
99
+ confirmation_required: true,
100
+ message: 'Hook installation requires confirm: true. ' \
101
+ 'Use rai_hook_preview first to review the script.'
102
+ }
103
+ end
104
+
105
+ # @param scope [String]
106
+ # @return [Pathname]
107
+ def resolve_hook_path(scope)
108
+ base = scope == 'project' ? project_hooks_dir : global_hooks_dir
109
+ FileUtils.mkdir_p(base)
110
+ base.join(HOOK_FILENAME)
111
+ end
112
+
113
+ # @return [Pathname]
114
+ def global_hooks_dir
115
+ RosettAi.paths.global_dir.join(HOOK_DIR_NAME)
116
+ end
117
+
118
+ # @return [Pathname]
119
+ def project_hooks_dir
120
+ RosettAi.context.project_root.join('.claude', HOOK_DIR_NAME)
121
+ end
122
+
123
+ # Backs up existing hook file before overwriting.
124
+ #
125
+ # @param hook_path [Pathname]
126
+ # @return [Pathname, nil] backup path or nil if no backup needed
127
+ def backup_existing(hook_path)
128
+ return nil unless hook_path.exist?
129
+
130
+ timestamp = Time.now.utc.strftime('%Y%m%d%H%M%S')
131
+ backup = Pathname.new("#{hook_path}#{BACKUP_SUFFIX}.#{timestamp}")
132
+ FileUtils.cp(hook_path, backup)
133
+ backup
134
+ end
135
+
136
+ # @param hook_path [Pathname]
137
+ # @param content [String]
138
+ def write_hook(hook_path, content)
139
+ FileUtils.mkdir_p(hook_path.dirname)
140
+ File.write(hook_path, content)
141
+ FileUtils.chmod(0o755, hook_path)
142
+ end
143
+
144
+ # Registers the hook in settings.json if not already present.
145
+ #
146
+ # @param hook_path [Pathname]
147
+ # @param scope [String]
148
+ # @return [Hash] diff info
149
+ def register_in_settings(hook_path, scope)
150
+ settings_path = resolve_settings_path(scope)
151
+ return { updated: false, reason: 'settings file not found' } unless settings_path.exist?
152
+
153
+ settings = JSON.parse(settings_path.read)
154
+ hooks = settings['hooks'] ||= {}
155
+ pre_tool = hooks['PreToolUse'] ||= []
156
+
157
+ hook_entry = build_hook_entry(hook_path)
158
+
159
+ if pre_tool.any? { |h| h['command'] == hook_entry['command'] }
160
+ return { updated: false, reason: 'hook already registered' }
161
+ end
162
+
163
+ pre_tool << hook_entry
164
+ File.write(settings_path, JSON.pretty_generate(settings))
165
+ { updated: true, settings_path: settings_path.to_s }
166
+ end
167
+
168
+ # @param scope [String]
169
+ # @return [Pathname]
170
+ def resolve_settings_path(scope)
171
+ if scope == 'project'
172
+ RosettAi.context.project_root.join('.claude', 'settings.json')
173
+ else
174
+ RosettAi.paths.global_dir.join('settings.json')
175
+ end
176
+ end
177
+
178
+ # @param hook_path [Pathname]
179
+ # @return [Hash]
180
+ def build_hook_entry(hook_path)
181
+ {
182
+ 'type' => 'command',
183
+ 'command' => "ruby #{hook_path}",
184
+ 'description' => 'Rosett-AI enforcement hook'
185
+ }
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end