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,81 @@
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: retrofit native configurations.
10
+ #
11
+ # Scan and convert native AI tool configs to Rosett-AI YAML.
12
+ # Write operation for convert action.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class RetrofitTool
17
+ TOOL_NAME = 'rai_retrofit'
18
+ DESCRIPTION = 'Scan and convert native AI configs to rosett-ai format'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => false,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ VALID_ACTIONS = ['engines', 'scan', 'convert'].freeze
28
+
29
+ INPUT_SCHEMA = {
30
+ type: 'object',
31
+ properties: {
32
+ action: {
33
+ type: 'string',
34
+ enum: ['engines', 'scan', 'convert'],
35
+ description: 'Retrofit action (default: scan)'
36
+ },
37
+ engine: {
38
+ type: 'string',
39
+ description: 'Engine to retrofit from'
40
+ },
41
+ simulate: {
42
+ type: 'boolean',
43
+ description: 'Dry-run mode (default: true)'
44
+ }
45
+ }
46
+ }.freeze
47
+
48
+ # Executes the retrofit operation.
49
+ #
50
+ # @param action [String] one of 'engines', 'scan', 'convert'
51
+ # @param engine [String, nil] specific engine for conversion
52
+ # @param simulate [Boolean] dry-run mode for convert
53
+ # @return [Hash] retrofit results
54
+ def call(action: 'scan', engine: nil, simulate: true)
55
+ return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
56
+
57
+ retrofitter = RosettAi::Retrofit::Scanner.new
58
+ case action
59
+ when 'engines' then { parsers: retrofitter.available_parsers }
60
+ when 'scan' then { findings: retrofitter.scan }
61
+ when 'convert' then action_convert(retrofitter, engine, simulate)
62
+ end
63
+ rescue StandardError => e
64
+ ResponseHelper.error("Retrofit #{action} failed: #{e.message}")
65
+ end
66
+
67
+ private
68
+
69
+ def action_convert(retrofitter, engine, simulate)
70
+ result = retrofitter.convert(engine: engine, options: { dry_run: simulate })
71
+ {
72
+ simulate: simulate,
73
+ engine: engine,
74
+ converted: result[:converted],
75
+ files: result[:files]
76
+ }
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,163 @@
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: search compiled rules by keyword.
13
+ #
14
+ # Searches behaviour YAML source files for rules matching a keyword,
15
+ # with optional filters for minimum priority, scope, and enabled status.
16
+ # Read-only operation.
17
+ #
18
+ # @author hugo
19
+ # @author claude
20
+ class RuleSearchTool
21
+ TOOL_NAME = 'rai_rule_search'
22
+ DESCRIPTION = 'Search rules by keyword with optional priority/scope/enabled filters'
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
+ keyword: {
35
+ type: 'string',
36
+ description: 'Search keyword to match against rule descriptions and IDs'
37
+ },
38
+ priority_min: {
39
+ type: 'integer',
40
+ minimum: 0,
41
+ maximum: 100,
42
+ description: 'Minimum priority threshold (0-100)'
43
+ },
44
+ scope: {
45
+ type: 'string',
46
+ description: 'Behaviour scope to search within'
47
+ },
48
+ enabled_only: {
49
+ type: 'boolean',
50
+ description: 'Only return enabled rules (default: true)'
51
+ }
52
+ },
53
+ required: ['keyword']
54
+ }.freeze
55
+
56
+ # Searches rules across all behaviour files.
57
+ #
58
+ # @param keyword [String] search term (matched case-insensitively)
59
+ # @param priority_min [Integer, nil] minimum priority filter
60
+ # @param scope [String, nil] scope filter ('global', 'project', 'personal')
61
+ # @param enabled_only [Boolean] filter to enabled rules only (default true)
62
+ # @return [Hash] matching rules with metadata
63
+ def call(keyword:, priority_min: nil, scope: nil, enabled_only: true)
64
+ matches = []
65
+ each_behaviour_file do |path, data|
66
+ search_behaviour(data, keyword, path, matches)
67
+ end
68
+
69
+ matches = apply_filters(matches, priority_min, scope, enabled_only)
70
+ matches.sort_by! { |m| -(m[:priority] || 0) }
71
+
72
+ { keyword: keyword, matches: matches, total: matches.size }
73
+ rescue StandardError => e
74
+ ResponseHelper.error("Rule search failed: #{e.message}")
75
+ end
76
+
77
+ private
78
+
79
+ # @yield [Pathname, Hash] path and parsed YAML data
80
+ def each_behaviour_file
81
+ behaviour_dirs.each do |dir|
82
+ next unless dir.directory?
83
+
84
+ dir.glob('*.yml').each do |path|
85
+ data = YAML.safe_load_file(path, permitted_classes: [Symbol])
86
+ yield path, data if data.is_a?(Hash)
87
+ end
88
+ end
89
+ end
90
+
91
+ # @return [Array<Pathname>] directories to search for behaviours
92
+ def behaviour_dirs
93
+ dirs = [RosettAi.root.join('conf', 'behaviour')]
94
+ xdg = RosettAi.paths.rai_conf_dir.join('behaviour')
95
+ dirs << xdg if xdg.directory?
96
+ dirs
97
+ end
98
+
99
+ # @param data [Hash] parsed behaviour YAML
100
+ # @param keyword [String] search term
101
+ # @param path [Pathname] source file path
102
+ # @param matches [Array<Hash>] accumulator
103
+ def search_behaviour(data, keyword, path, matches)
104
+ rules = data['rules']
105
+ return unless rules.is_a?(Array)
106
+
107
+ pattern = Regexp.new(Regexp.escape(keyword), Regexp::IGNORECASE)
108
+ behaviour_name = data['name'] || File.basename(path, '.yml')
109
+
110
+ rules.each do |rule|
111
+ next unless rule.is_a?(Hash)
112
+
113
+ description = rule['description'].to_s
114
+ next unless description.match?(pattern)
115
+
116
+ matches << build_match(behaviour_name, rule, description, data, pattern)
117
+ end
118
+ end
119
+
120
+ # @param behaviour_name [String]
121
+ # @param rule [Hash]
122
+ # @param description [String]
123
+ # @param data [Hash]
124
+ # @param pattern [Regexp]
125
+ # @return [Hash]
126
+ def build_match(behaviour_name, rule, description, data, pattern)
127
+ snippet = extract_snippet(description, pattern)
128
+ {
129
+ behaviour: behaviour_name,
130
+ rule_id: rule['id'],
131
+ priority: rule['priority'] || 0,
132
+ enabled: rule.fetch('enabled', true),
133
+ scope: data['scope'],
134
+ snippet: snippet
135
+ }
136
+ end
137
+
138
+ # @param text [String]
139
+ # @param pattern [Regexp]
140
+ # @return [String] snippet around the first match (max 120 chars)
141
+ def extract_snippet(text, pattern)
142
+ match = pattern.match(text)
143
+ return text[0, 120] unless match
144
+
145
+ start = [match.begin(0) - 40, 0].max
146
+ text[start, 120].strip
147
+ end
148
+
149
+ # @param matches [Array<Hash>]
150
+ # @param priority_min [Integer, nil]
151
+ # @param scope [String, nil]
152
+ # @param enabled_only [Boolean]
153
+ # @return [Array<Hash>]
154
+ def apply_filters(matches, priority_min, scope, enabled_only)
155
+ matches = matches.select { |m| m[:priority] >= priority_min } if priority_min
156
+ matches = matches.select { |m| m[:scope] == scope } if scope
157
+ matches = matches.select { |m| m[:enabled] } if enabled_only
158
+ matches
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,94 @@
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 'pathname'
8
+
9
+ module RosettAi
10
+ module Mcp
11
+ module Tools
12
+ # MCP tool: retrieve JSON Schema content for validation.
13
+ #
14
+ # Returns the raw JSON Schema used for validating behaviour,
15
+ # design, or tooling configuration files. Read-only.
16
+ #
17
+ # @author hugo
18
+ # @author claude
19
+ class SchemaGetTool
20
+ TOOL_NAME = 'rai_schema_get'
21
+ DESCRIPTION = 'Get JSON Schema content for behaviour/design/tooling validation'
22
+
23
+ ANNOTATIONS = {
24
+ 'readOnlyHint' => true,
25
+ 'destructiveHint' => false,
26
+ 'idempotentHint' => true,
27
+ 'openWorldHint' => false
28
+ }.freeze
29
+
30
+ VALID_SCHEMAS = {
31
+ 'behaviour' => 'behaviour_schema.json',
32
+ 'design' => 'design_schema.json',
33
+ 'tooling' => 'tooling_schema.json',
34
+ 'workflow' => 'workflow_schema.json',
35
+ 'provenance' => 'provenance_schema.json',
36
+ 'policy' => 'policy_schema.json'
37
+ }.freeze
38
+
39
+ INPUT_SCHEMA = {
40
+ type: 'object',
41
+ properties: {
42
+ schema_name: {
43
+ type: 'string',
44
+ enum: ['behaviour', 'design', 'tooling', 'workflow', 'provenance', 'policy'],
45
+ description: 'Schema name to retrieve'
46
+ }
47
+ },
48
+ required: ['schema_name']
49
+ }.freeze
50
+
51
+ # Retrieves the JSON Schema content.
52
+ #
53
+ # @param schema_name [String] one of: behaviour, design, tooling, workflow, provenance, policy
54
+ # @return [Hash] schema content and metadata
55
+ def call(schema_name:)
56
+ filename = VALID_SCHEMAS[schema_name]
57
+ return invalid_schema_response(schema_name) unless filename
58
+
59
+ schema_path = RosettAi.root.join('conf', 'schemas', filename)
60
+ return missing_schema_response(schema_path) unless schema_path.exist?
61
+
62
+ content = JSON.parse(schema_path.read)
63
+ {
64
+ schema_name: schema_name,
65
+ filename: filename,
66
+ content: content,
67
+ path: schema_path.to_s
68
+ }
69
+ rescue JSON::ParserError => e
70
+ ResponseHelper.error("Schema parse error: #{e.message}")
71
+ rescue StandardError => e
72
+ ResponseHelper.error("Schema get failed: #{e.message}")
73
+ end
74
+
75
+ private
76
+
77
+ # @param schema_name [String]
78
+ # @return [Hash]
79
+ def invalid_schema_response(schema_name)
80
+ ResponseHelper.error(
81
+ "Unknown schema: #{schema_name}. " \
82
+ "Available: #{VALID_SCHEMAS.keys.join(', ')}"
83
+ )
84
+ end
85
+
86
+ # @param schema_path [Pathname]
87
+ # @return [Hash]
88
+ def missing_schema_response(schema_path)
89
+ ResponseHelper.error("Schema file not found: #{schema_path}")
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,86 @@
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 tooling validation.
10
+ #
11
+ # Validates CI YAML syntax and checks tool versions.
12
+ # Read-only operation.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class ToolingTool
17
+ TOOL_NAME = 'rai_tooling'
18
+ DESCRIPTION = 'Run tooling validation (CI YAML, version checks)'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => true,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ VALID_ACTIONS = ['validate-ci-yaml', 'check-versions'].freeze
28
+
29
+ INPUT_SCHEMA = {
30
+ type: 'object',
31
+ properties: {
32
+ action: {
33
+ type: 'string',
34
+ enum: ['validate-ci-yaml', 'check-versions'],
35
+ description: 'Tooling action (default: validate-ci-yaml)'
36
+ },
37
+ verbose: {
38
+ type: 'boolean',
39
+ description: 'Show detailed output (default: false)'
40
+ }
41
+ }
42
+ }.freeze
43
+
44
+ # Executes tooling validation.
45
+ #
46
+ # @param action [String] one of 'validate-ci-yaml', 'check-versions'
47
+ # @param verbose [Boolean] show detailed output
48
+ # @return [Hash] validation results
49
+ def call(action: 'validate-ci-yaml', verbose: false)
50
+ return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
51
+
52
+ case action
53
+ when 'validate-ci-yaml' then validate_ci_yaml(verbose)
54
+ when 'check-versions' then check_versions(verbose)
55
+ end
56
+ rescue StandardError => e
57
+ ResponseHelper.error("Tooling #{action} failed: #{e.message}")
58
+ end
59
+
60
+ private
61
+
62
+ def validate_ci_yaml(verbose)
63
+ validator = RosettAi::Tooling::CiYamlValidator.new
64
+ results = validator.validate
65
+ {
66
+ valid: results[:errors].empty?,
67
+ files_checked: results[:files],
68
+ errors: results[:errors],
69
+ verbose: verbose
70
+ }
71
+ end
72
+
73
+ def check_versions(verbose)
74
+ checker = RosettAi::Tooling::VersionChecker.new
75
+ results = checker.check
76
+ {
77
+ up_to_date: results[:outdated].empty?,
78
+ tools: results[:tools],
79
+ outdated: results[:outdated],
80
+ verbose: verbose
81
+ }
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,105 @@
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
+
8
+ module RosettAi
9
+ module Mcp
10
+ module Tools
11
+ # MCP tool: validate rai configuration files.
12
+ #
13
+ # Performs both YAML syntax and JSON Schema validation using the
14
+ # project's {RosettAi::Validators::SchemaValidator} hierarchy.
15
+ # Read-only operation.
16
+ #
17
+ # @author hugo
18
+ # @author claude
19
+ class ValidateTool
20
+ TOOL_NAME = 'rai_validate'
21
+ DESCRIPTION = 'Validate rai configuration files (behaviours, designs, tooling)'
22
+
23
+ ANNOTATIONS = {
24
+ 'readOnlyHint' => true,
25
+ 'destructiveHint' => false,
26
+ 'idempotentHint' => true,
27
+ 'openWorldHint' => false
28
+ }.freeze
29
+
30
+ INPUT_SCHEMA = {
31
+ type: 'object',
32
+ properties: {
33
+ scope: {
34
+ type: 'string',
35
+ enum: ['behaviour', 'design', 'tooling'],
36
+ description: 'Validation scope (default: all scopes)'
37
+ }
38
+ }
39
+ }.freeze
40
+
41
+ # Maps scope names to their validator classes.
42
+ SCOPE_VALIDATORS = {
43
+ 'behaviour' => -> { RosettAi::Validators::BehaviourValidator.new },
44
+ 'design' => -> { RosettAi::Validators::DesignValidator.new },
45
+ 'tooling' => -> { RosettAi::Validators::ToolingValidator.new }
46
+ }.freeze
47
+
48
+ # Executes the validation with JSON Schema checking.
49
+ #
50
+ # @param scope [String, nil] optional scope filter ('behaviour', 'design', 'tooling')
51
+ # @return [Hash] result with :valid, :errors, :warnings
52
+ def call(scope: nil)
53
+ results = { valid: true, errors: [], warnings: [] }
54
+ scopes = scope ? [scope] : ['behaviour', 'design']
55
+
56
+ scopes.each { |s| validate_scope(s, results) }
57
+ results[:valid] = results[:errors].empty?
58
+ results
59
+ end
60
+
61
+ private
62
+
63
+ # @param scope [String]
64
+ # @param results [Hash]
65
+ # @return [void]
66
+ def validate_scope(scope, results)
67
+ dir = RosettAi.root.join('conf', scope)
68
+ return unless dir.directory?
69
+
70
+ validator = build_validator(scope)
71
+ dir.glob('*.yml').each do |path|
72
+ validate_file(path, scope, validator, results)
73
+ end
74
+ end
75
+
76
+ # @param scope [String]
77
+ # @return [RosettAi::Validators::SchemaValidator, nil]
78
+ def build_validator(scope)
79
+ factory = SCOPE_VALIDATORS[scope]
80
+ factory&.call
81
+ rescue RosettAi::ConfigurationError
82
+ nil
83
+ end
84
+
85
+ # @param path [Pathname]
86
+ # @param scope [String]
87
+ # @param validator [RosettAi::Validators::SchemaValidator, nil]
88
+ # @param results [Hash]
89
+ # @return [void]
90
+ def validate_file(path, scope, validator, results)
91
+ file_str = path.to_s
92
+ YAML.safe_load_file(path, permitted_classes: [Symbol])
93
+
94
+ return unless validator
95
+
96
+ validator.validate(file_str).each do |error|
97
+ results[:errors] << { file: file_str, scope: scope, message: error }
98
+ end
99
+ rescue Psych::SyntaxError => e
100
+ results[:errors] << { file: file_str, scope: scope, message: e.message }
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,74 @@
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: execute a named workflow.
10
+ #
11
+ # Runs a declarative workflow by name.
12
+ # Write operation — executes workflow steps.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class WorkflowExecuteTool
17
+ TOOL_NAME = 'rai_workflow_execute'
18
+ DESCRIPTION = 'Execute a named rai workflow'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => false,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => false,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ INPUT_SCHEMA = {
28
+ type: 'object',
29
+ properties: {
30
+ name: {
31
+ type: 'string',
32
+ description: 'Workflow name to execute'
33
+ },
34
+ resume: {
35
+ type: 'boolean',
36
+ description: 'Resume a previously interrupted workflow (default: false)'
37
+ }
38
+ },
39
+ required: ['name']
40
+ }.freeze
41
+
42
+ # Executes the named workflow.
43
+ #
44
+ # @param name [String] workflow name
45
+ # @param resume [Boolean] resume a previously interrupted workflow
46
+ # @return [Hash] execution results
47
+ def call(name:, resume: false)
48
+ return ResponseHelper.error('Workflow name is required') if name.nil? || name.empty?
49
+
50
+ path = resolve_workflow(name)
51
+ return ResponseHelper.error("Workflow '#{name}' not found") unless path
52
+
53
+ engine = RosettAi::Workflow::Engine.new(workflow_path: path)
54
+ build_result(name, engine.execute(resume: resume))
55
+ rescue StandardError => e
56
+ ResponseHelper.error("Workflow execute failed: #{e.message}")
57
+ end
58
+
59
+ private
60
+
61
+ def build_result(name, summary)
62
+ { name: name, status: summary[:status],
63
+ steps_completed: summary[:steps_completed], output: summary[:output] }
64
+ end
65
+
66
+ def resolve_workflow(name)
67
+ dir = RosettAi.root.join('conf', 'workflows')
68
+ path = dir.join("#{name}.yml")
69
+ path.exist? ? path : nil
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,78 @@
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: query workflow definitions.
10
+ #
11
+ # Lists, validates, and simulates declarative workflows.
12
+ # Read-only operation.
13
+ #
14
+ # @author hugo
15
+ # @author claude
16
+ class WorkflowTool
17
+ TOOL_NAME = 'rai_workflow'
18
+ DESCRIPTION = 'List, validate, or simulate rai workflows'
19
+
20
+ ANNOTATIONS = {
21
+ 'readOnlyHint' => true,
22
+ 'destructiveHint' => false,
23
+ 'idempotentHint' => true,
24
+ 'openWorldHint' => false
25
+ }.freeze
26
+
27
+ VALID_ACTIONS = ['list', 'validate', 'simulate'].freeze
28
+
29
+ INPUT_SCHEMA = {
30
+ type: 'object',
31
+ properties: {
32
+ action: {
33
+ type: 'string',
34
+ enum: ['list', 'validate', 'simulate'],
35
+ description: 'Workflow action (default: list)'
36
+ },
37
+ name: {
38
+ type: 'string',
39
+ description: 'Workflow name (required for validate/simulate)'
40
+ }
41
+ }
42
+ }.freeze
43
+
44
+ # Executes the workflow query.
45
+ #
46
+ # @param action [String] one of 'list', 'validate', 'simulate'
47
+ # @param name [String, nil] workflow name (for validate/simulate)
48
+ # @return [Hash] workflow information
49
+ def call(action: 'list', name: nil)
50
+ return ResponseHelper.error("Invalid action: #{action}") unless VALID_ACTIONS.include?(action)
51
+
52
+ manager = RosettAi::Workflow::Manager.new
53
+ case action
54
+ when 'list' then { workflows: manager.list }
55
+ when 'validate' then action_validate(manager, name)
56
+ when 'simulate' then action_simulate(manager, name)
57
+ end
58
+ rescue StandardError => e
59
+ ResponseHelper.error("Workflow #{action} failed: #{e.message}")
60
+ end
61
+
62
+ private
63
+
64
+ def action_validate(manager, name)
65
+ results = name ? manager.validate(name) : manager.validate_all
66
+ { valid: results[:errors].empty?, errors: results[:errors] }
67
+ end
68
+
69
+ def action_simulate(manager, name)
70
+ return ResponseHelper.error('Workflow name required for simulate') unless name
71
+
72
+ result = manager.simulate(name)
73
+ { name: name, steps: result[:steps], estimated_duration: result[:duration] }
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end