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,374 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../coeffects"
4
+ require_relative "../event_bus" unless defined?(Rubik::EventBus)
5
+ require_relative "../orchestrator/subagent/supervisor"
6
+ require_relative "../runtime/service_definition"
7
+
8
+ module Rubik
9
+ module REPL
10
+ # The ONE ordered, declarative catalog of everything Composition builds
11
+ # and the Shell publishes (architecture-review Candidate 05). Entry order
12
+ # IS construction order — the historical Shell#initialize sequence, which
13
+ # is load-bearing: the shared guard is built BEFORE every consumer
14
+ # (buffer, mcp, sandbox runner, task manager) so they all share the same
15
+ # instance; runtime_manager precedes task_manager / skills / mcp, which
16
+ # wire into its context; buffer precedes the subagent supervisor.
17
+ #
18
+ # owns : the entries (builders, provide keys, optional/reader flags,
19
+ # inject hooks) and the load-time derivations — Parts struct
20
+ # members, shell reader names, the build fold, wire provide rows
21
+ # leaves : key STRINGS and legacy aliases (Rubik::Coeffects SSOT —
22
+ # entries reference the constants); shell-identity phases
23
+ # (register shims, tool registry, UI, LLM route — lifecycle.rb);
24
+ # override-kwarg resolution and root resolution (composition.rb);
25
+ # tool-layer fallback defaults (Tools::Services)
26
+ #
27
+ # Adding a runtime service = its file + barrel require + a Coeffects key
28
+ # constant + ONE entry here. Parts members, the Shell ivar, the public
29
+ # reader and the wire provide all derive from that entry at load time;
30
+ # test_service_catalog.rb locks catalog <-> Parts <-> readers <->
31
+ # provides <-> Coeffects consistency so the derivation cannot drift.
32
+ module ServiceCatalog
33
+ # The fold scope: root inputs, caller-supplied overrides, and hash
34
+ # access to already-constructed entries. Service constants referenced
35
+ # inside builders resolve lazily (at fold time), so catalog load order
36
+ # carries no construction coupling.
37
+ class Scope
38
+ attr_reader :root_dir, :built
39
+
40
+ def initialize(root_dir:, overrides:)
41
+ @root_dir = root_dir
42
+ @overrides = overrides
43
+ @built = {}
44
+ end
45
+
46
+ def override_for(name)
47
+ @overrides[name]
48
+ end
49
+
50
+ def store(name, instance)
51
+ @built[name] = instance
52
+ end
53
+
54
+ # Forward-reference guard: a builder reaching for a service the fold
55
+ # has not produced yet fails here, loudly, instead of silently
56
+ # threading nil downstream. Overrides are visible immediately (an
57
+ # injected part IS the value its entry will adopt).
58
+ def [](name)
59
+ return @built[name] if @built.key?(name)
60
+ return @overrides[name] if @overrides.key?(name)
61
+
62
+ raise KeyError, "ServiceCatalog: :#{name} is not constructed yet — ordered later in the catalog (or missing)"
63
+ end
64
+
65
+ # instance_variable_get (not the home_dir reader) is kept verbatim
66
+ # from the pre-catalog Shell#initialize: it tolerates duck-typed
67
+ # configs without the reader.
68
+ def home_dir
69
+ @home_dir ||= self[:config]&.instance_variable_get(:@home_dir) || ENV.fetch("HOME", nil)
70
+ end
71
+ end
72
+
73
+ DEFINITION = Rubik::Runtime::ServiceDefinition
74
+
75
+ ENTRIES = [
76
+ DEFINITION.new(
77
+ name: :config,
78
+ builder: ->(s) { Rubik::Config.new(root_dir: s.root_dir) },
79
+ provide: Rubik::Coeffects::CONFIG
80
+ ),
81
+ DEFINITION.new(
82
+ name: :dmesg,
83
+ builder: ->(_s) { Rubik::Kernel::Dmesg.new },
84
+ provide: Rubik::Coeffects::DMESG
85
+ ),
86
+ DEFINITION.new(
87
+ name: :dispatcher,
88
+ builder: ->(s) { Rubik::Kernel::Dispatcher.new(dmesg: s[:dmesg]) },
89
+ provide: Rubik::Coeffects::DISPATCHER,
90
+ after_inject: lambda do |values, _parts, _injected|
91
+ dispatcher = values.fetch(:dispatcher)
92
+ values[:runtime_manager]&.context&.on_dispose { dispatcher.stop }
93
+ end
94
+ ),
95
+ DEFINITION.new(
96
+ name: :event_bus,
97
+ builder: ->(s) { Rubik::EventBus.new(dispatcher: s[:dispatcher]) },
98
+ provide: Rubik::Coeffects::EVENTS,
99
+ after_build: ->(s) { s[:config].event_bus = s[:event_bus] if s[:config].respond_to?(:event_bus=) }
100
+ ),
101
+ DEFINITION.new(name: :extra_roots, input: true,
102
+ builder: ->(s) { Array(s[:config].fetch("workspace.additional_roots", [])).compact }),
103
+ DEFINITION.new(name: :allowed_ext_dirs, input: true,
104
+ builder: ->(s) { Array(s[:config].fetch("workspace.allowed_external_dirs", [])).compact }),
105
+ DEFINITION.new(name: :allow_ext_writes, input: true,
106
+ builder: ->(s) { s[:config].fetch("workspace.allow_external_writes", false) || false }),
107
+ # The ONE operator-policy guard (security.*) — built BEFORE every
108
+ # consumer below (buffer, mcp, sandbox runner, task manager) so they
109
+ # all share this instance. Live in-place mutation from !allow-cmd /
110
+ # !config (repl/config_wiring.rb) then reaches every verification
111
+ # path in the same session without restarts.
112
+ DEFINITION.new(
113
+ name: :guard,
114
+ builder: ->(s) { Rubik::Security::Guard.for_config(s[:config]) },
115
+ provide: Rubik::Coeffects::GUARD
116
+ ),
117
+ DEFINITION.new(
118
+ name: :buffer_manager,
119
+ builder: lambda do |s|
120
+ Rubik::Buffer::Manager.new(
121
+ root_dir: s.root_dir, additional_roots: s[:extra_roots],
122
+ allowed_external_dirs: s[:allowed_ext_dirs], allow_external_writes: s[:allow_ext_writes],
123
+ event_bus: s[:event_bus], config: s[:config], guard: s[:guard]
124
+ )
125
+ end,
126
+ provide: Rubik::Coeffects::BUFFER_MANAGER,
127
+ after_inject: lambda do |values, parts, injected_buffer|
128
+ next unless injected_buffer
129
+
130
+ # Injected-buffer post-wiring runs only on the buffer_manager
131
+ # kwarg route, where Composition did not own the manager's config.
132
+ bm = values.fetch(:buffer_manager)
133
+ (parts.extra_roots || []).each { |r| bm.add_root(r) }
134
+ (parts.allowed_ext_dirs || []).each { |d| bm.add_allowed_dir(d) if bm.respond_to?(:add_allowed_dir) }
135
+ bm.allow_external_writes = parts.allow_ext_writes if bm.respond_to?(:allow_external_writes=)
136
+ end
137
+ ),
138
+ DEFINITION.new(
139
+ name: :snapshot_store,
140
+ builder: lambda do |s|
141
+ Rubik::Buffer::SnapshotStore.new(storage_dir: File.join(s.home_dir, ".rubik", "snapshots",
142
+ File.basename(s.root_dir)))
143
+ end,
144
+ provide: Rubik::Coeffects::SNAPSHOT_STORE
145
+ ),
146
+ DEFINITION.new(
147
+ name: :cache_store,
148
+ builder: ->(s) { Rubik::AST::CacheStore.new(root_dir: s.root_dir, home_dir: s.home_dir) }
149
+ ),
150
+ DEFINITION.new(
151
+ name: :ast_graph,
152
+ # Provided into the context later, by Runtime::ASTService (Shell
153
+ # phase 4) — not by wire_runtime_context.
154
+ builder: ->(_s) { Rubik::AST::Graph.new },
155
+ # Sync load: cache is small (no bodies), symbols instantly
156
+ # available; the expensive walk/extract stays in the background
157
+ # warm-up phase.
158
+ after_inject: lambda do |values, _parts, _injected|
159
+ values.fetch(:ast_graph).load_from_cache(values.fetch(:cache_store))
160
+ end
161
+ ),
162
+ # Provided into the context later, by Runtime::IndexService (Shell
163
+ # phase 4) — not by wire_runtime_context.
164
+ DEFINITION.new(
165
+ name: :context_index,
166
+ builder: lambda do |s|
167
+ includes = Array(s[:config]&.fetch("context.include", [])).compact
168
+ excludes = Array(s[:config]&.fetch("context.exclude", [])).compact
169
+ Rubik::Context::Index.new(
170
+ root_dir: s.root_dir,
171
+ additional_roots: (s[:config].fetch("workspace.auto_index_additional", true) ? s[:extra_roots] : []),
172
+ includes: includes,
173
+ excludes: excludes,
174
+ native: s[:config]&.fetch("tools.native")
175
+ )
176
+ end
177
+ ),
178
+ DEFINITION.new(
179
+ name: :recorder,
180
+ # Absorb (storage.absorb_artifacts, default true): yeni oturumlar
181
+ # storage köküne yazılır — worktree .rubik/'sü temiz kalır; okuma
182
+ # tarafı (Session::Manager) her iki konumu da tarar.
183
+ builder: lambda { |s|
184
+ Rubik::Session::Recorder.new(root_dir: s.root_dir, home_dir: s.home_dir,
185
+ sessions_dir: Rubik::Storage::Layout.sessions_dir_for(s.root_dir, s[:config]))
186
+ }
187
+ ),
188
+ DEFINITION.new(
189
+ name: :adapter_registry,
190
+ builder: ->(_s) { Rubik::Adapter::Registry.new },
191
+ provide: Rubik::Coeffects::ADAPTER_REGISTRY
192
+ ),
193
+ DEFINITION.new(name: :llm_registry, builder: ->(s) { Rubik::LLM::Registry.new(s[:config]) }),
194
+ DEFINITION.new(
195
+ name: :router,
196
+ builder: ->(s) { Rubik::LLM::Router.new(config: s[:config], registry: s[:llm_registry]) },
197
+ provide: Rubik::Coeffects::ROUTER
198
+ ),
199
+ # The context owner every later entry wires into; provided to tools
200
+ # by constructor injection, never as a coeffect.
201
+ DEFINITION.new(name: :runtime_manager, builder: ->(_s) { Rubik::Runtime::Manager.new }),
202
+ DEFINITION.new(name: :overlay_storage,
203
+ builder: ->(s) { Rubik::Runtime::OverlayStorage.new(root_dir: s.root_dir, home_dir: s.home_dir, guard: s[:guard]) }),
204
+ DEFINITION.new(
205
+ name: :lsp_manager,
206
+ builder: ->(s) { Rubik::LSP::Manager.new(config: s[:config], root_dir: s.root_dir) },
207
+ provide: Rubik::Coeffects::LSP_MANAGER,
208
+ after_inject: lambda do |values, _parts, _injected|
209
+ lsp = values.fetch(:lsp_manager)
210
+ values[:runtime_manager]&.context&.on_dispose { lsp&.stop_all }
211
+ end,
212
+ optional: true
213
+ ),
214
+ DEFINITION.new(
215
+ name: :mcp_manager,
216
+ builder: lambda do |s|
217
+ Rubik::MCP::Manager.new(config: s[:config], root_dir: s.root_dir,
218
+ runtime_manager: s[:runtime_manager], guard: s[:guard])
219
+ end,
220
+ provide: Rubik::Coeffects::MCP_MANAGER,
221
+ after_inject: lambda do |values, _parts, _injected|
222
+ mcp = values.fetch(:mcp_manager)
223
+ values[:runtime_manager]&.context&.on_dispose { mcp&.stop_all }
224
+ end,
225
+ optional: true
226
+ ),
227
+ DEFINITION.new(
228
+ name: :skill_manager,
229
+ builder: lambda do |s|
230
+ Rubik::Skills::Manager.new(config: s[:config], root_dir: s.root_dir, home_dir: s.home_dir,
231
+ runtime_context: s[:runtime_manager].context)
232
+ end,
233
+ provide: Rubik::Coeffects::SKILL_MANAGER,
234
+ optional: true
235
+ ),
236
+ # Provide key deliberately differs from the member name: third
237
+ # parties resolve "sandbox" (ctx.require / Coeffects bridge).
238
+ DEFINITION.new(
239
+ name: :sandbox_runner,
240
+ builder: lambda do |s|
241
+ Rubik::Sandbox::Runner.new(root_dir: s.root_dir, config: s[:config],
242
+ allowed_external_dirs: s[:allowed_ext_dirs], guard: s[:guard])
243
+ end,
244
+ provide: Rubik::Coeffects::SANDBOX_RUNNER
245
+ ),
246
+ DEFINITION.new(
247
+ name: :plugin_manager,
248
+ builder: ->(_s) { Rubik::Plugin::Manager.new },
249
+ provide: Rubik::Coeffects::PLUGIN_MANAGER,
250
+ reader: false
251
+ ),
252
+ DEFINITION.new(
253
+ name: :plugin_storage,
254
+ builder: lambda do |s|
255
+ defined?(Rubik::Plugin::Storage) ? Rubik::Plugin::Storage.new(root_dir: s.root_dir, home_dir: s.home_dir) : nil
256
+ end,
257
+ provide: Rubik::Coeffects::PLUGIN_STORAGE,
258
+ provide_from: :parts,
259
+ assign_ivar: false
260
+ ),
261
+ DEFINITION.new(
262
+ name: :task_manager,
263
+ builder: lambda do |s|
264
+ Rubik::Runtime::TaskManager.new(guard: s[:guard], context: s[:runtime_manager].context, root_dir: s.root_dir)
265
+ end,
266
+ provide: Rubik::Coeffects::TASK_MANAGER,
267
+ optional: true
268
+ ),
269
+ DEFINITION.new(
270
+ name: :subagent_supervisor,
271
+ builder: lambda do |s|
272
+ Rubik::Orchestrator::Subagent::Supervisor.new(
273
+ root_context: s[:runtime_manager].context,
274
+ buffer_manager: s[:buffer_manager],
275
+ tool_registry: nil,
276
+ provider: nil,
277
+ router: s[:router],
278
+ config: s[:config],
279
+ root_dir: s.root_dir,
280
+ event_bus: s[:event_bus]
281
+ )
282
+ end,
283
+ provide: Rubik::Coeffects::SUBAGENT_SUPERVISOR,
284
+ optional: true
285
+ ),
286
+ DEFINITION.new(
287
+ name: :init,
288
+ builder: lambda do |s|
289
+ Rubik::Kernel::Init.new(
290
+ context: s[:runtime_manager]&.context,
291
+ dmesg: s[:dmesg],
292
+ dispatcher: s[:dispatcher]
293
+ )
294
+ end,
295
+ provide: Rubik::Coeffects::INIT,
296
+ after_inject: lambda do |values, _parts, _injected|
297
+ init = values.fetch(:init)
298
+ values[:runtime_manager]&.context&.on_dispose { init.stop }
299
+ values[:task_manager].init = init if values[:task_manager].respond_to?(:init=)
300
+ values[:subagent_supervisor].init = init if values[:subagent_supervisor].respond_to?(:init=)
301
+ end
302
+ ),
303
+ # The shell identity itself — provided at wire time, never built.
304
+ DEFINITION.new(
305
+ name: :shell,
306
+ parts_member: false,
307
+ builder: nil,
308
+ provide: Rubik::Coeffects::SHELL,
309
+ provide_from: :self
310
+ )
311
+ ].each(&:freeze).freeze
312
+
313
+ class << self
314
+ def member_names
315
+ ENTRIES.reject { |e| e.parts_member == false }.map(&:name)
316
+ end
317
+
318
+ def parts_struct
319
+ Struct.new(*member_names, keyword_init: true)
320
+ end
321
+
322
+ def injectable
323
+ ENTRIES.select(&:injectable?)
324
+ end
325
+
326
+ def reader_names
327
+ ENTRIES.select(&:expose_reader?).map(&:name)
328
+ end
329
+
330
+ def provided
331
+ ENTRIES.select(&:provided?)
332
+ end
333
+
334
+ def wire_after_inject(values, parts = nil, injected_buffer: false)
335
+ rm = values[:runtime_manager]
336
+ unless rm&.instance_variable_get(:@_catalog_after_injected)
337
+ rm&.instance_variable_set(:@_catalog_after_injected, true)
338
+ injectable.each { |e| e.after_inject&.call(values, parts, injected_buffer) }
339
+ end
340
+ injectable.find { |e| e.name == :buffer_manager }&.after_inject&.call(values, parts, true) if injected_buffer
341
+ end
342
+
343
+ # The construction fold. Caller-supplied overrides (config, event_bus,
344
+ # buffer_manager, snapshot_store kwargs) adopt their entry verbatim;
345
+ # everything else runs its builder with the scope. after_build hooks
346
+ # run per-entry (the config.event_bus cross-wire).
347
+ def build(scope)
348
+ ENTRIES.each do |entry|
349
+ next if entry.parts_member == false
350
+
351
+ scope.store(entry.name, scope.override_for(entry.name) || entry.builder&.call(scope))
352
+ entry.after_build&.call(scope)
353
+ end
354
+ wire_after_inject(scope.built)
355
+ scope.built
356
+ end
357
+
358
+ # Wire provide rows, in catalog order. Order is inert at wire time:
359
+ # the runtime manager holds zero registered fibers until phase 4
360
+ # (register_runtime_services) and phase 3 (load_plugins) run — every
361
+ # reconcile_all inside Manager#provide iterates an empty fiber map.
362
+ def wire_rows(shell, parts)
363
+ provided.to_h do |entry|
364
+ [entry.provide, case entry.provide_from
365
+ when :ivar then shell.instance_variable_get(:"@#{entry.name}")
366
+ when :parts then parts.respond_to?(entry.name) ? parts[entry.name] : nil
367
+ when :self then shell
368
+ end]
369
+ end
370
+ end
371
+ end
372
+ end
373
+ end
374
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ module REPL
5
+ # Öneri 3 (Kademe 3): Shell'den ayrılan kurulum fabrikaları. build_tool_registry
6
+ # default araç kaydını enjekte edilen alt sistemlerle kurar; orchestrator_loop
7
+ # aktif loop fiber'ını erişir (yoksa / farklı opts ile yeniden kurar);
8
+ # build_orchestrator_loop route/provider çözümlemesiyle LoopService.build_loop
9
+ # fabrikasını çalıştırır.
10
+ # Durum Shell'de yaşar (@buffer_manager, @context_index, @ast_graph,
11
+ # @adapter_registry, @guard, @sandbox_runner, @overlay_storage,
12
+ # @runtime_manager, @lsp_manager, @config, @router); mixin yalnızca
13
+ # fabrika politikasını kodlar.
14
+ module SetupFactories
15
+ # Öneri 3 (Kademe 3): build_tool_registry / orchestrator_loop /
16
+ # build_orchestrator_loop, lib/rubik/repl/setup_factories.rb mixin'ine
17
+ # taşındı (araç kaydı kurulumu ve orchestrator loop fabrikaları).
18
+
19
+ def build_tool_registry
20
+ # Service slots (guard, adapters, ast, tasks, sandbox, supervisor)
21
+ # resolve through Tools::Services' triple chain — the wire phase
22
+ # already provided the session's own instances. context_index stays
23
+ # an explicit override: RunTestsTool consumes the RAW kwarg for
24
+ # language detection, which must be the session's index on this
25
+ # path (a nil silently defaults every project to the ruby adapter).
26
+ Rubik::Tools::Registry.build_default(
27
+ buffer_manager: @buffer_manager, context_index: @context_index,
28
+ shell: self, runtime_manager: @runtime_manager,
29
+ overlay_storage: @overlay_storage, lsp_manager: @lsp_manager,
30
+ config: @config, root_dir: Dir.pwd
31
+ )
32
+ end
33
+
34
+ def orchestrator_loop(opts = {})
35
+ fiber = @runtime_manager&.fibers&.[]("orchestrator_loop")
36
+ return fiber.instance if fiber && fiber.state == :active && fiber.instance && opts.empty?
37
+
38
+ build_orchestrator_loop(opts)
39
+ end
40
+
41
+ def build_orchestrator_loop(opts = {})
42
+ routed = if opts[:provider]
43
+ nil
44
+ else
45
+ begin
46
+ @router.route(task_type: opts[:task_type] || :auto, prompt: opts[:prompt])
47
+ rescue => e
48
+ Rubik::Platform.debug_warn("orchestrator loop route unavailable (coeffect fallback): #{e.class}: #{e.message}")
49
+ nil
50
+ end
51
+ end
52
+ provider = opts[:provider] || routed&.fetch(:provider, nil) ||
53
+ (@runtime_manager&.context.respond_to?(:get) ? Rubik::Coeffects.get(@runtime_manager.context, Rubik::Coeffects::LLM_CLIENT) : nil)
54
+ return nil unless provider
55
+
56
+ merged = opts.merge(
57
+ provider: provider,
58
+ model: opts[:model] || routed&.fetch(:model, nil),
59
+ context_limit: opts[:context_limit] || routed&.fetch(:context_limit, nil),
60
+ max_output_tokens: opts[:max_output_tokens] || routed&.fetch(:max_output_tokens, nil)
61
+ )
62
+ Rubik::Runtime::LoopService.build_loop(@runtime_manager&.context, shell: self, config: merged)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "reline"
4
+ require "stringio"
5
+ require_relative "command_parser"
6
+ require_relative "command_context"
7
+ require_relative "composition"
8
+ require_relative "../coeffects"
9
+ require_relative "hooks"
10
+ require_relative "commands"
11
+ require_relative "config_wiring"
12
+ require_relative "tee_io"
13
+ require_relative "interrupt_handler"
14
+ require_relative "confirmations"
15
+ require_relative "presentation"
16
+ require_relative "turn_recorder"
17
+ require_relative "lifecycle"
18
+ require_relative "input_loop"
19
+ require_relative "input_execution"
20
+ require_relative "paste_support"
21
+ require_relative "setup_factories"
22
+ require_relative "ast_warmup"
23
+ require_relative "../runtime"
24
+
25
+ module Rubik
26
+ module REPL
27
+ class Shell
28
+ include Confirmations
29
+ include Presentation
30
+ include TurnRecorder
31
+ include Lifecycle
32
+ include InputLoop
33
+ include InputExecution
34
+ include PasteSupport
35
+ include SetupFactories
36
+ include AstWarmup
37
+
38
+ attr_accessor :output, :conversation_history, :conversation_route_stamp,
39
+ :active_loop, :agent_caller, :terminal_guard
40
+ # Shell-owned readers (lifecycle state, not composed services).
41
+ attr_reader :commands, :hooks, :tool_registry, :input, :reline, :ui_theme,
42
+ :ui_renderer, :config_cleanups, :indexer_thread
43
+ # Composed-service readers derive from the ServiceCatalog SSOT
44
+ # (Candidate 05): adding a service = Coeffects key + catalog entry,
45
+ # and the reader follows the entry. Guarded by test_service_catalog.
46
+ attr_reader(*ServiceCatalog.reader_names)
47
+
48
+ # Plan 63 Faz B/C: subsystem construction lives in Composition.build; the
49
+ # shell keeps only phased coordination below (inject → invariant → wire →
50
+ # register → sync → warm-up). initialize stays a ~15-line orchestrator.
51
+ def initialize(config: nil, event_bus: nil, buffer_manager: nil, snapshot_store: nil, output: $stdout, input: $stdin, reline: Reline, root_dir: nil,
52
+ parts: nil, terminal_guard: nil)
53
+ parts ||= Composition.build(config: config, root_dir: root_dir, event_bus: event_bus,
54
+ buffer_manager: buffer_manager, snapshot_store: snapshot_store)
55
+ inject_parts(parts, injected_buffer: !buffer_manager.nil?)
56
+ @output = output
57
+ @input = input
58
+ @reline = reline
59
+ @terminal_guard = terminal_guard || TerminalGuard.new(
60
+ output: @output,
61
+ reline: @reline,
62
+ quiet: (@config.respond_to?(:fetch) ? @config.fetch("ui.quiet_notices", false) : false) == true
63
+ )
64
+ @hooks = Hooks.new
65
+ @commands = {}
66
+ @running = false
67
+ @dirty_turns = 0
68
+ # Plan 91 — set by the !paste command, consumed once by
69
+ # read_and_process_line to enter explicit paste mode.
70
+ @paste_requested = false
71
+ @conversation_history = []
72
+ # The in-flight Orchestrator::Loop during a free-text/goal turn, nil at
73
+ # idle. Set for the duration of a turn only (same-thread, synchronous
74
+ # tool execution); the ensure block in execute_free_text/goal clears it.
75
+ # NOT the runtime manager's orchestrator_loop fiber instance — that is
76
+ # a different, rebuilt-on-reprovide object no running turn is using.
77
+ @active_loop = nil
78
+
79
+ wire_runtime_context(parts)
80
+ load_plugins(parts)
81
+ register_runtime_services
82
+ build_and_provide_tool_registry
83
+ sync_ui
84
+ sync_default_llm_route
85
+ warm_up!
86
+ end
87
+
88
+ def sync_context_and_ast(files)
89
+ return unless files && !files.empty?
90
+
91
+ root_d = @buffer_manager&.root_dir || Dir.pwd
92
+ files.each do |rel_file|
93
+ @context_index.update_file(rel_file)
94
+ full_path = File.join(root_d, rel_file)
95
+ File.exist?(full_path) ? @ast_graph.index_file(full_path) : @ast_graph.remove_file(full_path)
96
+ end
97
+ @ast_graph.save_to_cache(@cache_store) if @cache_store
98
+ end
99
+
100
+ # Öneri 3 (Kademe 4): warm_ast_graph / stop_background_indexer /
101
+ # join_indexer_safely / perform_warm_ast_graph,
102
+ # lib/rubik/repl/ast_warmup.rb mixin'ine taşındı (AST grafiği ısınma ve
103
+ # arka plan indexer yaşam döngüsü).
104
+
105
+ def outline_for(file_path)
106
+ full_path = File.expand_path(file_path, @buffer_manager&.root_dir || Dir.pwd)
107
+ @ast_graph.index_file(full_path) if File.exist?(full_path) && !@ast_graph.file_symbols.key?(full_path)
108
+ @ast_graph.outline_for(file_path)
109
+ end
110
+
111
+ def symbol_definition(symbol_name)
112
+ res = @ast_graph.symbol_definition(symbol_name)
113
+ if res.nil? && @indexer_thread&.alive?
114
+ join_indexer_safely(0.2)
115
+ res = @ast_graph.symbol_definition(symbol_name)
116
+ end
117
+ res
118
+ end
119
+
120
+ def search_symbols(query)
121
+ join_indexer_safely(0.2) if @ast_graph.symbols.empty? && @indexer_thread&.alive?
122
+ @ast_graph.symbols.values.select { |sym| sym.name.downcase.include?(query.downcase) }
123
+ end
124
+
125
+ def register_command(name, description:, usage: nil, custom: false, audience: nil, &handler)
126
+ key = name.to_s.downcase.delete_prefix("!")
127
+ return false if custom && @commands.key?(key) && !@commands[key][:custom]
128
+
129
+ resolved = { human: :human, agent: :agent, both: :both }.fetch(audience || (custom ? :both : :human)) do
130
+ raise ArgumentError, "invalid audience: #{audience.inspect}"
131
+ end
132
+ @commands[key] = { description: description, usage: usage, handler: handler, custom: custom, audience: resolved }
133
+ true
134
+ end
135
+
136
+ def unregister_command(name)
137
+ @commands.delete(name.to_s.downcase.delete_prefix("!"))
138
+ end
139
+
140
+ def run(input_stream = nil)
141
+ @running = true
142
+
143
+ if input_stream
144
+ run_stream(input_stream)
145
+ else
146
+ run_interactive
147
+ end
148
+ end
149
+
150
+ def running?
151
+ !@running.nil? && @running
152
+ end
153
+
154
+ def stop
155
+ if @buffer_manager&.dirty? && (@config.nil? || @config.fetch("buffer.exit_guard", true) != false)
156
+ count = @buffer_manager.dirty_files.size
157
+ @output.puts "⚠️ Warning: You have #{count} unapplied dirty buffer change(s)! Run '!apply' to persist or '!diff' to review."
158
+ end
159
+ @ui_renderer&.spinner&.kill
160
+ stop_background_indexer
161
+ @config_cleanups&.reverse_each(&:call)
162
+ @config_cleanups = []
163
+ # Ordering is load-bearing: disposing the runtime manager drains the
164
+ # ctx.on_dispose inverses (including LSP and MCP stop_all, and the
165
+ # subagent-notice unsubscribe) BEFORE shutdown_all kills subagents,
166
+ # so kill-time completion events find no listener and never spam the
167
+ # terminal on exit. Subsequent stop_all calls are idempotent no-ops.
168
+ @runtime_manager&.dispose!
169
+ @subagent_supervisor&.shutdown_all
170
+ @lsp_manager&.stop_all
171
+ @mcp_manager&.stop_all
172
+ Rubik.current_shell = nil if Rubik.current_shell == self
173
+ Rubik.runtime_manager = nil if Rubik.runtime_manager == @runtime_manager
174
+ @running = false
175
+ end
176
+ alias close stop
177
+ alias dispose stop
178
+
179
+ # Parses and dispatches a single line of REPL input to the appropriate handler.
180
+ #
181
+ # @param input_text [String] the raw input line from the user
182
+ # @return [void]
183
+ def execute_input(input_text)
184
+ parsed = CommandParser.parse(input_text)
185
+ @recorder.record(parsed.type, input_text) if !@replaying && parsed.type != :empty && parsed.type != :prompt
186
+
187
+ case parsed.type
188
+ when :empty
189
+ # noop
190
+ when :command
191
+ execute_command(parsed)
192
+ when :prompt
193
+ execute_free_text(parsed.raw)
194
+ end
195
+
196
+ check_dirty_buffer_reminder unless @replaying
197
+ end
198
+
199
+ def check_dirty_buffer_reminder
200
+ return unless @buffer_manager
201
+
202
+ if @buffer_manager.dirty?
203
+ @dirty_turns += 1
204
+ interval = @config.fetch("buffer.reminder_interval", 3).to_i
205
+ if interval.positive? && @dirty_turns >= interval
206
+ count = @buffer_manager.dirty_files.size
207
+ @output.puts "💡 [Rubik::Buffer] #{count} unapplied file modification(s) in buffer. Run '!diff' to review or '!apply' to persist to disk."
208
+ @dirty_turns = 0
209
+ end
210
+ else
211
+ @dirty_turns = 0
212
+ end
213
+ end
214
+
215
+ def allowed_external_dirs
216
+ @buffer_manager&.allowed_external_dirs || []
217
+ end
218
+
219
+ # Öneri 3 (Kademe 2): with_output / capture_output / execute_command /
220
+ # execute_free_text, lib/rubik/repl/input_execution.rb mixin'ine taşındı
221
+ # (I/O geçişi, bang-komut dispatch'i, serbest metin turn yaşam döngüsü).
222
+ # Öneri 3 (Kademe 3): build_tool_registry / orchestrator_loop /
223
+ # build_orchestrator_loop, lib/rubik/repl/setup_factories.rb mixin'ine
224
+ # taşındı (araç kaydı kurulumu ve orchestrator loop fabrikaları).
225
+
226
+ def register_default_commands
227
+ Rubik::REPL::Commands.register_all(self)
228
+ end
229
+ end
230
+ end
231
+ end