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,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ module Storage
5
+ module Vakanuvis
6
+ # Mühimme küratörlüğü: memory kartlarının yazma disiplini (record),
7
+ # emeklilik politikası (forget — K2 supersede/purge) ve kaçak ham
8
+ # storage put'ların süpürülmesi (K9 tek-yol disiplininin son savunma
9
+ # hattı). Yazmaların hepsi Vakanuvis.upsert'ten geçer: ref minting,
10
+ # get-then-put ve hash-değişmediyse-skip bedava gelir.
11
+ module MemorySteward
12
+ module_function
13
+
14
+ # Deterministik Keywords onarımı (issue #99 P1): Keywords: satırı
15
+ # olmayan kart sessizce gömülür — hata yazım anında, bedava olan
16
+ # yerde yakalanır. Ret red değil onarımdır: gövdeye katlama-
17
+ # tokenize'dan çıkan en sık terimler eklenir. Onarım upsert'ten
18
+ # ÖNCE uygulanır: replay skip'i (content_unchanged?) onarılmış
19
+ # gövdeyi kıyaslar, idempotency bozulmaz.
20
+ KEYWORDS_LINE = /\Akeywords?:/i
21
+ KEYWORDS_MAX = 6
22
+ MIN_TERM_LENGTH = 3
23
+ # Katlanmış (Folding.fold) ve [a-z0-9]+ tokenize uzayında durur.
24
+ # DİAKRİKTİLİ TR sözcükler (ç/ğ/ö/ş/ü) tokenize'da parçalanır ve
25
+ # ≥3'lük parçalar süzülebilir (review CR#6: "kütüphane"→"phane"
26
+ # sınıfı) — onarım kalite hedefidir, recall doğruluğunu etkilemez:
27
+ # gövde metni zaten arama samanlığının parçasıdır. Ciddi TR kartlar
28
+ # için gerçek güvence eliciden yazılan Keywords: satırıdır.
29
+ STOPWORDS = [
30
+ "the", "and", "for", "are", "but", "not", "you", "your", "with", "from", "this", "that",
31
+ "have", "has", "had", "was", "were", "will", "would", "can", "could", "should", "into",
32
+ "onto", "over", "under", "then", "than", "when", "what", "which", "while", "where", "why",
33
+ "how", "all", "any", "some", "such", "only", "just", "also", "very", "more", "most",
34
+ "per", "via", "its", "does", "did", "done", "been", "being", "same", "other", "because",
35
+ "however", "without", "again", "both", "each", "between", "through", "during", "before",
36
+ "after", "above", "below", "out", "off", "use", "used", "uses", "using", "new", "old",
37
+ "get", "got", "set", "one", "two", "three", "first", "second", "third", "next", "last",
38
+ "may", "might", "must", "shall",
39
+ "ve", "ile", "ama", "fakat", "eger", "ancak", "daha", "bir", "bu", "su", "ne", "gibi",
40
+ "kadar", "sonra", "once", "var", "yok", "degil", "olan", "olur", "oldu", "ise", "cok",
41
+ "az", "hem", "ya", "nasil", "neden"
42
+ ].freeze
43
+
44
+ # Maestro talebiyle memory kartı (K7: authorship maestro'nun,
45
+ # yazma disiplini Düzlem 1'in). ref = mem.<slug(topic)>; aynı
46
+ # konuya ikinci record aynı satırı günceller (version ancak içerik
47
+ # değişince artar). tags (issue #99 P1): kullanıcı etiketleri
48
+ # Vocabulary ile birleşir — recall haystack'i etiketleri de tarar.
49
+ def record(store, topic:, body:, links: {}, tags: [])
50
+ clean_topic = topic.to_s.strip
51
+ raise ArgumentError, "record: 'topic' is required" if clean_topic.empty?
52
+ raise ArgumentError, "record: 'body' is required" if body.to_s.strip.empty?
53
+
54
+ user_tags = validate_tags(tags)
55
+ repaired, state = ensure_keywords(body.to_s, title: clean_topic)
56
+ ref = Vocabulary.memory_ref(clean_topic)
57
+ rec = Vakanuvis.upsert(store, type: "memory", ref: ref,
58
+ title: AnnalWriter.one_line(clean_topic, 120),
59
+ body: repaired,
60
+ tags: (Vocabulary::MEMORY_TAGS + user_tags).uniq,
61
+ links: links)
62
+ { "ref" => ref, "id" => rec["id"], "version" => rec["version"], "skipped" => rec["skipped"],
63
+ "keywords_extracted" => state == :extracted, "keywords_state" => state.to_s }
64
+ end
65
+
66
+ # issue #100 P1'in ret ailesi: kullanıcı etiketleri dizgi olmalı —
67
+ # boolean/sayı burada, record kapısında kesilir (Store#put'tan önce).
68
+ def validate_tags(tags)
69
+ list = Array(tags)
70
+ list.each do |tag|
71
+ next if tag.is_a?(String)
72
+
73
+ raise ArgumentError,
74
+ "record tags must be strings — got #{tag.class} (#{tag.inspect}). " \
75
+ "Pass an array of strings."
76
+ end
77
+ list
78
+ end
79
+
80
+ # Gövde Keywords: satırı taşıyor mu (case-insensitive)? Taşımıyorsa
81
+ # deterministik terimlerden bir tane ekler. Dönüş: [gövde, durum] —
82
+ # durum :present (satır vardı), :extracted (eklendi) ya da :none
83
+ # (satır yok VE terim çıkmadı — review M4: bu düşüş sessiz
84
+ # kalmaz, makbuz "none" taşır ve kart yüzü uyarır).
85
+ def ensure_keywords(body, title: "")
86
+ return [body, :present] if body.lines.any? { |line| line.match?(KEYWORDS_LINE) }
87
+
88
+ terms = extract_terms("#{title}\n#{body}")
89
+ return [body, :none] if terms.empty?
90
+
91
+ ["#{body.rstrip}\n\nKeywords: #{terms.join('; ')}", :extracted]
92
+ end
93
+
94
+ # Deterministik terim çıkarımı: katlama üzerinden tokenize,
95
+ # stopwords ve kısa öbekler dışarı, sıra [-sıklık, ilk geçme yeri] —
96
+ # aynı gövde her zaman aynı satırı üretir.
97
+ def extract_terms(text)
98
+ tokens = Folding.fold(text).scan(/[a-z0-9]+/).reject do |token|
99
+ token.size < MIN_TERM_LENGTH || STOPWORDS.include?(token)
100
+ end
101
+ counts = Hash.new(0)
102
+ first_seen = {}
103
+ tokens.each_with_index do |token, index|
104
+ counts[token] += 1
105
+ first_seen[token] = index unless first_seen.key?(token)
106
+ end
107
+ counts.keys.sort_by { |token| [-counts[token], first_seen[token]] }.first(KEYWORDS_MAX)
108
+ end
109
+
110
+ # K2: retention=supersede (default) iz bırakır — status + halef
111
+ # link'i; retention=purge satırı sert siler (refs de düşer).
112
+ # Not refs'i de çözümlenir (issue #100 follow-up: notlerin yaşam
113
+ # döngüsü — "note yalnız birikir" gözlemi): önce memory, yoksa
114
+ # note; stub dizisi memory yolunda değişmez.
115
+ def forget(store, ref:, retention: "supersede")
116
+ type = "memory"
117
+ rec = store.get(type: type, id: ref)
118
+ if rec.nil?
119
+ type = "note"
120
+ rec = store.get(type: type, id: ref)
121
+ end
122
+ return { "ref" => ref.to_s, "forgot" => false } unless rec
123
+
124
+ if retention.to_s == "purge"
125
+ deleted = store.delete(type: type, id: rec["id"])
126
+ return { "ref" => ref.to_s, "forgot" => false } unless deleted
127
+
128
+ { "ref" => ref.to_s, "id" => rec["id"], "forgot" => true, "mode" => "purge", "type" => type }
129
+ else
130
+ Vakanuvis.supersede_record(store, rec, by: "forget")
131
+ { "ref" => ref.to_s, "id" => rec["id"], "forgot" => true, "mode" => "supersede", "type" => type }
132
+ end
133
+ end
134
+
135
+ # K9 süpürme: hiçbir mem.* alias'ına bağlı olmayan (yani ham
136
+ # `storage put type=memory` ile açılmış) satırları disipline
137
+ # sokar — başlıktan slug türet, en yeni satıra mem.<slug> ref'i
138
+ # bağla (bir sonraki record() artık O satırı günceller), aynı
139
+ # slug'a düşen eski kopyaları supersede et. Deterministik: sıra
140
+ # ve seçim yalnızca [row] üzerinden.
141
+ def sweep_rogue_memory(store)
142
+ bound_ids = store.refs.select { |ref, _id| Vocabulary.memory_ref?(ref) }.values
143
+ rows = store.list(type: "memory").reject do |s|
144
+ bound_ids.include?(s.id) || s.status == Vocabulary::STATUS_SUPERSEDED
145
+ end
146
+ return 0 if rows.empty?
147
+
148
+ swept = 0
149
+ rows.group_by { |s| Vocabulary.slug(s.title) }.each do |slug, group|
150
+ winner = group.max_by(&:row)
151
+ store.set_ref(Vocabulary.memory_ref(slug), winner.id)
152
+ group.reject { |s| s.id == winner.id }.each do |s|
153
+ rogue = store.get(type: "memory", id: s.id)
154
+ Vakanuvis.supersede_record(store, rogue, by: winner.id) if rogue
155
+ end
156
+ swept += group.size
157
+ end
158
+ swept
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../layout"
4
+
5
+ module Rubik
6
+ module Storage
7
+ module Vakanuvis
8
+ # Kompozisyon kökü kancası (repl/lifecycle.rb Phase 4, Storage::
9
+ # Service.register'dan hemen sonra — STORE coeffect'i çözülebilir
10
+ # olmalı): kronikçi açıksa Harness kurulur, kendi child ctx'inde
11
+ # yaşar (abonelik + worker geri alımları orada), VAKANUIS coeffect'i
12
+ # köke sağlanır (talep düzlemi aracı buradan çözer). Kapalıysa
13
+ # (subagents.vakanuvis.enabled=false) nil döner — kronikçi yok,
14
+ # aracı REQUIRES karşılanmaz ve model yüzünde görünmez.
15
+ module Service
16
+ module_function
17
+
18
+ # Test zırhı: RUBIK_TEST=1 ortamında (tüm test süiti) kronikçi
19
+ # varsayılan KAPALI bağlanır — gerçek shell'lerle koşan scripted
20
+ # provider testlerinde arka plan damıtıcı doğumları yarış açar.
21
+ # RUBIK_VAKANUIS_FORCE=1 zırhı devre dışı bırakır (kronikçinin
22
+ # kendi bağlama testleri); üretimde zırh yok, K1 gereği AÇIK.
23
+ def enabled?(config = nil)
24
+ return false if ENV["RUBIK_TEST"] == "1" && ENV["RUBIK_VAKANUIS_FORCE"] != "1"
25
+
26
+ Layout.config_get(config, "subagents.vakanuvis.enabled", true)
27
+ end
28
+
29
+ def bind(context_or_manager, config: nil, event_bus: nil)
30
+ return nil unless enabled?(config)
31
+
32
+ ctx = context_or_manager.respond_to?(:context) ? context_or_manager.context : context_or_manager
33
+ return nil unless ctx.respond_to?(:provide)
34
+
35
+ store = Rubik::Coeffects.get(ctx, Rubik::Coeffects::STORAGE_STORE)
36
+ unless store
37
+ Rubik::Log.warn(:storage, "vakanuvis not bound: STORAGE_STORE coeffect unsatisfied " \
38
+ "(storage.enabled=false or boot-order gap)")
39
+ return nil
40
+ end
41
+
42
+ child = ctx.respond_to?(:extend) ? ctx.extend : nil
43
+ unless child
44
+ Rubik::Log.warn(:storage, "vakanuvis bound without a disposable child ctx — worker and " \
45
+ "subscriptions live for the process lifetime")
46
+ end
47
+ unless event_bus.respond_to?(:subscribe)
48
+ Rubik::Log.warn(:storage, "vakanuvis bound without an event bus — the distiller stays " \
49
+ "dormant (digests queue, no harvest path)")
50
+ end
51
+
52
+ harness = Harness.new(
53
+ store: store, config: config, event_bus: event_bus,
54
+ supervisor: Rubik::Coeffects.get(ctx, Rubik::Coeffects::SUBAGENT_SUPERVISOR),
55
+ dmesg: Rubik::Coeffects.get(ctx, Rubik::Coeffects::DMESG),
56
+ context: child, session_slug: nil
57
+ )
58
+ # Ömür (§2.7): child ctx'in cascade inverse'i worker'ı kapatır;
59
+ # kuyruk FIFO'su drain olur, yarım kronik kalmaz.
60
+ child&.on_dispose { harness.shutdown }
61
+ harness.start!
62
+ ctx.provide(Rubik::Coeffects::VAKANUIS, harness)
63
+ context_or_manager.reconcile_all if context_or_manager.respond_to?(:reconcile_all)
64
+ harness
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ module Storage
5
+ module Vakanuvis
6
+ # Kronikçinin kelime hazinesi: ref grameri (mint + parse), etiket
7
+ # dağarcığı, status sözlüğü ve rakam-slug koruması — TEK ev.
8
+ # Store mekanik kalır: type.row çözümü alias'tan ÖNCE gelir
9
+ # (store.rb resolve_record), bu yüzden mint edilen HİÇBİR ref
10
+ # /\A[a-z]+\.\d+\z/ şekline düşmez (rakam koruması).
11
+ module Vocabulary
12
+ SLUG_MAX = 40
13
+ BASE_TAG = "vakanuvis"
14
+
15
+ # Etiket dağarcığı (yazar modüllerin sabitleri tek evden alınır).
16
+ ANNAL_TAGS = [BASE_TAG, "annal"].freeze
17
+ MEMORY_TAGS = [BASE_TAG, "memory"].freeze
18
+ WIKI_TAGS = [BASE_TAG, "wiki"].freeze
19
+ GOAL_TAG = "goal" # goal kayıtlarının annal etiketi üstündeki ekstrası
20
+ GAP_TAG = "recall-gap" # recall smoke iska notlarının etiketi
21
+
22
+ # Status sözlüğü — kronikçinin yazdığı/okuduğu depolama düzlemi
23
+ # değerleri (transcript durumları kronikçinin AYNALADIĞI subagent
24
+ # yaşam-döngüsü değeridir; lifecycle durumları bunun dışındadır).
25
+ STATUS_ACTIVE = "active"
26
+ STATUS_SUPERSEDED = "superseded"
27
+ TRANSCRIPT_COMPLETED = "completed"
28
+
29
+ # Store çözümleyicisinin tip.satır çarpışma biçimi: resolve_record
30
+ # bu şekli alias'tan ÖNCE dener — mint edilen ref bu şekle ASLA
31
+ # düşmez (tek Garantör: rakam koruması, aşağıda #slug).
32
+ TYPE_ROW_PATTERN = /\A[a-z]+\.\d+\z/
33
+ # Store id biçimi ("plan.3.<hash16>"; store.rb ID_PATTERN ile aynı
34
+ # şekil — self-contained kopya, Store'a bağımlılık açmamak için).
35
+ ID_SHAPE = /\A([a-z]+)\.\d+\.[0-9a-f]{16}\z/
36
+
37
+ module_function
38
+
39
+ # Deterministik slug: aynı metin ⇒ aynı slug ⇒ aynı ref (digest
40
+ # makbuzlarının ve mem./wiki. ref'lerin yeniden oynatılabilirliği
41
+ # buna dayanır). Yalnızca-rakam slug'a "n" öneki zorunlu: Store'un
42
+ # çözümleyicisi "wiki.42" gibi iki parçalı tip.satır formlarını ref
43
+ # alias'tan ÖNCE dener — çarpışma yanlış satırı ezmesi demekti.
44
+ def slug(text)
45
+ s = text.to_s.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-+|-+\z/, "")
46
+ s = s[0, SLUG_MAX].to_s.gsub(/\A-+|-+\z/, "")
47
+ s = "n#{s}" if s.match?(/\A\d+\z/)
48
+ s.empty? ? "untitled" : s
49
+ end
50
+
51
+ # ---- mint ------------------------------------------------------------
52
+
53
+ def session_ref(slug) = "annal.session.#{slug}"
54
+
55
+ def goal_ref(ordinal) = "annal.goal.#{ordinal}"
56
+
57
+ def memory_ref(topic) = "mem.#{slug(topic)}"
58
+
59
+ # digest hedefi de bu
60
+ def wiki_ref(topic) = "wiki.#{slug(topic)}"
61
+
62
+ # Recall-gap not kartının ref'i: sayfa ref'inden türetilir. wiki
63
+ # olmayan bir sayfa ref'i sessizce bozuk bir alias basmamak için
64
+ # sınırda patlar (fail-fast — split('.') körlemesine bölme değildi).
65
+ def recall_gap_ref(page_ref)
66
+ page_slug = wiki_slug(page_ref)
67
+ raise ArgumentError, "recall_gap_ref: '#{page_ref}' is not a wiki.<slug> ref" if page_slug.nil?
68
+
69
+ "annal.recallgap.#{page_slug}"
70
+ end
71
+
72
+ HOME_REF = "wiki.home"
73
+
74
+ # ---- parse -----------------------------------------------------------
75
+
76
+ def goal_ordinal(ref)
77
+ m = ref.to_s.match(/\Aannal\.goal\.(\d+)\z/)
78
+ m && m[1].to_i
79
+ end
80
+
81
+ def wiki_ref?(ref) = /\Awiki\.[a-z0-9][a-z0-9-]*\z/.match?(ref.to_s)
82
+
83
+ def wiki_slug(ref)
84
+ m = ref.to_s.match(/\Awiki\.([a-z0-9][a-z0-9-]*)\z/)
85
+ m && m[1]
86
+ end
87
+
88
+ def memory_ref?(ref) = ref.to_s.start_with?("mem.")
89
+
90
+ def type_row_shaped?(selector) = TYPE_ROW_PATTERN.match?(selector.to_s)
91
+
92
+ def id_type(id)
93
+ m = id.to_s.match(ID_SHAPE)
94
+ m && m[1]
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ module Storage
5
+ module Vakanuvis
6
+ # Ta'rih iskeleti: wiki.home indeks sayfasını deterministik yeniden
7
+ # hesaplar (Karpathy "compilation over retrieval" — derlenmiş sayfa
8
+ # seti tek bakışta görünür; home kendisi bir wiki satırıdır ama
9
+ # listeye asla girmez). Sıralama Store#list'in [type,row] sırası —
10
+ # aynı depo ⇒ aynı home gövdesi. v2 §4-V3: her satır tek satırlık
11
+ # ÖZET taşır (başlık+blockquote atlanır, ≤120 karakter) — LLM home'a
12
+ # tek bakışla hangi sayfayı çekeceğine karar verir (≤2 adım erişim).
13
+ module WikiSkeleton
14
+ module_function
15
+
16
+ SUMMARY_MAX = 120
17
+
18
+ def rebuild_home(store)
19
+ home = store.get(type: "wiki", id: Vocabulary::HOME_REF)
20
+ pages = store.list(type: "wiki").reject do |s|
21
+ s.status == Vocabulary::STATUS_SUPERSEDED || (home && s.id == home["id"])
22
+ end
23
+ body = pages.empty? ? "_(no compiled pages yet)_" : pages.map { |s| page_line(store, s) }.join("\n")
24
+ Vakanuvis.upsert(store, type: "wiki", ref: Vocabulary::HOME_REF,
25
+ title: "Compiled knowledge (wiki home)",
26
+ body: body, tags: Vocabulary::WIKI_TAGS, links: {})
27
+ end
28
+
29
+ # Derleme dedup adayları (issue #100 P1b): aynı başlık ya da
30
+ # birebir aynı gövdeyi taşıyan AKTİF sayfalar — süpersede
31
+ # edilenler, home ve kendisi (excluding_id) aday değildir. Sıra
32
+ # Store#list'in [type,row] sırası: eskisi önce, süpersede de
33
+ # daima eskisini bastırır. Uzun gövde kıyası örneklemli değil
34
+ # birebirdir — derleme çıktısı zaten sayfa boyutu sınırlıdır.
35
+ def find_duplicate_pages(store, title:, body:, excluding_id: nil)
36
+ home = store.get(type: "wiki", id: Vocabulary::HOME_REF)
37
+ candidates = store.list(type: "wiki").reject do |s|
38
+ s.status == Vocabulary::STATUS_SUPERSEDED ||
39
+ (home && s.id == home["id"]) ||
40
+ (excluding_id && s.id == excluding_id)
41
+ end
42
+ candidates.filter_map do |s|
43
+ record = store.get(type: "wiki", id: s.id)
44
+ next unless record
45
+
46
+ record if record["title"] == title || record["body"] == body
47
+ end
48
+ end
49
+
50
+ def page_line(store, summary)
51
+ record = store.get(type: "wiki", id: summary.id)
52
+ "- #{summary.title} — #{page_summary(record ? record['body'].to_s : '')} — #{store.uri_for('wiki', summary.id)}"
53
+ end
54
+
55
+ # Sayfanın açılış özeti: başlık/blok-alıntı atlanır, ilk dolu satır
56
+ # tek satıra indirilir (AnnalWriter disiplini). Özet yoksa dürüst
57
+ # yer tutucu — home satırı asla düşmez.
58
+ def page_summary(body)
59
+ line = body.lines.map(&:strip).reject(&:empty?)
60
+ .reject { |l| l.start_with?("#", ">") }.first.to_s
61
+ cleaned = AnnalWriter.one_line(line, SUMMARY_MAX)
62
+ cleaned == "(none)" ? "(no summary yet)" : cleaned
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ module Storage
5
+ # Vakanüvis — arka plan kronikçisi (plan 2026-10-04, kararlar K1-K9).
6
+ # Saray kronikcisi metaforu: maestro'nun (padişah) turn'ünü ASLA
7
+ # uyarmaz (re-uyandırma kümesinin tamamen dışında; yalnız dmesg/stderr),
8
+ # olay sınırlarında deterministik annal yazar (Düzlem 1, LLM'siz),
9
+ # gerektiğinde sınırlı otonomlu damıtıcı subagent doğurur (Düzlem 2,
10
+ # LLM'li — öneri üretir, yazmayı daima Düzlem 1 yapar).
11
+ #
12
+ # ref grammar : annal.session.<slug> / annal.goal.<n> (note tipi)
13
+ # mem.<topic-slug> (memory tipi)
14
+ # wiki.home / wiki.<slug> (wiki tipi)
15
+ # konfig : subagents.vakanuvis.* (operatör-only; K1 defaults ON)
16
+ #
17
+ # Determinizm garantileri (§4.2): kararlarda duvar saati YOK (sadece
18
+ # olay sırası), sabit ref grammar, her yazım ref-bazlı gerçek upsert
19
+ # (içerik hash'i değişmediyse put atlanır — version sabit), tek worker
20
+ # + FIFO kuyruk, taşmada drop-oldest + warn.
21
+ #
22
+ # Ref gramerinin, etiket dağarcığının ve status sözlüğünün TEK evi
23
+ # Vocabulary'dir (vakanuvis/vocabulary.rb).
24
+ module Vakanuvis
25
+ module_function
26
+
27
+ # Ref-bazlı GERÇEK upsert (Store'da ref ile ikinci put yeni satır
28
+ # açtığı için — store.rb §2.3 bulgusu): ref üzerinden mevcut kaydı
29
+ # bulur; içerik değişmediyse DOKUNMAZ (version/updated_at sabit —
30
+ # replay idempotent), değiştiyse AYNI id ile günceller (refs/linkler
31
+ # kopmaz). Dönen kayda "skipped" tanıklığı eklenir.
32
+ def upsert(store, type:, ref:, title:, body: "", tags: [], links: {}, status: Vocabulary::STATUS_ACTIVE)
33
+ existing = store.get(type: type, id: ref)
34
+ return existing.merge("skipped" => true) if existing && content_unchanged?(existing, title, body, tags, links, status)
35
+
36
+ store.put(type: type, title: title, body: body, tags: tags, links: links,
37
+ status: status, id: existing&.[]("id"), ref: ref)
38
+ .merge("skipped" => false)
39
+ end
40
+
41
+ # K2 supersede: iz bırak — status "superseded" + superseded_by
42
+ # link'i (halefi kim: yeni kayıt id'si ya da "forget").
43
+ def supersede_record(store, record, by:)
44
+ links = record["links"].to_h.merge("superseded_by" => by.to_s)
45
+ store.put(type: record["type"], title: record["title"], body: record["body"],
46
+ tags: record["tags"], links: links, status: Vocabulary::STATUS_SUPERSEDED, id: record["id"])
47
+ end
48
+
49
+ # Vakanüvis'in kendi dmesg etiketi (K6): sessizlik sözleşmesi
50
+ # gereği notice YOK — sadece dmesg/stderr düzlemi.
51
+ SUBSYSTEM = :storage
52
+
53
+ def log_via(dmesg, level, message)
54
+ dmesg.respond_to?(:log) ? dmesg.log(SUBSYSTEM, level, message) : Rubik::Log.send(level, SUBSYSTEM, message)
55
+ rescue StandardError
56
+ Rubik::Log.warn(SUBSYSTEM, message)
57
+ end
58
+
59
+ def content_unchanged?(existing, title, body, tags, links, status)
60
+ existing["title"] == title.to_s &&
61
+ existing["body"] == body.to_s &&
62
+ existing["tags"] == Array(tags).map(&:to_s).reject { |t| t.strip.empty? }.uniq.sort &&
63
+ existing["links"] == links.to_h { |k, v| [k.to_s, v.to_s] }.sort.to_h &&
64
+ existing["status"] == status.to_s
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ require_relative "vakanuvis/vocabulary"
71
+ require_relative "vakanuvis/annal_writer"
72
+ require_relative "vakanuvis/memory_steward"
73
+ require_relative "vakanuvis/wiki_skeleton"
74
+ require_relative "vakanuvis/distill_gate"
75
+ require_relative "vakanuvis/gate"
76
+ require_relative "vakanuvis/harness"
77
+ require_relative "vakanuvis/service"
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ # rubik:// URI-adreslenebilir sanal FS-DB (RubikFS): hedef, plan, bellek,
5
+ # wiki, transcript, inceleme ve not varlıkları için merkezi depo.
6
+ #
7
+ # rubik://storage/index — merkezi indeks
8
+ # rubik://storage/<project> — proje özeti
9
+ # rubik://storage/<project>/<type> — tip listesi
10
+ # rubik://storage/<project>/<type>/<id> — varlık (satır/karma/alias)
11
+ # rubik://storage/<project>/refs/<alias> — alias çözümü
12
+ #
13
+ # Fiziki yerleşim ~/.rubik/rubikstorage/<project>/ (Layout SSOT); varlık
14
+ # kimliği tip + satır + içerik karması ("plan.3.9f86d081884c7d21" — Store).
15
+ # Okuma düzlemi read_file URI rotaları + Render; yazma düzlemi `storage`
16
+ # aracı ve otomatik transcript indekslemesi (supervisor). Worktree'yi
17
+ # temiz tutma görevi: absorb_artifacts açıkken yeni oturum dosyaları ve
18
+ # subagent transcriptları aynı proje köküne yazılır (Layout).
19
+ module Storage
20
+ end
21
+ end
22
+
23
+ require_relative "storage/layout"
24
+ require_relative "storage/store"
25
+ require_relative "storage/render"
26
+ require_relative "storage/service"
27
+ require_relative "storage/uri_reader"
28
+ require_relative "storage/transcript_indexer"
29
+ require_relative "storage/vakanuvis"
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require_relative "declaration"
5
+
6
+ module Rubik
7
+ module Tools
8
+ class BaseTool
9
+ attr_reader :name, :description, :risk, :params_schema, :aliases, :allow_additional_properties, :declaration
10
+
11
+ # Builtins pass their class-level DECLARATION constant; dynamic tools
12
+ # (synthesis, overlay rehydration, MCP proxies, test doubles) keep the
13
+ # kwargs path — both produce the same frozen Declaration. New builtins
14
+ # must use DECLARATION (single-source invariant).
15
+ def initialize(declaration_or_kwargs = {})
16
+ d = declaration_or_kwargs.is_a?(Declaration) ? declaration_or_kwargs : Declaration.new(**declaration_or_kwargs)
17
+ @declaration = d
18
+ @name = d.name
19
+ @description = d.description
20
+ @risk = d.risk
21
+ @params_schema = d.params_schema
22
+ @aliases = d.aliases
23
+ @allow_additional_properties = d.allow_additional_properties
24
+ end
25
+
26
+ # Policy readers project through the declaration for consumers that
27
+ # hold a tool instance (they never enter to_h — LLM surface stays
28
+ # name/description/risk/parameters).
29
+ def archetypes = @declaration.archetypes
30
+
31
+ def maestro_blocked? = @declaration.maestro_blocked?
32
+
33
+ def spawn? = @declaration.spawn?
34
+
35
+ def write_grant? = @declaration.write_grant?
36
+
37
+ # Executes the tool with the given arguments. Must be implemented by subclasses.
38
+ #
39
+ # @param args [Hash] keyword arguments for the tool execution
40
+ # @return [Rubik::Tools::Result] the result of the tool execution
41
+ def execute(args = {})
42
+ raise NotImplementedError
43
+ end
44
+
45
+ # Normalizes a model-supplied path filter against the tool's root
46
+ # directory: backslashes become slashes, "./" is stripped, and ABSOLUTE
47
+ # paths (C:/…, /…) are relativized when they sit inside the root.
48
+ # Models habitually pass absolute paths (read_file accepts them), so
49
+ # filters must not silently fail on them.
50
+ #
51
+ # @return [Array(String, String, nil)] [filter, error] — filter "" means
52
+ # no filter (the root itself); error is a user-facing message for a
53
+ # path outside the workspace, in which case filter is nil.
54
+ def self.normalize_path_filter(raw, root_dir)
55
+ filter = raw.to_s.strip.tr("\\", "/").sub(%r{^\./}, "")
56
+ return ["", nil] if filter.empty? || filter == "." || filter == "/"
57
+
58
+ norm_root = root_dir.to_s.tr("\\", "/").sub(%r{/+\z}, "")
59
+ if filter.downcase == norm_root.downcase
60
+ return ["", nil]
61
+ elsif filter.downcase.start_with?("#{norm_root.downcase}/")
62
+ sub_path = filter[(norm_root.length + 1)..]
63
+ return [sub_path, nil]
64
+ end
65
+
66
+ return [filter, nil] unless filter =~ %r{\A([A-Za-z]:/|/)}
67
+
68
+ rel = begin
69
+ Pathname.new(File.expand_path(filter)).relative_path_from(Pathname.new(File.expand_path(root_dir))).to_s.tr("\\", "/")
70
+ rescue ArgumentError
71
+ nil
72
+ end
73
+ outside = rel.nil? || rel == ".." || rel.start_with?("../")
74
+ if outside
75
+ return [nil, "Path '#{raw}' is outside the project workspace (#{root_dir}). " \
76
+ "Use a path relative to the project root, e.g. 'lib'."]
77
+ end
78
+ return ["", nil] if rel == "."
79
+
80
+ [rel, nil]
81
+ end
82
+
83
+ # Friendly no-match message that echoes the filter and states the
84
+ # relative-path convention, so the model corrects itself instead of
85
+ # retrying the same failing call.
86
+ def self.no_match_message(filter)
87
+ hint = " Tip: 'path' is relative to the project root (e.g. 'lib', 'src/main.go'); " \
88
+ "absolute paths are converted automatically."
89
+ "No matching files found for '#{filter}'.#{hint}"
90
+ end
91
+
92
+ def to_h
93
+ schema = @params_schema
94
+ if !@allow_additional_properties && schema.is_a?(Hash)
95
+ schema = schema.dup
96
+ schema[:additionalProperties] = false unless schema.key?(:additionalProperties) || schema.key?("additionalProperties")
97
+ end
98
+ {
99
+ name: @name,
100
+ description: @description,
101
+ risk: @risk,
102
+ parameters: schema
103
+ }
104
+ end
105
+ end
106
+ end
107
+ end