rubik 0.1.0

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 (523) hide show
  1. checksums.yaml +7 -0
  2. data/ATTRIBUTION.md +139 -0
  3. data/CHANGELOG.md +236 -0
  4. data/LICENSE +21 -0
  5. data/README.md +269 -0
  6. data/bin/rubik +82 -0
  7. data/lib/rubik/adapter/base.rb +195 -0
  8. data/lib/rubik/adapter/c.rb +36 -0
  9. data/lib/rubik/adapter/crystal.rb +78 -0
  10. data/lib/rubik/adapter/generic.rb +86 -0
  11. data/lib/rubik/adapter/go.rb +36 -0
  12. data/lib/rubik/adapter/node.rb +146 -0
  13. data/lib/rubik/adapter/process.rb +78 -0
  14. data/lib/rubik/adapter/python.rb +30 -0
  15. data/lib/rubik/adapter/registry.rb +81 -0
  16. data/lib/rubik/adapter/ruby.rb +30 -0
  17. data/lib/rubik/adapter/rust.rb +36 -0
  18. data/lib/rubik/adapter/zig.rb +36 -0
  19. data/lib/rubik/adapter.rb +18 -0
  20. data/lib/rubik/ast/body_loader.rb +19 -0
  21. data/lib/rubik/ast/c_extractor.rb +114 -0
  22. data/lib/rubik/ast/cache_store.rb +191 -0
  23. data/lib/rubik/ast/extractor.rb +59 -0
  24. data/lib/rubik/ast/fallback_cleaner.rb +145 -0
  25. data/lib/rubik/ast/go_extractor.rb +148 -0
  26. data/lib/rubik/ast/graph.rb +272 -0
  27. data/lib/rubik/ast/python_extractor.rb +120 -0
  28. data/lib/rubik/ast/ruby_extractor.rb +260 -0
  29. data/lib/rubik/ast/rust_extractor.rb +127 -0
  30. data/lib/rubik/ast/symbol.rb +103 -0
  31. data/lib/rubik/ast/treesitter/base_walker.rb +109 -0
  32. data/lib/rubik/ast/treesitter/c_walker.rb +122 -0
  33. data/lib/rubik/ast/treesitter/go_walker.rb +120 -0
  34. data/lib/rubik/ast/treesitter/python_walker.rb +107 -0
  35. data/lib/rubik/ast/treesitter/rust_walker.rb +130 -0
  36. data/lib/rubik/ast/treesitter/typescript_walker.rb +182 -0
  37. data/lib/rubik/ast/treesitter/zig_walker.rb +109 -0
  38. data/lib/rubik/ast/treesitter_extractor.rb +93 -0
  39. data/lib/rubik/ast/typescript_extractor.rb +283 -0
  40. data/lib/rubik/ast/zig_extractor.rb +116 -0
  41. data/lib/rubik/ast.rb +13 -0
  42. data/lib/rubik/benchmark/runner.rb +224 -0
  43. data/lib/rubik/benchmark/synth_repo.rb +59 -0
  44. data/lib/rubik/benchmark.rb +9 -0
  45. data/lib/rubik/buffer/diff.rb +43 -0
  46. data/lib/rubik/buffer/manager.rb +427 -0
  47. data/lib/rubik/buffer/path_validator.rb +60 -0
  48. data/lib/rubik/buffer/snapshot.rb +99 -0
  49. data/lib/rubik/buffer.rb +10 -0
  50. data/lib/rubik/coeffects.rb +129 -0
  51. data/lib/rubik/concurrency/boundary.rb +74 -0
  52. data/lib/rubik/concurrency/dns_pool.rb +68 -0
  53. data/lib/rubik/concurrency/errors.rb +17 -0
  54. data/lib/rubik/concurrency/event_loop.rb +441 -0
  55. data/lib/rubik/concurrency/join.rb +55 -0
  56. data/lib/rubik/concurrency/process_waiter.rb +55 -0
  57. data/lib/rubik/concurrency/scheduler.rb +84 -0
  58. data/lib/rubik/concurrency/timers.rb +109 -0
  59. data/lib/rubik/concurrency.rb +132 -0
  60. data/lib/rubik/config/deep_ops.rb +80 -0
  61. data/lib/rubik/config/defaults.rb +248 -0
  62. data/lib/rubik/config/model_catalog.rb +93 -0
  63. data/lib/rubik/config/store.rb +155 -0
  64. data/lib/rubik/config.rb +216 -0
  65. data/lib/rubik/context/git_info.rb +84 -0
  66. data/lib/rubik/context/git_lister.rb +37 -0
  67. data/lib/rubik/context/index.rb +134 -0
  68. data/lib/rubik/context/repo_context_reader.rb +121 -0
  69. data/lib/rubik/context/scanner.rb +439 -0
  70. data/lib/rubik/context.rb +11 -0
  71. data/lib/rubik/diagnostics/collector.rb +200 -0
  72. data/lib/rubik/diagnostics/entry.rb +60 -0
  73. data/lib/rubik/diagnostics/parser.rb +107 -0
  74. data/lib/rubik/diagnostics.rb +10 -0
  75. data/lib/rubik/event_bus.rb +56 -0
  76. data/lib/rubik/kernel/dispatcher.rb +171 -0
  77. data/lib/rubik/kernel/dmesg.rb +116 -0
  78. data/lib/rubik/kernel/init.rb +177 -0
  79. data/lib/rubik/kernel/record.rb +85 -0
  80. data/lib/rubik/kernel/unit.rb +160 -0
  81. data/lib/rubik/llm/anthropic.rb +202 -0
  82. data/lib/rubik/llm/chunk_assembler.rb +285 -0
  83. data/lib/rubik/llm/client.rb +526 -0
  84. data/lib/rubik/llm/formatter.rb +450 -0
  85. data/lib/rubik/llm/message_sanitizer.rb +87 -0
  86. data/lib/rubik/llm/ollama.rb +167 -0
  87. data/lib/rubik/llm/openai.rb +21 -0
  88. data/lib/rubik/llm/openai_compatible.rb +216 -0
  89. data/lib/rubik/llm/provider.rb +125 -0
  90. data/lib/rubik/llm/registry.rb +336 -0
  91. data/lib/rubik/llm/router.rb +100 -0
  92. data/lib/rubik/llm.rb +18 -0
  93. data/lib/rubik/log.rb +38 -0
  94. data/lib/rubik/lsp/client.rb +475 -0
  95. data/lib/rubik/lsp/manager.rb +366 -0
  96. data/lib/rubik/lsp.rb +9 -0
  97. data/lib/rubik/mcp/boot_lifecycle.rb +114 -0
  98. data/lib/rubik/mcp/client.rb +169 -0
  99. data/lib/rubik/mcp/errors.rb +11 -0
  100. data/lib/rubik/mcp/manager.rb +619 -0
  101. data/lib/rubik/mcp/proxy_tool.rb +104 -0
  102. data/lib/rubik/mcp/transport/stdio.rb +255 -0
  103. data/lib/rubik/mcp.rb +12 -0
  104. data/lib/rubik/orchestrator/activity_log.rb +75 -0
  105. data/lib/rubik/orchestrator/auto_apply.rb +166 -0
  106. data/lib/rubik/orchestrator/budget_manager.rb +97 -0
  107. data/lib/rubik/orchestrator/continuation_manager.rb +161 -0
  108. data/lib/rubik/orchestrator/finish_message_selector.rb +55 -0
  109. data/lib/rubik/orchestrator/goal_ledger/drift.rb +71 -0
  110. data/lib/rubik/orchestrator/goal_ledger/ledger.rb +399 -0
  111. data/lib/rubik/orchestrator/goal_ledger/observation_projection.rb +179 -0
  112. data/lib/rubik/orchestrator/goal_ledger/patch.rb +268 -0
  113. data/lib/rubik/orchestrator/goal_ledger/schema.rb +123 -0
  114. data/lib/rubik/orchestrator/goal_ledger/validator.rb +361 -0
  115. data/lib/rubik/orchestrator/goal_ledger/verifier.rb +129 -0
  116. data/lib/rubik/orchestrator/goal_ledger/verify_spec.rb +50 -0
  117. data/lib/rubik/orchestrator/goal_ledger.rb +65 -0
  118. data/lib/rubik/orchestrator/goal_state.rb +184 -0
  119. data/lib/rubik/orchestrator/goal_wait.rb +125 -0
  120. data/lib/rubik/orchestrator/loop.rb +388 -0
  121. data/lib/rubik/orchestrator/lsp_feedback.rb +55 -0
  122. data/lib/rubik/orchestrator/maestro_gate.rb +65 -0
  123. data/lib/rubik/orchestrator/provider_recovery.rb +90 -0
  124. data/lib/rubik/orchestrator/provider_resolver.rb +69 -0
  125. data/lib/rubik/orchestrator/shell_deps.rb +85 -0
  126. data/lib/rubik/orchestrator/step_executor.rb +328 -0
  127. data/lib/rubik/orchestrator/stream_manager.rb +118 -0
  128. data/lib/rubik/orchestrator/subagent/activation.rb +65 -0
  129. data/lib/rubik/orchestrator/subagent/budget.rb +108 -0
  130. data/lib/rubik/orchestrator/subagent/completion.rb +61 -0
  131. data/lib/rubik/orchestrator/subagent/concurrency_gate.rb +37 -0
  132. data/lib/rubik/orchestrator/subagent/conversation_id.rb +67 -0
  133. data/lib/rubik/orchestrator/subagent/dependency.rb +398 -0
  134. data/lib/rubik/orchestrator/subagent/instance.rb +384 -0
  135. data/lib/rubik/orchestrator/subagent/lifecycle.rb +118 -0
  136. data/lib/rubik/orchestrator/subagent/messaging.rb +61 -0
  137. data/lib/rubik/orchestrator/subagent/narration.rb +125 -0
  138. data/lib/rubik/orchestrator/subagent/notices.rb +209 -0
  139. data/lib/rubik/orchestrator/subagent/questioner.rb +80 -0
  140. data/lib/rubik/orchestrator/subagent/reaper.rb +98 -0
  141. data/lib/rubik/orchestrator/subagent/resilience.rb +70 -0
  142. data/lib/rubik/orchestrator/subagent/reuse.rb +127 -0
  143. data/lib/rubik/orchestrator/subagent/scoped_registry.rb +79 -0
  144. data/lib/rubik/orchestrator/subagent/step_runner.rb +240 -0
  145. data/lib/rubik/orchestrator/subagent/storage_id.rb +26 -0
  146. data/lib/rubik/orchestrator/subagent/supervisor.rb +327 -0
  147. data/lib/rubik/orchestrator/subagent/transcript_formatter.rb +141 -0
  148. data/lib/rubik/orchestrator/subagent/type_registry.rb +199 -0
  149. data/lib/rubik/orchestrator/subagent_notice_inbox.rb +80 -0
  150. data/lib/rubik/orchestrator/tool_call_id.rb +47 -0
  151. data/lib/rubik/orchestrator/tool_circuit_breaker.rb +91 -0
  152. data/lib/rubik/orchestrator/tool_history.rb +39 -0
  153. data/lib/rubik/orchestrator/turn_executor/subagent_channel.rb +102 -0
  154. data/lib/rubik/orchestrator/turn_executor/user_channel.rb +172 -0
  155. data/lib/rubik/orchestrator/turn_executor.rb +376 -0
  156. data/lib/rubik/orchestrator.rb +11 -0
  157. data/lib/rubik/platform/filesystem.rb +64 -0
  158. data/lib/rubik/platform/io_drain.rb +53 -0
  159. data/lib/rubik/platform/path.rb +210 -0
  160. data/lib/rubik/platform/process.rb +38 -0
  161. data/lib/rubik/platform/sanitize.rb +157 -0
  162. data/lib/rubik/platform.rb +113 -0
  163. data/lib/rubik/plugin/dsl.rb +51 -0
  164. data/lib/rubik/plugin/manager.rb +247 -0
  165. data/lib/rubik/plugin/storage.rb +195 -0
  166. data/lib/rubik/plugin.rb +10 -0
  167. data/lib/rubik/prompt/context_builder.rb +377 -0
  168. data/lib/rubik/prompt/context_compactor.rb +326 -0
  169. data/lib/rubik/prompt/conversation_rows.rb +74 -0
  170. data/lib/rubik/prompt/doctrine.rb +160 -0
  171. data/lib/rubik/prompt/emergency_compactor.rb +81 -0
  172. data/lib/rubik/prompt/services.rb +60 -0
  173. data/lib/rubik/prompt/token_estimator.rb +63 -0
  174. data/lib/rubik/prompt/tool_teachings.rb +78 -0
  175. data/lib/rubik/prompt.rb +13 -0
  176. data/lib/rubik/repair/loop.rb +95 -0
  177. data/lib/rubik/repair.rb +8 -0
  178. data/lib/rubik/repl/ast_warmup.rb +129 -0
  179. data/lib/rubik/repl/command_context.rb +214 -0
  180. data/lib/rubik/repl/command_parser.rb +48 -0
  181. data/lib/rubik/repl/commands/agent.rb +30 -0
  182. data/lib/rubik/repl/commands/agent_context.rb +289 -0
  183. data/lib/rubik/repl/commands/agent_execution.rb +342 -0
  184. data/lib/rubik/repl/commands/agent_models.rb +212 -0
  185. data/lib/rubik/repl/commands/agent_providers.rb +232 -0
  186. data/lib/rubik/repl/commands/base.rb +55 -0
  187. data/lib/rubik/repl/commands/buffer.rb +124 -0
  188. data/lib/rubik/repl/commands/dmesg.rb +87 -0
  189. data/lib/rubik/repl/commands/lsp.rb +235 -0
  190. data/lib/rubik/repl/commands/mcp.rb +194 -0
  191. data/lib/rubik/repl/commands/plugins.rb +208 -0
  192. data/lib/rubik/repl/commands/pruner.rb +71 -0
  193. data/lib/rubik/repl/commands/session.rb +183 -0
  194. data/lib/rubik/repl/commands/skills.rb +111 -0
  195. data/lib/rubik/repl/commands/storage.rb +85 -0
  196. data/lib/rubik/repl/commands/subagents.rb +195 -0
  197. data/lib/rubik/repl/commands/system.rb +85 -0
  198. data/lib/rubik/repl/commands/tasks.rb +120 -0
  199. data/lib/rubik/repl/commands/workspace.rb +443 -0
  200. data/lib/rubik/repl/commands.rb +44 -0
  201. data/lib/rubik/repl/composition.rb +32 -0
  202. data/lib/rubik/repl/config_wiring.rb +142 -0
  203. data/lib/rubik/repl/confirmations.rb +56 -0
  204. data/lib/rubik/repl/hooks.rb +47 -0
  205. data/lib/rubik/repl/input_execution.rb +323 -0
  206. data/lib/rubik/repl/input_loop.rb +213 -0
  207. data/lib/rubik/repl/interrupt_handler.rb +217 -0
  208. data/lib/rubik/repl/lifecycle.rb +410 -0
  209. data/lib/rubik/repl/paste_support.rb +88 -0
  210. data/lib/rubik/repl/pending_notice_queue.rb +60 -0
  211. data/lib/rubik/repl/presentation.rb +132 -0
  212. data/lib/rubik/repl/pruner/brief.rb +101 -0
  213. data/lib/rubik/repl/pruner/gate.rb +279 -0
  214. data/lib/rubik/repl/pruner/harness.rb +236 -0
  215. data/lib/rubik/repl/pruner/rules.rb +316 -0
  216. data/lib/rubik/repl/pruner/service.rb +82 -0
  217. data/lib/rubik/repl/pruner.rb +39 -0
  218. data/lib/rubik/repl/service_catalog.rb +374 -0
  219. data/lib/rubik/repl/setup_factories.rb +66 -0
  220. data/lib/rubik/repl/shell.rb +231 -0
  221. data/lib/rubik/repl/step_formatter.rb +324 -0
  222. data/lib/rubik/repl/tee_io.rb +105 -0
  223. data/lib/rubik/repl/terminal_guard.rb +157 -0
  224. data/lib/rubik/repl/turn_recorder.rb +234 -0
  225. data/lib/rubik/repl.rb +14 -0
  226. data/lib/rubik/runtime/ast_service.rb +78 -0
  227. data/lib/rubik/runtime/context.rb +335 -0
  228. data/lib/rubik/runtime/diagnostic_engine.rb +188 -0
  229. data/lib/rubik/runtime/discovery.rb +342 -0
  230. data/lib/rubik/runtime/fiber.rb +142 -0
  231. data/lib/rubik/runtime/fiber_ordering.rb +52 -0
  232. data/lib/rubik/runtime/fiber_retry_gate.rb +84 -0
  233. data/lib/rubik/runtime/index_service.rb +58 -0
  234. data/lib/rubik/runtime/loop_service.rb +73 -0
  235. data/lib/rubik/runtime/manager.rb +241 -0
  236. data/lib/rubik/runtime/overlay_storage.rb +481 -0
  237. data/lib/rubik/runtime/service_definition.rb +54 -0
  238. data/lib/rubik/runtime/service_registration.rb +33 -0
  239. data/lib/rubik/runtime/task_manager.rb +302 -0
  240. data/lib/rubik/runtime.rb +19 -0
  241. data/lib/rubik/sandbox/driver/base.rb +30 -0
  242. data/lib/rubik/sandbox/driver/bubblewrap.rb +111 -0
  243. data/lib/rubik/sandbox/driver/docker.rb +113 -0
  244. data/lib/rubik/sandbox/driver/podman.rb +98 -0
  245. data/lib/rubik/sandbox/driver/process.rb +190 -0
  246. data/lib/rubik/sandbox/driver.rb +91 -0
  247. data/lib/rubik/sandbox/runner.rb +218 -0
  248. data/lib/rubik/sandbox/shell_resolver.rb +178 -0
  249. data/lib/rubik/sandbox.rb +8 -0
  250. data/lib/rubik/security/blocked_guidance.rb +187 -0
  251. data/lib/rubik/security/classifier/classifier.rb +440 -0
  252. data/lib/rubik/security/classifier/commands/agent-browser.yaml +9 -0
  253. data/lib/rubik/security/classifier/commands/apt.yaml +25 -0
  254. data/lib/rubik/security/classifier/commands/awk.yaml +10 -0
  255. data/lib/rubik/security/classifier/commands/basename.yaml +7 -0
  256. data/lib/rubik/security/classifier/commands/bash-classify.yaml +8 -0
  257. data/lib/rubik/security/classifier/commands/bash.yaml +20 -0
  258. data/lib/rubik/security/classifier/commands/black.yaml +12 -0
  259. data/lib/rubik/security/classifier/commands/brew.yaml +31 -0
  260. data/lib/rubik/security/classifier/commands/builtin.yaml +8 -0
  261. data/lib/rubik/security/classifier/commands/cargo.yaml +114 -0
  262. data/lib/rubik/security/classifier/commands/cat.yaml +7 -0
  263. data/lib/rubik/security/classifier/commands/chgrp.yaml +10 -0
  264. data/lib/rubik/security/classifier/commands/chmod.yaml +10 -0
  265. data/lib/rubik/security/classifier/commands/chown.yaml +10 -0
  266. data/lib/rubik/security/classifier/commands/column.yaml +7 -0
  267. data/lib/rubik/security/classifier/commands/comm.yaml +7 -0
  268. data/lib/rubik/security/classifier/commands/command.yaml +12 -0
  269. data/lib/rubik/security/classifier/commands/cp.yaml +7 -0
  270. data/lib/rubik/security/classifier/commands/crystal.yaml +63 -0
  271. data/lib/rubik/security/classifier/commands/curl.yaml +30 -0
  272. data/lib/rubik/security/classifier/commands/cut.yaml +7 -0
  273. data/lib/rubik/security/classifier/commands/date.yaml +10 -0
  274. data/lib/rubik/security/classifier/commands/df.yaml +7 -0
  275. data/lib/rubik/security/classifier/commands/diff.yaml +7 -0
  276. data/lib/rubik/security/classifier/commands/dig.yaml +7 -0
  277. data/lib/rubik/security/classifier/commands/dirname.yaml +7 -0
  278. data/lib/rubik/security/classifier/commands/docker.yaml +177 -0
  279. data/lib/rubik/security/classifier/commands/du.yaml +7 -0
  280. data/lib/rubik/security/classifier/commands/echo.yaml +7 -0
  281. data/lib/rubik/security/classifier/commands/env.yaml +15 -0
  282. data/lib/rubik/security/classifier/commands/eslint.yaml +12 -0
  283. data/lib/rubik/security/classifier/commands/eval.yaml +8 -0
  284. data/lib/rubik/security/classifier/commands/exec.yaml +12 -0
  285. data/lib/rubik/security/classifier/commands/expand.yaml +7 -0
  286. data/lib/rubik/security/classifier/commands/false.yaml +7 -0
  287. data/lib/rubik/security/classifier/commands/file.yaml +7 -0
  288. data/lib/rubik/security/classifier/commands/find.yaml +25 -0
  289. data/lib/rubik/security/classifier/commands/fmt.yaml +7 -0
  290. data/lib/rubik/security/classifier/commands/fold.yaml +7 -0
  291. data/lib/rubik/security/classifier/commands/free.yaml +7 -0
  292. data/lib/rubik/security/classifier/commands/gawk.yaml +10 -0
  293. data/lib/rubik/security/classifier/commands/gcloud.yaml +979 -0
  294. data/lib/rubik/security/classifier/commands/gh.yaml +615 -0
  295. data/lib/rubik/security/classifier/commands/git.yaml +524 -0
  296. data/lib/rubik/security/classifier/commands/glab-discussion.yaml +21 -0
  297. data/lib/rubik/security/classifier/commands/glab.yaml +807 -0
  298. data/lib/rubik/security/classifier/commands/go.yaml +44 -0
  299. data/lib/rubik/security/classifier/commands/gog.yaml +983 -0
  300. data/lib/rubik/security/classifier/commands/gradle.yaml +92 -0
  301. data/lib/rubik/security/classifier/commands/gradlew.yaml +92 -0
  302. data/lib/rubik/security/classifier/commands/grep.yaml +7 -0
  303. data/lib/rubik/security/classifier/commands/groups.yaml +7 -0
  304. data/lib/rubik/security/classifier/commands/gunzip.yaml +8 -0
  305. data/lib/rubik/security/classifier/commands/gws.yaml +211 -0
  306. data/lib/rubik/security/classifier/commands/gzip.yaml +7 -0
  307. data/lib/rubik/security/classifier/commands/hadolint.yaml +9 -0
  308. data/lib/rubik/security/classifier/commands/halt.yaml +8 -0
  309. data/lib/rubik/security/classifier/commands/head.yaml +7 -0
  310. data/lib/rubik/security/classifier/commands/helm.yaml +129 -0
  311. data/lib/rubik/security/classifier/commands/hostname.yaml +7 -0
  312. data/lib/rubik/security/classifier/commands/id.yaml +7 -0
  313. data/lib/rubik/security/classifier/commands/install.yaml +7 -0
  314. data/lib/rubik/security/classifier/commands/isort.yaml +14 -0
  315. data/lib/rubik/security/classifier/commands/java.yaml +35 -0
  316. data/lib/rubik/security/classifier/commands/javac.yaml +39 -0
  317. data/lib/rubik/security/classifier/commands/join.yaml +7 -0
  318. data/lib/rubik/security/classifier/commands/journalctl.yaml +14 -0
  319. data/lib/rubik/security/classifier/commands/jq.yaml +7 -0
  320. data/lib/rubik/security/classifier/commands/kill.yaml +7 -0
  321. data/lib/rubik/security/classifier/commands/killall.yaml +7 -0
  322. data/lib/rubik/security/classifier/commands/kubectl.yaml +211 -0
  323. data/lib/rubik/security/classifier/commands/less.yaml +7 -0
  324. data/lib/rubik/security/classifier/commands/ln.yaml +7 -0
  325. data/lib/rubik/security/classifier/commands/ls.yaml +7 -0
  326. data/lib/rubik/security/classifier/commands/make.yaml +21 -0
  327. data/lib/rubik/security/classifier/commands/md5sum.yaml +7 -0
  328. data/lib/rubik/security/classifier/commands/mise.yaml +579 -0
  329. data/lib/rubik/security/classifier/commands/mkdir.yaml +8 -0
  330. data/lib/rubik/security/classifier/commands/more.yaml +7 -0
  331. data/lib/rubik/security/classifier/commands/mv.yaml +7 -0
  332. data/lib/rubik/security/classifier/commands/mvn.yaml +83 -0
  333. data/lib/rubik/security/classifier/commands/mvnw.yaml +83 -0
  334. data/lib/rubik/security/classifier/commands/mypy.yaml +9 -0
  335. data/lib/rubik/security/classifier/commands/netstat.yaml +7 -0
  336. data/lib/rubik/security/classifier/commands/nice.yaml +11 -0
  337. data/lib/rubik/security/classifier/commands/nl.yaml +7 -0
  338. data/lib/rubik/security/classifier/commands/node.yaml +20 -0
  339. data/lib/rubik/security/classifier/commands/nohup.yaml +9 -0
  340. data/lib/rubik/security/classifier/commands/npm.yaml +71 -0
  341. data/lib/rubik/security/classifier/commands/npx.yaml +42 -0
  342. data/lib/rubik/security/classifier/commands/nslookup.yaml +7 -0
  343. data/lib/rubik/security/classifier/commands/paste.yaml +7 -0
  344. data/lib/rubik/security/classifier/commands/ping.yaml +7 -0
  345. data/lib/rubik/security/classifier/commands/pip.yaml +33 -0
  346. data/lib/rubik/security/classifier/commands/pip3.yaml +33 -0
  347. data/lib/rubik/security/classifier/commands/pipx.yaml +33 -0
  348. data/lib/rubik/security/classifier/commands/pkill.yaml +7 -0
  349. data/lib/rubik/security/classifier/commands/pnpm.yaml +346 -0
  350. data/lib/rubik/security/classifier/commands/poetry.yaml +97 -0
  351. data/lib/rubik/security/classifier/commands/poweroff.yaml +8 -0
  352. data/lib/rubik/security/classifier/commands/prettier.yaml +13 -0
  353. data/lib/rubik/security/classifier/commands/printenv.yaml +7 -0
  354. data/lib/rubik/security/classifier/commands/printf.yaml +7 -0
  355. data/lib/rubik/security/classifier/commands/ps.yaml +7 -0
  356. data/lib/rubik/security/classifier/commands/pup.yaml +882 -0
  357. data/lib/rubik/security/classifier/commands/pwd.yaml +7 -0
  358. data/lib/rubik/security/classifier/commands/pytest.yaml +7 -0
  359. data/lib/rubik/security/classifier/commands/python.yaml +9 -0
  360. data/lib/rubik/security/classifier/commands/python3.yaml +9 -0
  361. data/lib/rubik/security/classifier/commands/read.yaml +7 -0
  362. data/lib/rubik/security/classifier/commands/readlink.yaml +7 -0
  363. data/lib/rubik/security/classifier/commands/realpath.yaml +7 -0
  364. data/lib/rubik/security/classifier/commands/reboot.yaml +8 -0
  365. data/lib/rubik/security/classifier/commands/rev.yaml +7 -0
  366. data/lib/rubik/security/classifier/commands/rg.yaml +7 -0
  367. data/lib/rubik/security/classifier/commands/rm.yaml +8 -0
  368. data/lib/rubik/security/classifier/commands/rsync.yaml +12 -0
  369. data/lib/rubik/security/classifier/commands/rtk.yaml +140 -0
  370. data/lib/rubik/security/classifier/commands/ruff.yaml +29 -0
  371. data/lib/rubik/security/classifier/commands/rustfmt.yaml +14 -0
  372. data/lib/rubik/security/classifier/commands/scp.yaml +7 -0
  373. data/lib/rubik/security/classifier/commands/sed.yaml +14 -0
  374. data/lib/rubik/security/classifier/commands/seq.yaml +7 -0
  375. data/lib/rubik/security/classifier/commands/sh.yaml +14 -0
  376. data/lib/rubik/security/classifier/commands/sha1sum.yaml +7 -0
  377. data/lib/rubik/security/classifier/commands/sha256sum.yaml +7 -0
  378. data/lib/rubik/security/classifier/commands/shellcheck.yaml +10 -0
  379. data/lib/rubik/security/classifier/commands/shutdown.yaml +8 -0
  380. data/lib/rubik/security/classifier/commands/slack.yaml +113 -0
  381. data/lib/rubik/security/classifier/commands/sleep.yaml +8 -0
  382. data/lib/rubik/security/classifier/commands/snip.yaml +79 -0
  383. data/lib/rubik/security/classifier/commands/sort.yaml +10 -0
  384. data/lib/rubik/security/classifier/commands/ss.yaml +7 -0
  385. data/lib/rubik/security/classifier/commands/ssh.yaml +41 -0
  386. data/lib/rubik/security/classifier/commands/stat.yaml +7 -0
  387. data/lib/rubik/security/classifier/commands/stdbuf.yaml +15 -0
  388. data/lib/rubik/security/classifier/commands/strace.yaml +20 -0
  389. data/lib/rubik/security/classifier/commands/strings.yaml +11 -0
  390. data/lib/rubik/security/classifier/commands/sudo.yaml +24 -0
  391. data/lib/rubik/security/classifier/commands/systemctl.yaml +58 -0
  392. data/lib/rubik/security/classifier/commands/systemd-run.yaml +30 -0
  393. data/lib/rubik/security/classifier/commands/tac.yaml +7 -0
  394. data/lib/rubik/security/classifier/commands/tail.yaml +7 -0
  395. data/lib/rubik/security/classifier/commands/tar.yaml +25 -0
  396. data/lib/rubik/security/classifier/commands/tee.yaml +8 -0
  397. data/lib/rubik/security/classifier/commands/terraform.yaml +70 -0
  398. data/lib/rubik/security/classifier/commands/test.yaml +7 -0
  399. data/lib/rubik/security/classifier/commands/time.yaml +8 -0
  400. data/lib/rubik/security/classifier/commands/timeout.yaml +18 -0
  401. data/lib/rubik/security/classifier/commands/top.yaml +7 -0
  402. data/lib/rubik/security/classifier/commands/touch.yaml +8 -0
  403. data/lib/rubik/security/classifier/commands/tr.yaml +7 -0
  404. data/lib/rubik/security/classifier/commands/traceroute.yaml +7 -0
  405. data/lib/rubik/security/classifier/commands/true.yaml +7 -0
  406. data/lib/rubik/security/classifier/commands/tsc.yaml +16 -0
  407. data/lib/rubik/security/classifier/commands/type.yaml +7 -0
  408. data/lib/rubik/security/classifier/commands/uname.yaml +7 -0
  409. data/lib/rubik/security/classifier/commands/unexpand.yaml +7 -0
  410. data/lib/rubik/security/classifier/commands/uniq.yaml +7 -0
  411. data/lib/rubik/security/classifier/commands/unzip.yaml +10 -0
  412. data/lib/rubik/security/classifier/commands/uptime.yaml +7 -0
  413. data/lib/rubik/security/classifier/commands/uv.yaml +156 -0
  414. data/lib/rubik/security/classifier/commands/wait.yaml +11 -0
  415. data/lib/rubik/security/classifier/commands/wc.yaml +7 -0
  416. data/lib/rubik/security/classifier/commands/wget.yaml +7 -0
  417. data/lib/rubik/security/classifier/commands/whereis.yaml +7 -0
  418. data/lib/rubik/security/classifier/commands/which.yaml +7 -0
  419. data/lib/rubik/security/classifier/commands/whoami.yaml +7 -0
  420. data/lib/rubik/security/classifier/commands/xargs.yaml +26 -0
  421. data/lib/rubik/security/classifier/commands/xmllint.yaml +7 -0
  422. data/lib/rubik/security/classifier/commands/yarn.yaml +72 -0
  423. data/lib/rubik/security/classifier/commands/yes.yaml +7 -0
  424. data/lib/rubik/security/classifier/commands/yq.yaml +10 -0
  425. data/lib/rubik/security/classifier/commands/zip.yaml +8 -0
  426. data/lib/rubik/security/classifier/commands/zsh.yaml +14 -0
  427. data/lib/rubik/security/classifier/database.rb +216 -0
  428. data/lib/rubik/security/classifier/matcher/delegation.rb +190 -0
  429. data/lib/rubik/security/classifier/matcher/options.rb +245 -0
  430. data/lib/rubik/security/classifier/matcher/subcommands.rb +103 -0
  431. data/lib/rubik/security/classifier/matcher.rb +249 -0
  432. data/lib/rubik/security/classifier/models.rb +170 -0
  433. data/lib/rubik/security/classifier/parser.rb +23 -0
  434. data/lib/rubik/security/classifier/parsers/bash.rb +280 -0
  435. data/lib/rubik/security/classifier/parsers/bash_extractors.rb +221 -0
  436. data/lib/rubik/security/classifier/parsers/powershell.rb +185 -0
  437. data/lib/rubik/security/classifier/pwsh_rules.rb +224 -0
  438. data/lib/rubik/security/classifier.rb +25 -0
  439. data/lib/rubik/security/errors.rb +39 -0
  440. data/lib/rubik/security/guard.rb +954 -0
  441. data/lib/rubik/security/redactor.rb +89 -0
  442. data/lib/rubik/security.rb +11 -0
  443. data/lib/rubik/session/manager.rb +331 -0
  444. data/lib/rubik/session/recorder.rb +149 -0
  445. data/lib/rubik/session/replayer.rb +65 -0
  446. data/lib/rubik/session.rb +10 -0
  447. data/lib/rubik/skills/frontmatter.rb +41 -0
  448. data/lib/rubik/skills/manager.rb +223 -0
  449. data/lib/rubik/skills/registry.rb +64 -0
  450. data/lib/rubik/skills/skill.rb +33 -0
  451. data/lib/rubik/skills/storage.rb +77 -0
  452. data/lib/rubik/skills/validator.rb +116 -0
  453. data/lib/rubik/skills.rb +20 -0
  454. data/lib/rubik/storage/folding.rb +23 -0
  455. data/lib/rubik/storage/layout.rb +145 -0
  456. data/lib/rubik/storage/render.rb +97 -0
  457. data/lib/rubik/storage/service.rb +42 -0
  458. data/lib/rubik/storage/store.rb +514 -0
  459. data/lib/rubik/storage/transcript_indexer.rb +116 -0
  460. data/lib/rubik/storage/uri_reader.rb +168 -0
  461. data/lib/rubik/storage/vakanuvis/annal_writer.rb +92 -0
  462. data/lib/rubik/storage/vakanuvis/distill_gate.rb +323 -0
  463. data/lib/rubik/storage/vakanuvis/gate.rb +250 -0
  464. data/lib/rubik/storage/vakanuvis/harness.rb +421 -0
  465. data/lib/rubik/storage/vakanuvis/memory_steward.rb +163 -0
  466. data/lib/rubik/storage/vakanuvis/service.rb +69 -0
  467. data/lib/rubik/storage/vakanuvis/vocabulary.rb +99 -0
  468. data/lib/rubik/storage/vakanuvis/wiki_skeleton.rb +67 -0
  469. data/lib/rubik/storage/vakanuvis.rb +77 -0
  470. data/lib/rubik/storage.rb +29 -0
  471. data/lib/rubik/tools/base.rb +107 -0
  472. data/lib/rubik/tools/builtins/dmesg.rb +74 -0
  473. data/lib/rubik/tools/builtins/exec.rb +293 -0
  474. data/lib/rubik/tools/builtins/files.rb +354 -0
  475. data/lib/rubik/tools/builtins/goal_ledger.rb +89 -0
  476. data/lib/rubik/tools/builtins/grep.rb +281 -0
  477. data/lib/rubik/tools/builtins/interactive.rb +175 -0
  478. data/lib/rubik/tools/builtins/lifecycle.rb +359 -0
  479. data/lib/rubik/tools/builtins/lsp.rb +283 -0
  480. data/lib/rubik/tools/builtins/pruner.rb +88 -0
  481. data/lib/rubik/tools/builtins/runtime.rb +120 -0
  482. data/lib/rubik/tools/builtins/send_message.rb +109 -0
  483. data/lib/rubik/tools/builtins/show_file.rb +84 -0
  484. data/lib/rubik/tools/builtins/skill.rb +72 -0
  485. data/lib/rubik/tools/builtins/storage.rb +232 -0
  486. data/lib/rubik/tools/builtins/subagent.rb +356 -0
  487. data/lib/rubik/tools/builtins/subagent_lifecycle.rb +72 -0
  488. data/lib/rubik/tools/builtins/symbols.rb +209 -0
  489. data/lib/rubik/tools/builtins/synthesis.rb +314 -0
  490. data/lib/rubik/tools/builtins/task.rb +168 -0
  491. data/lib/rubik/tools/builtins/unsynthesize.rb +111 -0
  492. data/lib/rubik/tools/builtins/vakanuvis.rb +161 -0
  493. data/lib/rubik/tools/builtins.rb +30 -0
  494. data/lib/rubik/tools/catalog.rb +70 -0
  495. data/lib/rubik/tools/declaration.rb +90 -0
  496. data/lib/rubik/tools/diagnostic_reporter.rb +283 -0
  497. data/lib/rubik/tools/fuzzy_matcher.rb +172 -0
  498. data/lib/rubik/tools/native_search.rb +180 -0
  499. data/lib/rubik/tools/output.rb +54 -0
  500. data/lib/rubik/tools/preflight_runner.rb +209 -0
  501. data/lib/rubik/tools/registry.rb +247 -0
  502. data/lib/rubik/tools/result.rb +71 -0
  503. data/lib/rubik/tools/services.rb +96 -0
  504. data/lib/rubik/tools/truncator.rb +78 -0
  505. data/lib/rubik/tools/uri_resolver.rb +195 -0
  506. data/lib/rubik/tools.rb +14 -0
  507. data/lib/rubik/ui/approvals.rb +52 -0
  508. data/lib/rubik/ui/banner.rb +43 -0
  509. data/lib/rubik/ui/capability.rb +122 -0
  510. data/lib/rubik/ui/dialog.rb +106 -0
  511. data/lib/rubik/ui/diff_viewer.rb +78 -0
  512. data/lib/rubik/ui/frame.rb +95 -0
  513. data/lib/rubik/ui/markdown_stream.rb +107 -0
  514. data/lib/rubik/ui/panel.rb +98 -0
  515. data/lib/rubik/ui/renderer.rb +206 -0
  516. data/lib/rubik/ui/spinner.rb +120 -0
  517. data/lib/rubik/ui/statusline.rb +134 -0
  518. data/lib/rubik/ui/theme.rb +118 -0
  519. data/lib/rubik/ui.rb +57 -0
  520. data/lib/rubik/uris.rb +101 -0
  521. data/lib/rubik/version.rb +5 -0
  522. data/lib/rubik.rb +65 -0
  523. metadata +770 -0
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "../../tools"
5
+
6
+ module Rubik
7
+ module REPL
8
+ module Commands
9
+ module Lsp
10
+ extend Base
11
+
12
+ # Whole-command synopses: shared by the spine registration (usage:)
13
+ # and the fallback branches that print them, so the two cannot drift.
14
+ USAGE_LSP = "Usage: !lsp <status | on | off | diag [file] | start <lang> | stop [lang] | restart [lang] | servers> [--project|--global]"
15
+ USAGE_SYMBOLS = "Usage: !symbols <name>"
16
+ USAGE_OUTLINE = "Usage: !outline <file_path>"
17
+ USAGE_REFS = "Usage: !refs <symbol_name>"
18
+ USAGE_AST = "Usage: !ast <file_path | symbol_name>"
19
+
20
+ def self.register(shell)
21
+ diag_handler = lambda do |args, ctx|
22
+ target_file = args.first
23
+ if target_file
24
+ diags = ctx.lsp_manager.diagnostics_for(target_file)
25
+ if diags.empty?
26
+ ctx.out.puts "No LSP diagnostics or errors for '#{target_file}'."
27
+ else
28
+ ctx.out.puts "=== LSP Diagnostics for #{target_file} (#{diags.size} issue(s)) ==="
29
+ diags.each do |d|
30
+ sev = d["severity"] == 1 ? "ERROR" : "WARN"
31
+ line = (d.dig("range", "start", "line") || 0) + 1
32
+ ctx.out.puts " • [#{sev}] Line #{line}: #{d['message']}"
33
+ end
34
+ end
35
+ else
36
+ all = ctx.lsp_manager.all_diagnostics
37
+ if all.empty? || all.values.flatten.empty?
38
+ ctx.out.puts "No active LSP diagnostics across workspace."
39
+ else
40
+ all.each do |fpath, diags|
41
+ next if diags.empty?
42
+
43
+ ctx.out.puts "\nFile: #{fpath} (#{diags.size} issue(s)):"
44
+ diags.each do |d|
45
+ sev = d["severity"] == 1 ? "ERROR" : "WARN"
46
+ line = (d.dig("range", "start", "line") || 0) + 1
47
+ ctx.out.puts " • [#{sev}] Line #{line}: #{d['message']}"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ shell.register_command("diagnostics", description: "Display live LSP diagnostics across workspace or for a specific file", &diag_handler)
55
+
56
+ shell.register_command("lsp",
57
+ audience: :both,
58
+ description: "Manage Language Server Protocol (LSP) integration (status|on|off|diag|start|stop|restart|servers)",
59
+ usage: USAGE_LSP) do |args, ctx|
60
+ scope, clean_args = Base.parse_scope(args)
61
+ sub = clean_args.first&.downcase || "status"
62
+
63
+ case sub
64
+ when "status"
65
+ summary = ctx.lsp_manager.status_summary
66
+ ctx.out.puts "=== LSP Integration Status ==="
67
+ ctx.out.puts " • Enabled : #{summary[:enabled]}"
68
+ ctx.out.puts " • Auto Diagnostics : #{summary[:auto_diagnostics]}"
69
+ ctx.out.puts "\nConfigured Language Servers:"
70
+ summary[:servers].each do |lang, info|
71
+ status_mark = if info[:running]
72
+ "🟢 RUNNING"
73
+ else
74
+ (info[:available] ? "⚪ AVAILABLE" : "🔴 NOT FOUND")
75
+ end
76
+ diags_str = info[:running] ? " (#{info[:diagnostics_count]} diagnostics)" : ""
77
+ ctx.out.puts " • #{lang.ljust(8)} : #{info[:server_cmd].ljust(16)} -> #{status_mark}#{diags_str}"
78
+ end
79
+ when "servers"
80
+ summary = ctx.lsp_manager.status_summary
81
+ ctx.out.puts "=== Supported & Configured Language Servers ==="
82
+ summary[:servers].each do |lang, info|
83
+ status_mark = if info[:running]
84
+ "🟢 RUNNING"
85
+ else
86
+ (info[:available] ? "⚪ AVAILABLE" : "🔴 NOT INSTALLED")
87
+ end
88
+ ctx.out.puts " • #{lang.ljust(12)} : #{info[:server_cmd].ljust(35)} -> #{status_mark}"
89
+ end
90
+ when "on", "enable"
91
+ res = ctx.config.set("lsp.enabled", true, persist: scope)
92
+ next if Base.report_persist_failure(ctx, res, scope)
93
+
94
+ ctx.out.puts "LSP integration enabled [#{Base.scope_label(scope)}]."
95
+ when "off", "disable"
96
+ res = ctx.config.set("lsp.enabled", false, persist: scope)
97
+ ctx.lsp_manager.stop_all
98
+ next if Base.report_persist_failure(ctx, res, scope)
99
+
100
+ ctx.out.puts "LSP integration disabled and running servers stopped [#{Base.scope_label(scope)}]."
101
+ when "diag", "diagnostics"
102
+ diag_handler.call(clean_args[1..], ctx)
103
+ when "start"
104
+ lang = clean_args[1]
105
+ if lang.nil? || lang.empty?
106
+ supported = (Rubik::LSP::Client::SERVERS.keys + (ctx.config.fetch("lsp.servers", {})&.keys || [])).map(&:to_s).uniq.sort
107
+ ctx.out.puts "Usage: !lsp start <#{supported.join('|')}>"
108
+ else
109
+ res = ctx.lsp_manager.start_client(lang)
110
+ if res[:ok]
111
+ ctx.out.puts res[:message]
112
+ else
113
+ ctx.out.puts "Error: #{res[:error]}"
114
+ ctx.out.puts "\n💡 Suggestion: #{res[:suggestion]}" if res[:suggestion]
115
+ candidates = Rubik::Runtime::Discovery.lsp_candidates(lang)
116
+ ctx.out.puts "Probed candidates for #{lang}: #{candidates.join(', ')}" unless candidates.empty?
117
+ ctx.out.puts "Tip: Use `repair_runtime_service` (or `fix_service`) to probe or configure an alternative server."
118
+ end
119
+ end
120
+ when "stop"
121
+ lang = clean_args[1]
122
+ if lang.nil? || lang.empty?
123
+ ctx.lsp_manager.stop_all
124
+ ctx.out.puts "Stopped all active LSP servers."
125
+ else
126
+ res = ctx.lsp_manager.stop_client(lang)
127
+ ctx.out.puts res[:ok] ? res[:message] : "Error: #{res[:error]}"
128
+ end
129
+ when "restart"
130
+ lang = clean_args[1]
131
+ if lang.nil? || lang.empty?
132
+ running_langs = ctx.lsp_manager.status_summary[:servers].select { |_, v| v[:running] }.keys
133
+ if running_langs.empty?
134
+ ctx.out.puts "No LSP servers are currently running."
135
+ else
136
+ ctx.lsp_manager.stop_all
137
+ restarted = []
138
+ running_langs.each do |l|
139
+ res = ctx.lsp_manager.start_client(l)
140
+ restarted << l if res[:ok]
141
+ end
142
+ ctx.out.puts "Restarted LSP servers: #{restarted.join(', ')}"
143
+ end
144
+ else
145
+ ctx.lsp_manager.stop_client(lang)
146
+ res = ctx.lsp_manager.start_client(lang)
147
+ if res[:ok]
148
+ ctx.out.puts "Restarted LSP server for #{lang}."
149
+ else
150
+ ctx.out.puts "Error: #{res[:error]}"
151
+ ctx.out.puts "💡 Suggestion: #{res[:suggestion]}" if res[:suggestion]
152
+ end
153
+ end
154
+ else
155
+ ctx.out.puts USAGE_LSP
156
+ end
157
+ end
158
+
159
+ shell.register_command("symbols", description: "Search function, class, struct definitions in AST graph",
160
+ usage: USAGE_SYMBOLS) do |args, ctx|
161
+ query = args.join(" ").strip
162
+ if ctx.ast_graph.symbols.empty? && ctx.indexer_thread&.alive?
163
+ begin
164
+ ctx.indexer_thread.join(0.2)
165
+ rescue
166
+ nil
167
+ end
168
+ end
169
+ if query.empty?
170
+ total = ctx.ast_graph.symbols.size
171
+ ctx.out.puts "AST Symbol Graph (#{total} symbols indexed). #{USAGE_SYMBOLS}"
172
+ else
173
+ tool = Rubik::Tools::SearchSymbolsTool.new(ctx.ast_graph)
174
+ ctx.out.puts tool.execute(query: query)
175
+ end
176
+ end
177
+
178
+ shell.register_command("outline", description: "Show semantic outline (classes/methods) of a source file",
179
+ usage: USAGE_OUTLINE) do |args, ctx|
180
+ target = args.first
181
+ if target.nil? || target.empty?
182
+ ctx.out.puts USAGE_OUTLINE
183
+ else
184
+ full_path = File.expand_path(target, ctx.buffer_manager&.root_dir || Dir.pwd)
185
+ ctx.ast_graph.index_file(full_path) if File.exist?(full_path) && !ctx.ast_graph.file_symbols.key?(full_path)
186
+ tool = Rubik::Tools::GetOutlineTool.new(ctx.ast_graph)
187
+ ctx.out.puts tool.execute(file_path: target)
188
+ end
189
+ end
190
+
191
+ shell.register_command("refs", description: "Find call sites and references of a symbol across codebase",
192
+ usage: USAGE_REFS) do |args, ctx|
193
+ sym = args.first
194
+ if ctx.ast_graph.symbols.empty? && ctx.indexer_thread&.alive?
195
+ begin
196
+ ctx.indexer_thread.join(0.2)
197
+ rescue
198
+ nil
199
+ end
200
+ end
201
+ if sym.nil? || sym.empty?
202
+ ctx.out.puts USAGE_REFS
203
+ else
204
+ tool = Rubik::Tools::FindReferencesTool.new(ctx.ast_graph)
205
+ ctx.out.puts tool.execute(symbol_name: sym)
206
+ end
207
+ end
208
+
209
+ shell.register_command("ast", description: "Inspect AST outline for a file or definition for a symbol",
210
+ usage: USAGE_AST) do |args, ctx|
211
+ target = args.first
212
+ if target.nil? || target.empty?
213
+ ctx.out.puts USAGE_AST
214
+ elsif File.exist?(target)
215
+ full_path = File.expand_path(target, ctx.buffer_manager&.root_dir || Dir.pwd)
216
+ ctx.ast_graph.index_file(full_path) unless ctx.ast_graph.file_symbols.key?(full_path)
217
+ tool = Rubik::Tools::GetOutlineTool.new(ctx.ast_graph)
218
+ ctx.out.puts tool.execute(file_path: target)
219
+ else
220
+ if ctx.ast_graph.symbols.empty? && ctx.indexer_thread&.alive?
221
+ begin
222
+ ctx.indexer_thread.join(0.2)
223
+ rescue
224
+ nil
225
+ end
226
+ end
227
+ tool = Rubik::Tools::GetSymbolTool.new(ctx.ast_graph)
228
+ ctx.out.puts tool.execute(symbol_name: target)
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Rubik
6
+ module REPL
7
+ module Commands
8
+ module Mcp
9
+ extend Base
10
+
11
+ def self.register(shell)
12
+ shell.register_command("mcp",
13
+ audience: :both,
14
+ description: "Manage MCP servers (status|list|add|remove|enable|disable|connect|disconnect|ping)") do |args, ctx|
15
+ manager = ctx.mcp_manager
16
+ unless manager
17
+ ctx.out.puts "MCP manager is not initialized."
18
+ next
19
+ end
20
+
21
+ scope, clean_args = Base.parse_scope(args)
22
+ sub = clean_args.first&.downcase || "status"
23
+
24
+ case sub
25
+ when "status"
26
+ status_servers(ctx, manager)
27
+ when "list"
28
+ list_tools(ctx, manager, clean_args[1])
29
+ when "add"
30
+ name = clean_args[1]
31
+ cmd = clean_args[2]
32
+ server_args = clean_args[3..] || []
33
+ if name.nil? || cmd.nil?
34
+ ctx.out.puts "Usage: !mcp add <name> <command> [args...] [-p|-g]"
35
+ next
36
+ end
37
+ defn = { "command" => cmd, "args" => server_args }
38
+ begin
39
+ res = manager.add_server(name, defn, auto_start: true, persist: scope)
40
+ if res[:ok]
41
+ ctx.out.puts "MCP server '#{name}' added and started (#{res[:tools]} tool(s) registered) [#{Base.scope_label(scope)}]."
42
+ else
43
+ Base.report_persist_failure(ctx, res, scope)
44
+ end
45
+ rescue StandardError => e
46
+ ctx.out.puts "Failed to add/start MCP server '#{name}': #{e.message}"
47
+ end
48
+ when "remove", "rm", "delete"
49
+ name = clean_args[1]
50
+ if name.nil?
51
+ ctx.out.puts "Usage: !mcp remove <name> [-p|-g]"
52
+ next
53
+ end
54
+ begin
55
+ res = manager.remove_server(name, persist: scope)
56
+ if res[:ok]
57
+ ctx.out.puts "MCP server '#{name}' removed [#{Base.scope_label(scope)}]."
58
+ else
59
+ Base.report_persist_failure(ctx, res, scope)
60
+ end
61
+ rescue StandardError => e
62
+ ctx.out.puts "Failed to remove MCP server '#{name}': #{e.message}"
63
+ end
64
+ when "enable", "on", "start"
65
+ name = clean_args[1]
66
+ if name.nil?
67
+ ctx.out.puts "Usage: !mcp enable <name>"
68
+ next
69
+ end
70
+ begin
71
+ manager.enable_server(name)
72
+ tool_count = (manager.tools[name] || []).size
73
+ ctx.out.puts "MCP server '#{name}' enabled (#{tool_count} tool(s) registered)."
74
+ rescue StandardError => e
75
+ ctx.out.puts "Failed to enable MCP server '#{name}': #{e.message}"
76
+ end
77
+ when "disable", "off", "stop"
78
+ name = clean_args[1]
79
+ if name.nil?
80
+ ctx.out.puts "Usage: !mcp disable <name>"
81
+ next
82
+ end
83
+ begin
84
+ manager.disable_server(name)
85
+ ctx.out.puts "MCP server '#{name}' disabled and tools unregistered."
86
+ rescue StandardError => e
87
+ ctx.out.puts "Failed to disable MCP server '#{name}': #{e.message}"
88
+ end
89
+ when "connect"
90
+ name = clean_args[1]
91
+ cmd = clean_args[2]
92
+ server_args = clean_args[3..] || []
93
+ if name.nil? || cmd.nil?
94
+ ctx.out.puts "Usage: !mcp connect <name> <command> [args...]"
95
+ next
96
+ end
97
+ begin
98
+ manager.connect(name, cmd, server_args)
99
+ tool_count = (manager.tools[name] || []).size
100
+ ctx.out.puts "MCP server '#{name}' connected [Session only] (#{tool_count} tool(s) registered)."
101
+ rescue StandardError => e
102
+ ctx.out.puts "Failed to connect MCP server '#{name}': #{e.message}"
103
+ end
104
+ when "disconnect"
105
+ name = clean_args[1]
106
+ if name.nil?
107
+ ctx.out.puts "Usage: !mcp disconnect <name>"
108
+ next
109
+ end
110
+ begin
111
+ manager.disconnect(name)
112
+ ctx.out.puts "MCP server '#{name}' disconnected."
113
+ rescue StandardError => e
114
+ ctx.out.puts "Failed to disconnect MCP server '#{name}': #{e.message}"
115
+ end
116
+ when "ping"
117
+ name = clean_args[1]
118
+ if name.nil?
119
+ ctx.out.puts "Usage: !mcp ping <name>"
120
+ next
121
+ end
122
+ begin
123
+ latency = manager.ping(name)
124
+ ctx.out.puts "MCP server '#{name}' is alive (ping: #{latency} ms)."
125
+ rescue StandardError => e
126
+ ctx.out.puts "Ping failed for MCP server '#{name}': #{e.message}"
127
+ end
128
+ else
129
+ ctx.out.puts "Unknown !mcp action '#{sub}'. Valid actions: status, list, add, remove, enable, disable, connect, disconnect, ping."
130
+ end
131
+ end
132
+ end
133
+
134
+ def self.status_servers(ctx, manager)
135
+ summary = manager.status_summary
136
+ ctx.out.puts "=== Model Context Protocol (MCP) Servers ==="
137
+ ctx.out.puts " • Global Enabled : #{manager.enabled?}"
138
+ ctx.out.puts " • Servers Count : #{summary.size}"
139
+ if summary.empty?
140
+ ctx.out.puts "\nNo MCP servers configured or active."
141
+ ctx.out.puts "Use '!mcp add <name> <command> [args...]' or '!mcp connect <name> <command>' to add one."
142
+ return
143
+ end
144
+
145
+ ctx.out.puts "\nConfigured Servers:"
146
+ summary.each do |srv|
147
+ badge = case srv[:status]
148
+ when :active then "[ACTIVE]"
149
+ when :connecting then "[CONNECTING]"
150
+ when :failed then "[FAILED]"
151
+ else "[DISABLED]"
152
+ end
153
+ ctx.out.puts " • #{srv[:name]} #{badge}"
154
+ ctx.out.puts " Command : #{srv[:command]}"
155
+ ctx.out.puts " Tools : #{srv[:tools_count]} registered"
156
+ end
157
+ end
158
+
159
+ def self.list_tools(ctx, manager, target_server = nil)
160
+ servers_to_check = if target_server
161
+ [target_server.to_s]
162
+ else
163
+ manager.servers.keys
164
+ end
165
+
166
+ tools_found = 0
167
+ servers_to_check.each do |sname|
168
+ tools = manager.tools[sname] || []
169
+ next if tools.empty?
170
+
171
+ ctx.out.puts "=== Tools for MCP Server '#{sname}' (#{tools.size}) ==="
172
+ tools.each do |t|
173
+ tools_found += 1
174
+ ctx.out.puts " • #{t.name} : #{t.description}"
175
+ props = t.params_schema["properties"] || {}
176
+ unless props.empty?
177
+ params_str = props.keys.join(", ")
178
+ ctx.out.puts " Parameters: #{params_str}"
179
+ end
180
+ end
181
+ end
182
+
183
+ return unless tools_found.zero?
184
+
185
+ if target_server
186
+ ctx.out.puts "No active tools found for MCP server '#{target_server}'."
187
+ else
188
+ ctx.out.puts "No active MCP tools across servers."
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ module REPL
5
+ module Commands
6
+ module Plugins
7
+ # Whole-command synopsis: shared by the spine registration (usage:)
8
+ # and the unknown-subcommand fallback branch, so the two cannot drift.
9
+ USAGE_PLUGIN = "Kullanım: !plugin [list|add|remove|reload|enable|disable|info]"
10
+
11
+ def self.register(shell)
12
+ handler = proc do |args, ctx|
13
+ manager = ctx.plugin_manager
14
+ unless manager
15
+ ctx.out.puts "Eklenti yöneticisi bulunamadı."
16
+ next
17
+ end
18
+
19
+ storage = ctx.plugin_storage
20
+
21
+ args = args.split if args.is_a?(String)
22
+ subcommand = args[0] || "list"
23
+
24
+ case subcommand
25
+ when "list"
26
+ list_plugins(ctx, manager, storage)
27
+ when "add", "install"
28
+ add_plugin(ctx, manager, storage, args[1], args[2..] || [])
29
+ when "remove", "uninstall"
30
+ remove_plugin(ctx, manager, storage, args[1], args[2..] || [])
31
+ when "reload"
32
+ reload_plugin(ctx, manager, storage, args[1])
33
+ when "enable"
34
+ enable_plugin(ctx, manager, args[1])
35
+ when "disable"
36
+ disable_plugin(ctx, manager, args[1])
37
+ when "info"
38
+ info_plugin(ctx, manager, args[1])
39
+ else
40
+ ctx.out.puts "Bilinmeyen komut: #{subcommand}. #{USAGE_PLUGIN}"
41
+ end
42
+ end
43
+
44
+ shell.register_command("plugin", description: "Eklenti yönetimi: list, add, remove, reload, enable, disable, info",
45
+ usage: USAGE_PLUGIN, audience: :human, &handler)
46
+ shell.register_command("plugins", description: "Yüklü eklentileri listeler", usage: USAGE_PLUGIN, audience: :human) do |_args, ctx|
47
+ handler.call(["list"], ctx)
48
+ end
49
+ end
50
+
51
+ def self.list_plugins(ctx, manager, storage)
52
+ ctx.out.puts "🧩 Eklentiler (Plugins)"
53
+ ctx.out.puts "======================"
54
+
55
+ if manager.registered_plugins.empty? && (!storage || storage.discover_plugins.empty?)
56
+ ctx.out.puts "Yüklü eklenti yok."
57
+ else
58
+ manager.registered_plugins.each do |gem_name|
59
+ component_name = "plugin_#{gem_name}"
60
+ fiber = ctx.runtime_manager&.fibers&.[](component_name)
61
+ state = fiber ? fiber.state : "unknown"
62
+ ctx.out.puts "- #{gem_name} [#{state}]"
63
+ end
64
+ end
65
+
66
+ if storage
67
+ dropins = storage.discover_plugins
68
+ unless dropins.empty?
69
+ ctx.out.puts "\n📂 Drop-in Eklentileri"
70
+ ctx.out.puts "======================"
71
+ dropins.each do |meta|
72
+ component_name = "plugin_#{meta[:name]}"
73
+ fiber = ctx.runtime_manager&.fibers&.[](component_name)
74
+ state = fiber ? fiber.state : "inactive"
75
+ ctx.out.puts "- #{meta[:name]} [#{state}] (#{meta[:scope]} - #{meta[:type]})"
76
+ end
77
+ end
78
+ end
79
+
80
+ ctx.out.puts "\n🔗 Alias Tablosu"
81
+ ctx.out.puts "================"
82
+ if manager.aliases.empty?
83
+ ctx.out.puts "Kayıtlı alias yok."
84
+ else
85
+ manager.aliases.each do |short, full|
86
+ ctx.out.puts "- !#{short} -> !#{full}"
87
+ end
88
+ end
89
+ end
90
+
91
+ def self.add_plugin(ctx, manager, storage, path, flags)
92
+ unless storage
93
+ ctx.out.puts "Hata: Drop-in eklenti motoru aktif değil."
94
+ return
95
+ end
96
+
97
+ unless path
98
+ ctx.out.puts "Hata: Eklenti yolu belirtilmedi. Kullanım: !plugin add <path>"
99
+ return
100
+ end
101
+
102
+ scope = flags.include?("--global") ? :global : :project
103
+ result = storage.add_plugin(path, scope: scope)
104
+
105
+ if result[:ok]
106
+ ctx.out.puts "✅ Eklenti #{result[:name]} (#{scope}) başarıyla eklendi."
107
+
108
+ # Build a proper plugin_meta for load_plugin_file:
109
+ # storage.add_plugin returns {ok, name, path, type} but
110
+ # load_plugin_file needs {name, path, load_path} where path
111
+ # is the entry .rb file and load_path is the directory for
112
+ # $LOAD_PATH.
113
+ plugin_meta = build_plugin_meta(result, scope)
114
+ if manager.load_plugin_file(plugin_meta, runtime_manager: ctx.runtime_manager)
115
+ ctx.out.puts "🚀 Eklenti #{result[:name]} canlı olarak yüklendi."
116
+ else
117
+ ctx.out.puts "⚠️ Eklenti #{result[:name]} dosyası kopyalandı ancak yüklenemedi."
118
+ end
119
+ else
120
+ ctx.out.puts "❌ Hata: #{result[:error]}"
121
+ end
122
+ end
123
+
124
+ def self.remove_plugin(ctx, manager, storage, name, flags)
125
+ unless storage
126
+ ctx.out.puts "Hata: Drop-in eklenti motoru aktif değil."
127
+ return
128
+ end
129
+ unless name
130
+ ctx.out.puts "Hata: Eklenti adı belirtilmedi."
131
+ return
132
+ end
133
+
134
+ scope = flags.include?("--global") ? :global : :project
135
+
136
+ unloaded = manager.unload_plugin(name, runtime_manager: ctx.runtime_manager)
137
+ ctx.out.puts "🧹 Eklenti #{name} fiber'ı sonlandırıldı (ters kayıtlar işletildi)." if unloaded
138
+
139
+ result = storage.remove_plugin(name, scope: scope)
140
+ if result[:ok]
141
+ ctx.out.puts "🗑️ Eklenti #{name} diskten (#{scope}) silindi."
142
+ else
143
+ ctx.out.puts "⚠️ Eklenti diskten silinemedi: #{result[:error]}"
144
+ end
145
+ end
146
+
147
+ def self.reload_plugin(ctx, manager, storage, name)
148
+ unless storage
149
+ ctx.out.puts "Hata: Drop-in eklenti motoru aktif değil."
150
+ return
151
+ end
152
+ unless name
153
+ ctx.out.puts "Hata: Eklenti adı belirtilmedi."
154
+ return
155
+ end
156
+
157
+ if manager.reload_plugin(name, storage: storage, runtime_manager: ctx.runtime_manager)
158
+ ctx.out.puts "🔄 Eklenti #{name} başarıyla yeniden yüklendi (zero-downtime)."
159
+ else
160
+ ctx.out.puts "❌ Hata: Eklenti #{name} yeniden yüklenemedi."
161
+ end
162
+ end
163
+
164
+ def self.enable_plugin(ctx, _manager, name)
165
+ # Not implemented deeply for now, simply unpauses fiber
166
+ ctx.out.puts "Eklenti #{name} etkinleştirildi (placeholder)."
167
+ end
168
+
169
+ def self.disable_plugin(ctx, _manager, name)
170
+ ctx.out.puts "Eklenti #{name} devre dışı bırakıldı (placeholder)."
171
+ end
172
+
173
+ def self.info_plugin(ctx, _manager, name)
174
+ if Rubik::Plugin::Registry.get(name)
175
+ def_obj = Rubik::Plugin::Registry.get(name)
176
+ ctx.out.puts "Eklenti: #{def_obj.name} v#{def_obj.version}"
177
+ ctx.out.puts "Açıklama: #{def_obj.description}"
178
+ ctx.out.puts "Bağımlılıklar (inject): #{def_obj.inject.join(', ')}"
179
+ else
180
+ ctx.out.puts "Eklenti bilgisi bulunamadı."
181
+ end
182
+ end
183
+
184
+ # Translate storage.add_plugin result into load_plugin_file meta.
185
+ # storage returns { ok:, name:, path:, type: } where :path is the
186
+ # dest directory for :directory plugins or the .rb file for :file
187
+ # plugins. load_plugin_file needs { name:, path: (entry .rb),
188
+ # load_path: (dir for $LOAD_PATH) }.
189
+ def self.build_plugin_meta(result, scope)
190
+ name = result[:name]
191
+ if result[:type] == :directory
192
+ dir = result[:path]
193
+ lib_dir = File.join(dir, "lib")
194
+ load_path = Dir.exist?(lib_dir) ? lib_dir : dir
195
+
196
+ entry_file = File.join(dir, "#{name}.rb")
197
+ entry_file = File.join(dir, "lib", "#{name}.rb") unless File.exist?(entry_file)
198
+
199
+ { name: name, path: entry_file, dir: dir, load_path: load_path, scope: scope, type: :directory }
200
+ else
201
+ dir = File.dirname(result[:path])
202
+ { name: name, path: result[:path], dir: dir, load_path: dir, scope: scope, type: :file }
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end