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.
- checksums.yaml +7 -0
- data/ATTRIBUTION.md +139 -0
- data/CHANGELOG.md +236 -0
- data/LICENSE +21 -0
- data/README.md +269 -0
- data/bin/rubik +82 -0
- data/lib/rubik/adapter/base.rb +195 -0
- data/lib/rubik/adapter/c.rb +36 -0
- data/lib/rubik/adapter/crystal.rb +78 -0
- data/lib/rubik/adapter/generic.rb +86 -0
- data/lib/rubik/adapter/go.rb +36 -0
- data/lib/rubik/adapter/node.rb +146 -0
- data/lib/rubik/adapter/process.rb +78 -0
- data/lib/rubik/adapter/python.rb +30 -0
- data/lib/rubik/adapter/registry.rb +81 -0
- data/lib/rubik/adapter/ruby.rb +30 -0
- data/lib/rubik/adapter/rust.rb +36 -0
- data/lib/rubik/adapter/zig.rb +36 -0
- data/lib/rubik/adapter.rb +18 -0
- data/lib/rubik/ast/body_loader.rb +19 -0
- data/lib/rubik/ast/c_extractor.rb +114 -0
- data/lib/rubik/ast/cache_store.rb +191 -0
- data/lib/rubik/ast/extractor.rb +59 -0
- data/lib/rubik/ast/fallback_cleaner.rb +145 -0
- data/lib/rubik/ast/go_extractor.rb +148 -0
- data/lib/rubik/ast/graph.rb +272 -0
- data/lib/rubik/ast/python_extractor.rb +120 -0
- data/lib/rubik/ast/ruby_extractor.rb +260 -0
- data/lib/rubik/ast/rust_extractor.rb +127 -0
- data/lib/rubik/ast/symbol.rb +103 -0
- data/lib/rubik/ast/treesitter/base_walker.rb +109 -0
- data/lib/rubik/ast/treesitter/c_walker.rb +122 -0
- data/lib/rubik/ast/treesitter/go_walker.rb +120 -0
- data/lib/rubik/ast/treesitter/python_walker.rb +107 -0
- data/lib/rubik/ast/treesitter/rust_walker.rb +130 -0
- data/lib/rubik/ast/treesitter/typescript_walker.rb +182 -0
- data/lib/rubik/ast/treesitter/zig_walker.rb +109 -0
- data/lib/rubik/ast/treesitter_extractor.rb +93 -0
- data/lib/rubik/ast/typescript_extractor.rb +283 -0
- data/lib/rubik/ast/zig_extractor.rb +116 -0
- data/lib/rubik/ast.rb +13 -0
- data/lib/rubik/benchmark/runner.rb +224 -0
- data/lib/rubik/benchmark/synth_repo.rb +59 -0
- data/lib/rubik/benchmark.rb +9 -0
- data/lib/rubik/buffer/diff.rb +43 -0
- data/lib/rubik/buffer/manager.rb +427 -0
- data/lib/rubik/buffer/path_validator.rb +60 -0
- data/lib/rubik/buffer/snapshot.rb +99 -0
- data/lib/rubik/buffer.rb +10 -0
- data/lib/rubik/coeffects.rb +129 -0
- data/lib/rubik/concurrency/boundary.rb +74 -0
- data/lib/rubik/concurrency/dns_pool.rb +68 -0
- data/lib/rubik/concurrency/errors.rb +17 -0
- data/lib/rubik/concurrency/event_loop.rb +441 -0
- data/lib/rubik/concurrency/join.rb +55 -0
- data/lib/rubik/concurrency/process_waiter.rb +55 -0
- data/lib/rubik/concurrency/scheduler.rb +84 -0
- data/lib/rubik/concurrency/timers.rb +109 -0
- data/lib/rubik/concurrency.rb +132 -0
- data/lib/rubik/config/deep_ops.rb +80 -0
- data/lib/rubik/config/defaults.rb +248 -0
- data/lib/rubik/config/model_catalog.rb +93 -0
- data/lib/rubik/config/store.rb +155 -0
- data/lib/rubik/config.rb +216 -0
- data/lib/rubik/context/git_info.rb +84 -0
- data/lib/rubik/context/git_lister.rb +37 -0
- data/lib/rubik/context/index.rb +134 -0
- data/lib/rubik/context/repo_context_reader.rb +121 -0
- data/lib/rubik/context/scanner.rb +439 -0
- data/lib/rubik/context.rb +11 -0
- data/lib/rubik/diagnostics/collector.rb +200 -0
- data/lib/rubik/diagnostics/entry.rb +60 -0
- data/lib/rubik/diagnostics/parser.rb +107 -0
- data/lib/rubik/diagnostics.rb +10 -0
- data/lib/rubik/event_bus.rb +56 -0
- data/lib/rubik/kernel/dispatcher.rb +171 -0
- data/lib/rubik/kernel/dmesg.rb +116 -0
- data/lib/rubik/kernel/init.rb +177 -0
- data/lib/rubik/kernel/record.rb +85 -0
- data/lib/rubik/kernel/unit.rb +160 -0
- data/lib/rubik/llm/anthropic.rb +202 -0
- data/lib/rubik/llm/chunk_assembler.rb +285 -0
- data/lib/rubik/llm/client.rb +526 -0
- data/lib/rubik/llm/formatter.rb +450 -0
- data/lib/rubik/llm/message_sanitizer.rb +87 -0
- data/lib/rubik/llm/ollama.rb +167 -0
- data/lib/rubik/llm/openai.rb +21 -0
- data/lib/rubik/llm/openai_compatible.rb +216 -0
- data/lib/rubik/llm/provider.rb +125 -0
- data/lib/rubik/llm/registry.rb +336 -0
- data/lib/rubik/llm/router.rb +100 -0
- data/lib/rubik/llm.rb +18 -0
- data/lib/rubik/log.rb +38 -0
- data/lib/rubik/lsp/client.rb +475 -0
- data/lib/rubik/lsp/manager.rb +366 -0
- data/lib/rubik/lsp.rb +9 -0
- data/lib/rubik/mcp/boot_lifecycle.rb +114 -0
- data/lib/rubik/mcp/client.rb +169 -0
- data/lib/rubik/mcp/errors.rb +11 -0
- data/lib/rubik/mcp/manager.rb +619 -0
- data/lib/rubik/mcp/proxy_tool.rb +104 -0
- data/lib/rubik/mcp/transport/stdio.rb +255 -0
- data/lib/rubik/mcp.rb +12 -0
- data/lib/rubik/orchestrator/activity_log.rb +75 -0
- data/lib/rubik/orchestrator/auto_apply.rb +166 -0
- data/lib/rubik/orchestrator/budget_manager.rb +97 -0
- data/lib/rubik/orchestrator/continuation_manager.rb +161 -0
- data/lib/rubik/orchestrator/finish_message_selector.rb +55 -0
- data/lib/rubik/orchestrator/goal_ledger/drift.rb +71 -0
- data/lib/rubik/orchestrator/goal_ledger/ledger.rb +399 -0
- data/lib/rubik/orchestrator/goal_ledger/observation_projection.rb +179 -0
- data/lib/rubik/orchestrator/goal_ledger/patch.rb +268 -0
- data/lib/rubik/orchestrator/goal_ledger/schema.rb +123 -0
- data/lib/rubik/orchestrator/goal_ledger/validator.rb +361 -0
- data/lib/rubik/orchestrator/goal_ledger/verifier.rb +129 -0
- data/lib/rubik/orchestrator/goal_ledger/verify_spec.rb +50 -0
- data/lib/rubik/orchestrator/goal_ledger.rb +65 -0
- data/lib/rubik/orchestrator/goal_state.rb +184 -0
- data/lib/rubik/orchestrator/goal_wait.rb +125 -0
- data/lib/rubik/orchestrator/loop.rb +388 -0
- data/lib/rubik/orchestrator/lsp_feedback.rb +55 -0
- data/lib/rubik/orchestrator/maestro_gate.rb +65 -0
- data/lib/rubik/orchestrator/provider_recovery.rb +90 -0
- data/lib/rubik/orchestrator/provider_resolver.rb +69 -0
- data/lib/rubik/orchestrator/shell_deps.rb +85 -0
- data/lib/rubik/orchestrator/step_executor.rb +328 -0
- data/lib/rubik/orchestrator/stream_manager.rb +118 -0
- data/lib/rubik/orchestrator/subagent/activation.rb +65 -0
- data/lib/rubik/orchestrator/subagent/budget.rb +108 -0
- data/lib/rubik/orchestrator/subagent/completion.rb +61 -0
- data/lib/rubik/orchestrator/subagent/concurrency_gate.rb +37 -0
- data/lib/rubik/orchestrator/subagent/conversation_id.rb +67 -0
- data/lib/rubik/orchestrator/subagent/dependency.rb +398 -0
- data/lib/rubik/orchestrator/subagent/instance.rb +384 -0
- data/lib/rubik/orchestrator/subagent/lifecycle.rb +118 -0
- data/lib/rubik/orchestrator/subagent/messaging.rb +61 -0
- data/lib/rubik/orchestrator/subagent/narration.rb +125 -0
- data/lib/rubik/orchestrator/subagent/notices.rb +209 -0
- data/lib/rubik/orchestrator/subagent/questioner.rb +80 -0
- data/lib/rubik/orchestrator/subagent/reaper.rb +98 -0
- data/lib/rubik/orchestrator/subagent/resilience.rb +70 -0
- data/lib/rubik/orchestrator/subagent/reuse.rb +127 -0
- data/lib/rubik/orchestrator/subagent/scoped_registry.rb +79 -0
- data/lib/rubik/orchestrator/subagent/step_runner.rb +240 -0
- data/lib/rubik/orchestrator/subagent/storage_id.rb +26 -0
- data/lib/rubik/orchestrator/subagent/supervisor.rb +327 -0
- data/lib/rubik/orchestrator/subagent/transcript_formatter.rb +141 -0
- data/lib/rubik/orchestrator/subagent/type_registry.rb +199 -0
- data/lib/rubik/orchestrator/subagent_notice_inbox.rb +80 -0
- data/lib/rubik/orchestrator/tool_call_id.rb +47 -0
- data/lib/rubik/orchestrator/tool_circuit_breaker.rb +91 -0
- data/lib/rubik/orchestrator/tool_history.rb +39 -0
- data/lib/rubik/orchestrator/turn_executor/subagent_channel.rb +102 -0
- data/lib/rubik/orchestrator/turn_executor/user_channel.rb +172 -0
- data/lib/rubik/orchestrator/turn_executor.rb +376 -0
- data/lib/rubik/orchestrator.rb +11 -0
- data/lib/rubik/platform/filesystem.rb +64 -0
- data/lib/rubik/platform/io_drain.rb +53 -0
- data/lib/rubik/platform/path.rb +210 -0
- data/lib/rubik/platform/process.rb +38 -0
- data/lib/rubik/platform/sanitize.rb +157 -0
- data/lib/rubik/platform.rb +113 -0
- data/lib/rubik/plugin/dsl.rb +51 -0
- data/lib/rubik/plugin/manager.rb +247 -0
- data/lib/rubik/plugin/storage.rb +195 -0
- data/lib/rubik/plugin.rb +10 -0
- data/lib/rubik/prompt/context_builder.rb +377 -0
- data/lib/rubik/prompt/context_compactor.rb +326 -0
- data/lib/rubik/prompt/conversation_rows.rb +74 -0
- data/lib/rubik/prompt/doctrine.rb +160 -0
- data/lib/rubik/prompt/emergency_compactor.rb +81 -0
- data/lib/rubik/prompt/services.rb +60 -0
- data/lib/rubik/prompt/token_estimator.rb +63 -0
- data/lib/rubik/prompt/tool_teachings.rb +78 -0
- data/lib/rubik/prompt.rb +13 -0
- data/lib/rubik/repair/loop.rb +95 -0
- data/lib/rubik/repair.rb +8 -0
- data/lib/rubik/repl/ast_warmup.rb +129 -0
- data/lib/rubik/repl/command_context.rb +214 -0
- data/lib/rubik/repl/command_parser.rb +48 -0
- data/lib/rubik/repl/commands/agent.rb +30 -0
- data/lib/rubik/repl/commands/agent_context.rb +289 -0
- data/lib/rubik/repl/commands/agent_execution.rb +342 -0
- data/lib/rubik/repl/commands/agent_models.rb +212 -0
- data/lib/rubik/repl/commands/agent_providers.rb +232 -0
- data/lib/rubik/repl/commands/base.rb +55 -0
- data/lib/rubik/repl/commands/buffer.rb +124 -0
- data/lib/rubik/repl/commands/dmesg.rb +87 -0
- data/lib/rubik/repl/commands/lsp.rb +235 -0
- data/lib/rubik/repl/commands/mcp.rb +194 -0
- data/lib/rubik/repl/commands/plugins.rb +208 -0
- data/lib/rubik/repl/commands/pruner.rb +71 -0
- data/lib/rubik/repl/commands/session.rb +183 -0
- data/lib/rubik/repl/commands/skills.rb +111 -0
- data/lib/rubik/repl/commands/storage.rb +85 -0
- data/lib/rubik/repl/commands/subagents.rb +195 -0
- data/lib/rubik/repl/commands/system.rb +85 -0
- data/lib/rubik/repl/commands/tasks.rb +120 -0
- data/lib/rubik/repl/commands/workspace.rb +443 -0
- data/lib/rubik/repl/commands.rb +44 -0
- data/lib/rubik/repl/composition.rb +32 -0
- data/lib/rubik/repl/config_wiring.rb +142 -0
- data/lib/rubik/repl/confirmations.rb +56 -0
- data/lib/rubik/repl/hooks.rb +47 -0
- data/lib/rubik/repl/input_execution.rb +323 -0
- data/lib/rubik/repl/input_loop.rb +213 -0
- data/lib/rubik/repl/interrupt_handler.rb +217 -0
- data/lib/rubik/repl/lifecycle.rb +410 -0
- data/lib/rubik/repl/paste_support.rb +88 -0
- data/lib/rubik/repl/pending_notice_queue.rb +60 -0
- data/lib/rubik/repl/presentation.rb +132 -0
- data/lib/rubik/repl/pruner/brief.rb +101 -0
- data/lib/rubik/repl/pruner/gate.rb +279 -0
- data/lib/rubik/repl/pruner/harness.rb +236 -0
- data/lib/rubik/repl/pruner/rules.rb +316 -0
- data/lib/rubik/repl/pruner/service.rb +82 -0
- data/lib/rubik/repl/pruner.rb +39 -0
- data/lib/rubik/repl/service_catalog.rb +374 -0
- data/lib/rubik/repl/setup_factories.rb +66 -0
- data/lib/rubik/repl/shell.rb +231 -0
- data/lib/rubik/repl/step_formatter.rb +324 -0
- data/lib/rubik/repl/tee_io.rb +105 -0
- data/lib/rubik/repl/terminal_guard.rb +157 -0
- data/lib/rubik/repl/turn_recorder.rb +234 -0
- data/lib/rubik/repl.rb +14 -0
- data/lib/rubik/runtime/ast_service.rb +78 -0
- data/lib/rubik/runtime/context.rb +335 -0
- data/lib/rubik/runtime/diagnostic_engine.rb +188 -0
- data/lib/rubik/runtime/discovery.rb +342 -0
- data/lib/rubik/runtime/fiber.rb +142 -0
- data/lib/rubik/runtime/fiber_ordering.rb +52 -0
- data/lib/rubik/runtime/fiber_retry_gate.rb +84 -0
- data/lib/rubik/runtime/index_service.rb +58 -0
- data/lib/rubik/runtime/loop_service.rb +73 -0
- data/lib/rubik/runtime/manager.rb +241 -0
- data/lib/rubik/runtime/overlay_storage.rb +481 -0
- data/lib/rubik/runtime/service_definition.rb +54 -0
- data/lib/rubik/runtime/service_registration.rb +33 -0
- data/lib/rubik/runtime/task_manager.rb +302 -0
- data/lib/rubik/runtime.rb +19 -0
- data/lib/rubik/sandbox/driver/base.rb +30 -0
- data/lib/rubik/sandbox/driver/bubblewrap.rb +111 -0
- data/lib/rubik/sandbox/driver/docker.rb +113 -0
- data/lib/rubik/sandbox/driver/podman.rb +98 -0
- data/lib/rubik/sandbox/driver/process.rb +190 -0
- data/lib/rubik/sandbox/driver.rb +91 -0
- data/lib/rubik/sandbox/runner.rb +218 -0
- data/lib/rubik/sandbox/shell_resolver.rb +178 -0
- data/lib/rubik/sandbox.rb +8 -0
- data/lib/rubik/security/blocked_guidance.rb +187 -0
- data/lib/rubik/security/classifier/classifier.rb +440 -0
- data/lib/rubik/security/classifier/commands/agent-browser.yaml +9 -0
- data/lib/rubik/security/classifier/commands/apt.yaml +25 -0
- data/lib/rubik/security/classifier/commands/awk.yaml +10 -0
- data/lib/rubik/security/classifier/commands/basename.yaml +7 -0
- data/lib/rubik/security/classifier/commands/bash-classify.yaml +8 -0
- data/lib/rubik/security/classifier/commands/bash.yaml +20 -0
- data/lib/rubik/security/classifier/commands/black.yaml +12 -0
- data/lib/rubik/security/classifier/commands/brew.yaml +31 -0
- data/lib/rubik/security/classifier/commands/builtin.yaml +8 -0
- data/lib/rubik/security/classifier/commands/cargo.yaml +114 -0
- data/lib/rubik/security/classifier/commands/cat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/chgrp.yaml +10 -0
- data/lib/rubik/security/classifier/commands/chmod.yaml +10 -0
- data/lib/rubik/security/classifier/commands/chown.yaml +10 -0
- data/lib/rubik/security/classifier/commands/column.yaml +7 -0
- data/lib/rubik/security/classifier/commands/comm.yaml +7 -0
- data/lib/rubik/security/classifier/commands/command.yaml +12 -0
- data/lib/rubik/security/classifier/commands/cp.yaml +7 -0
- data/lib/rubik/security/classifier/commands/crystal.yaml +63 -0
- data/lib/rubik/security/classifier/commands/curl.yaml +30 -0
- data/lib/rubik/security/classifier/commands/cut.yaml +7 -0
- data/lib/rubik/security/classifier/commands/date.yaml +10 -0
- data/lib/rubik/security/classifier/commands/df.yaml +7 -0
- data/lib/rubik/security/classifier/commands/diff.yaml +7 -0
- data/lib/rubik/security/classifier/commands/dig.yaml +7 -0
- data/lib/rubik/security/classifier/commands/dirname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/docker.yaml +177 -0
- data/lib/rubik/security/classifier/commands/du.yaml +7 -0
- data/lib/rubik/security/classifier/commands/echo.yaml +7 -0
- data/lib/rubik/security/classifier/commands/env.yaml +15 -0
- data/lib/rubik/security/classifier/commands/eslint.yaml +12 -0
- data/lib/rubik/security/classifier/commands/eval.yaml +8 -0
- data/lib/rubik/security/classifier/commands/exec.yaml +12 -0
- data/lib/rubik/security/classifier/commands/expand.yaml +7 -0
- data/lib/rubik/security/classifier/commands/false.yaml +7 -0
- data/lib/rubik/security/classifier/commands/file.yaml +7 -0
- data/lib/rubik/security/classifier/commands/find.yaml +25 -0
- data/lib/rubik/security/classifier/commands/fmt.yaml +7 -0
- data/lib/rubik/security/classifier/commands/fold.yaml +7 -0
- data/lib/rubik/security/classifier/commands/free.yaml +7 -0
- data/lib/rubik/security/classifier/commands/gawk.yaml +10 -0
- data/lib/rubik/security/classifier/commands/gcloud.yaml +979 -0
- data/lib/rubik/security/classifier/commands/gh.yaml +615 -0
- data/lib/rubik/security/classifier/commands/git.yaml +524 -0
- data/lib/rubik/security/classifier/commands/glab-discussion.yaml +21 -0
- data/lib/rubik/security/classifier/commands/glab.yaml +807 -0
- data/lib/rubik/security/classifier/commands/go.yaml +44 -0
- data/lib/rubik/security/classifier/commands/gog.yaml +983 -0
- data/lib/rubik/security/classifier/commands/gradle.yaml +92 -0
- data/lib/rubik/security/classifier/commands/gradlew.yaml +92 -0
- data/lib/rubik/security/classifier/commands/grep.yaml +7 -0
- data/lib/rubik/security/classifier/commands/groups.yaml +7 -0
- data/lib/rubik/security/classifier/commands/gunzip.yaml +8 -0
- data/lib/rubik/security/classifier/commands/gws.yaml +211 -0
- data/lib/rubik/security/classifier/commands/gzip.yaml +7 -0
- data/lib/rubik/security/classifier/commands/hadolint.yaml +9 -0
- data/lib/rubik/security/classifier/commands/halt.yaml +8 -0
- data/lib/rubik/security/classifier/commands/head.yaml +7 -0
- data/lib/rubik/security/classifier/commands/helm.yaml +129 -0
- data/lib/rubik/security/classifier/commands/hostname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/id.yaml +7 -0
- data/lib/rubik/security/classifier/commands/install.yaml +7 -0
- data/lib/rubik/security/classifier/commands/isort.yaml +14 -0
- data/lib/rubik/security/classifier/commands/java.yaml +35 -0
- data/lib/rubik/security/classifier/commands/javac.yaml +39 -0
- data/lib/rubik/security/classifier/commands/join.yaml +7 -0
- data/lib/rubik/security/classifier/commands/journalctl.yaml +14 -0
- data/lib/rubik/security/classifier/commands/jq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/kill.yaml +7 -0
- data/lib/rubik/security/classifier/commands/killall.yaml +7 -0
- data/lib/rubik/security/classifier/commands/kubectl.yaml +211 -0
- data/lib/rubik/security/classifier/commands/less.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ln.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ls.yaml +7 -0
- data/lib/rubik/security/classifier/commands/make.yaml +21 -0
- data/lib/rubik/security/classifier/commands/md5sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mise.yaml +579 -0
- data/lib/rubik/security/classifier/commands/mkdir.yaml +8 -0
- data/lib/rubik/security/classifier/commands/more.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mv.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mvn.yaml +83 -0
- data/lib/rubik/security/classifier/commands/mvnw.yaml +83 -0
- data/lib/rubik/security/classifier/commands/mypy.yaml +9 -0
- data/lib/rubik/security/classifier/commands/netstat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/nice.yaml +11 -0
- data/lib/rubik/security/classifier/commands/nl.yaml +7 -0
- data/lib/rubik/security/classifier/commands/node.yaml +20 -0
- data/lib/rubik/security/classifier/commands/nohup.yaml +9 -0
- data/lib/rubik/security/classifier/commands/npm.yaml +71 -0
- data/lib/rubik/security/classifier/commands/npx.yaml +42 -0
- data/lib/rubik/security/classifier/commands/nslookup.yaml +7 -0
- data/lib/rubik/security/classifier/commands/paste.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ping.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pip.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pip3.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pipx.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pkill.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pnpm.yaml +346 -0
- data/lib/rubik/security/classifier/commands/poetry.yaml +97 -0
- data/lib/rubik/security/classifier/commands/poweroff.yaml +8 -0
- data/lib/rubik/security/classifier/commands/prettier.yaml +13 -0
- data/lib/rubik/security/classifier/commands/printenv.yaml +7 -0
- data/lib/rubik/security/classifier/commands/printf.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ps.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pup.yaml +882 -0
- data/lib/rubik/security/classifier/commands/pwd.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pytest.yaml +7 -0
- data/lib/rubik/security/classifier/commands/python.yaml +9 -0
- data/lib/rubik/security/classifier/commands/python3.yaml +9 -0
- data/lib/rubik/security/classifier/commands/read.yaml +7 -0
- data/lib/rubik/security/classifier/commands/readlink.yaml +7 -0
- data/lib/rubik/security/classifier/commands/realpath.yaml +7 -0
- data/lib/rubik/security/classifier/commands/reboot.yaml +8 -0
- data/lib/rubik/security/classifier/commands/rev.yaml +7 -0
- data/lib/rubik/security/classifier/commands/rg.yaml +7 -0
- data/lib/rubik/security/classifier/commands/rm.yaml +8 -0
- data/lib/rubik/security/classifier/commands/rsync.yaml +12 -0
- data/lib/rubik/security/classifier/commands/rtk.yaml +140 -0
- data/lib/rubik/security/classifier/commands/ruff.yaml +29 -0
- data/lib/rubik/security/classifier/commands/rustfmt.yaml +14 -0
- data/lib/rubik/security/classifier/commands/scp.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sed.yaml +14 -0
- data/lib/rubik/security/classifier/commands/seq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sh.yaml +14 -0
- data/lib/rubik/security/classifier/commands/sha1sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sha256sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/shellcheck.yaml +10 -0
- data/lib/rubik/security/classifier/commands/shutdown.yaml +8 -0
- data/lib/rubik/security/classifier/commands/slack.yaml +113 -0
- data/lib/rubik/security/classifier/commands/sleep.yaml +8 -0
- data/lib/rubik/security/classifier/commands/snip.yaml +79 -0
- data/lib/rubik/security/classifier/commands/sort.yaml +10 -0
- data/lib/rubik/security/classifier/commands/ss.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ssh.yaml +41 -0
- data/lib/rubik/security/classifier/commands/stat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/stdbuf.yaml +15 -0
- data/lib/rubik/security/classifier/commands/strace.yaml +20 -0
- data/lib/rubik/security/classifier/commands/strings.yaml +11 -0
- data/lib/rubik/security/classifier/commands/sudo.yaml +24 -0
- data/lib/rubik/security/classifier/commands/systemctl.yaml +58 -0
- data/lib/rubik/security/classifier/commands/systemd-run.yaml +30 -0
- data/lib/rubik/security/classifier/commands/tac.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tail.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tar.yaml +25 -0
- data/lib/rubik/security/classifier/commands/tee.yaml +8 -0
- data/lib/rubik/security/classifier/commands/terraform.yaml +70 -0
- data/lib/rubik/security/classifier/commands/test.yaml +7 -0
- data/lib/rubik/security/classifier/commands/time.yaml +8 -0
- data/lib/rubik/security/classifier/commands/timeout.yaml +18 -0
- data/lib/rubik/security/classifier/commands/top.yaml +7 -0
- data/lib/rubik/security/classifier/commands/touch.yaml +8 -0
- data/lib/rubik/security/classifier/commands/tr.yaml +7 -0
- data/lib/rubik/security/classifier/commands/traceroute.yaml +7 -0
- data/lib/rubik/security/classifier/commands/true.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tsc.yaml +16 -0
- data/lib/rubik/security/classifier/commands/type.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/unexpand.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uniq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/unzip.yaml +10 -0
- data/lib/rubik/security/classifier/commands/uptime.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uv.yaml +156 -0
- data/lib/rubik/security/classifier/commands/wait.yaml +11 -0
- data/lib/rubik/security/classifier/commands/wc.yaml +7 -0
- data/lib/rubik/security/classifier/commands/wget.yaml +7 -0
- data/lib/rubik/security/classifier/commands/whereis.yaml +7 -0
- data/lib/rubik/security/classifier/commands/which.yaml +7 -0
- data/lib/rubik/security/classifier/commands/whoami.yaml +7 -0
- data/lib/rubik/security/classifier/commands/xargs.yaml +26 -0
- data/lib/rubik/security/classifier/commands/xmllint.yaml +7 -0
- data/lib/rubik/security/classifier/commands/yarn.yaml +72 -0
- data/lib/rubik/security/classifier/commands/yes.yaml +7 -0
- data/lib/rubik/security/classifier/commands/yq.yaml +10 -0
- data/lib/rubik/security/classifier/commands/zip.yaml +8 -0
- data/lib/rubik/security/classifier/commands/zsh.yaml +14 -0
- data/lib/rubik/security/classifier/database.rb +216 -0
- data/lib/rubik/security/classifier/matcher/delegation.rb +190 -0
- data/lib/rubik/security/classifier/matcher/options.rb +245 -0
- data/lib/rubik/security/classifier/matcher/subcommands.rb +103 -0
- data/lib/rubik/security/classifier/matcher.rb +249 -0
- data/lib/rubik/security/classifier/models.rb +170 -0
- data/lib/rubik/security/classifier/parser.rb +23 -0
- data/lib/rubik/security/classifier/parsers/bash.rb +280 -0
- data/lib/rubik/security/classifier/parsers/bash_extractors.rb +221 -0
- data/lib/rubik/security/classifier/parsers/powershell.rb +185 -0
- data/lib/rubik/security/classifier/pwsh_rules.rb +224 -0
- data/lib/rubik/security/classifier.rb +25 -0
- data/lib/rubik/security/errors.rb +39 -0
- data/lib/rubik/security/guard.rb +954 -0
- data/lib/rubik/security/redactor.rb +89 -0
- data/lib/rubik/security.rb +11 -0
- data/lib/rubik/session/manager.rb +331 -0
- data/lib/rubik/session/recorder.rb +149 -0
- data/lib/rubik/session/replayer.rb +65 -0
- data/lib/rubik/session.rb +10 -0
- data/lib/rubik/skills/frontmatter.rb +41 -0
- data/lib/rubik/skills/manager.rb +223 -0
- data/lib/rubik/skills/registry.rb +64 -0
- data/lib/rubik/skills/skill.rb +33 -0
- data/lib/rubik/skills/storage.rb +77 -0
- data/lib/rubik/skills/validator.rb +116 -0
- data/lib/rubik/skills.rb +20 -0
- data/lib/rubik/storage/folding.rb +23 -0
- data/lib/rubik/storage/layout.rb +145 -0
- data/lib/rubik/storage/render.rb +97 -0
- data/lib/rubik/storage/service.rb +42 -0
- data/lib/rubik/storage/store.rb +514 -0
- data/lib/rubik/storage/transcript_indexer.rb +116 -0
- data/lib/rubik/storage/uri_reader.rb +168 -0
- data/lib/rubik/storage/vakanuvis/annal_writer.rb +92 -0
- data/lib/rubik/storage/vakanuvis/distill_gate.rb +323 -0
- data/lib/rubik/storage/vakanuvis/gate.rb +250 -0
- data/lib/rubik/storage/vakanuvis/harness.rb +421 -0
- data/lib/rubik/storage/vakanuvis/memory_steward.rb +163 -0
- data/lib/rubik/storage/vakanuvis/service.rb +69 -0
- data/lib/rubik/storage/vakanuvis/vocabulary.rb +99 -0
- data/lib/rubik/storage/vakanuvis/wiki_skeleton.rb +67 -0
- data/lib/rubik/storage/vakanuvis.rb +77 -0
- data/lib/rubik/storage.rb +29 -0
- data/lib/rubik/tools/base.rb +107 -0
- data/lib/rubik/tools/builtins/dmesg.rb +74 -0
- data/lib/rubik/tools/builtins/exec.rb +293 -0
- data/lib/rubik/tools/builtins/files.rb +354 -0
- data/lib/rubik/tools/builtins/goal_ledger.rb +89 -0
- data/lib/rubik/tools/builtins/grep.rb +281 -0
- data/lib/rubik/tools/builtins/interactive.rb +175 -0
- data/lib/rubik/tools/builtins/lifecycle.rb +359 -0
- data/lib/rubik/tools/builtins/lsp.rb +283 -0
- data/lib/rubik/tools/builtins/pruner.rb +88 -0
- data/lib/rubik/tools/builtins/runtime.rb +120 -0
- data/lib/rubik/tools/builtins/send_message.rb +109 -0
- data/lib/rubik/tools/builtins/show_file.rb +84 -0
- data/lib/rubik/tools/builtins/skill.rb +72 -0
- data/lib/rubik/tools/builtins/storage.rb +232 -0
- data/lib/rubik/tools/builtins/subagent.rb +356 -0
- data/lib/rubik/tools/builtins/subagent_lifecycle.rb +72 -0
- data/lib/rubik/tools/builtins/symbols.rb +209 -0
- data/lib/rubik/tools/builtins/synthesis.rb +314 -0
- data/lib/rubik/tools/builtins/task.rb +168 -0
- data/lib/rubik/tools/builtins/unsynthesize.rb +111 -0
- data/lib/rubik/tools/builtins/vakanuvis.rb +161 -0
- data/lib/rubik/tools/builtins.rb +30 -0
- data/lib/rubik/tools/catalog.rb +70 -0
- data/lib/rubik/tools/declaration.rb +90 -0
- data/lib/rubik/tools/diagnostic_reporter.rb +283 -0
- data/lib/rubik/tools/fuzzy_matcher.rb +172 -0
- data/lib/rubik/tools/native_search.rb +180 -0
- data/lib/rubik/tools/output.rb +54 -0
- data/lib/rubik/tools/preflight_runner.rb +209 -0
- data/lib/rubik/tools/registry.rb +247 -0
- data/lib/rubik/tools/result.rb +71 -0
- data/lib/rubik/tools/services.rb +96 -0
- data/lib/rubik/tools/truncator.rb +78 -0
- data/lib/rubik/tools/uri_resolver.rb +195 -0
- data/lib/rubik/tools.rb +14 -0
- data/lib/rubik/ui/approvals.rb +52 -0
- data/lib/rubik/ui/banner.rb +43 -0
- data/lib/rubik/ui/capability.rb +122 -0
- data/lib/rubik/ui/dialog.rb +106 -0
- data/lib/rubik/ui/diff_viewer.rb +78 -0
- data/lib/rubik/ui/frame.rb +95 -0
- data/lib/rubik/ui/markdown_stream.rb +107 -0
- data/lib/rubik/ui/panel.rb +98 -0
- data/lib/rubik/ui/renderer.rb +206 -0
- data/lib/rubik/ui/spinner.rb +120 -0
- data/lib/rubik/ui/statusline.rb +134 -0
- data/lib/rubik/ui/theme.rb +118 -0
- data/lib/rubik/ui.rb +57 -0
- data/lib/rubik/uris.rb +101 -0
- data/lib/rubik/version.rb +5 -0
- data/lib/rubik.rb +65 -0
- metadata +770 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Prompt
|
|
5
|
+
# Frozen-in view of everything the prompt renderer needs (architecture
|
|
6
|
+
# review 2026-10-01, candidate 2). Unlike Tools::Services there is NO
|
|
7
|
+
# fresh tier and NO coeffect lookup here: the prompt layer never
|
|
8
|
+
# constructs services (a Guard/Sandbox built here would be policy the
|
|
9
|
+
# caller never granted) and never resolves coeffects — the orchestrator
|
|
10
|
+
# resolves, the prompt renders. Unsatisfied slots degrade to empty
|
|
11
|
+
# sections, exactly matching the builder's historical nil-tolerant
|
|
12
|
+
# rendering.
|
|
13
|
+
#
|
|
14
|
+
# Owns: the slot list (the Struct members are the SSOT) and its frozen
|
|
15
|
+
# snapshot semantics.
|
|
16
|
+
# Leaves: where values come from — the composition root builds the
|
|
17
|
+
# view (from_shell / explicit kwargs); coeffect resolution (skills
|
|
18
|
+
# index, type registry) stays in the orchestrator.
|
|
19
|
+
Services = Struct.new(
|
|
20
|
+
:config, :commands, :buffer_manager, :snapshot_store, :context_index,
|
|
21
|
+
:lsp_manager, :skills_index, :type_registry, :context_limit,
|
|
22
|
+
:max_output_tokens, :root_dir, :extra_context,
|
|
23
|
+
keyword_init: true
|
|
24
|
+
) do
|
|
25
|
+
def initialize(config: nil, commands: nil, buffer_manager: nil, snapshot_store: nil, context_index: nil,
|
|
26
|
+
lsp_manager: nil, skills_index: nil, type_registry: nil, context_limit: nil,
|
|
27
|
+
max_output_tokens: nil, root_dir: Dir.pwd, extra_context: "")
|
|
28
|
+
super
|
|
29
|
+
freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Reader-only shell view: a public reader contributes its value, a
|
|
33
|
+
# reader-less duck type contributes nothing (graceful degradation,
|
|
34
|
+
# never a raise — an ivar without a reader is invisible here, by
|
|
35
|
+
# design). Explicit non-nil overrides beat shell readers, preserving
|
|
36
|
+
# the historical kwarg-over-shell precedence
|
|
37
|
+
# (test_from_shell_overrides_beat_shell_readers).
|
|
38
|
+
# root_dir/context_limit/max_output_tokens/skills_index/extra_context
|
|
39
|
+
# are per-run values, not shell state — they arrive via overrides.
|
|
40
|
+
def self.from_shell(shell, **overrides)
|
|
41
|
+
base = {
|
|
42
|
+
config: reader(shell, :config),
|
|
43
|
+
commands: reader(shell, :commands),
|
|
44
|
+
buffer_manager: reader(shell, :buffer_manager),
|
|
45
|
+
snapshot_store: reader(shell, :snapshot_store),
|
|
46
|
+
context_index: reader(shell, :context_index),
|
|
47
|
+
lsp_manager: reader(shell, :lsp_manager),
|
|
48
|
+
type_registry: reader(shell, :subagent_supervisor)&.type_registry
|
|
49
|
+
}
|
|
50
|
+
new(**base, **overrides.compact)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.reader(shell, name)
|
|
54
|
+
shell.respond_to?(name) ? shell.public_send(name) : nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private_class_method :reader
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../platform"
|
|
5
|
+
require_relative "conversation_rows"
|
|
6
|
+
|
|
7
|
+
module Rubik
|
|
8
|
+
module Prompt
|
|
9
|
+
module TokenEstimator
|
|
10
|
+
CHARS_PER_TOKEN = 3.8
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def estimate_text(text)
|
|
15
|
+
return 0 if text.nil?
|
|
16
|
+
|
|
17
|
+
str = text.to_s
|
|
18
|
+
return 0 if str.empty?
|
|
19
|
+
|
|
20
|
+
(str.length / CHARS_PER_TOKEN).ceil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def estimate_message(msg)
|
|
24
|
+
return 0 unless ConversationRows.row?(msg)
|
|
25
|
+
|
|
26
|
+
tokens = 4 # Base message overhead (role delimiter, headers)
|
|
27
|
+
role = ConversationRows.value_of(msg, :role)
|
|
28
|
+
tokens += estimate_text(role) if role
|
|
29
|
+
|
|
30
|
+
content = ConversationRows.value_of(msg, :content)
|
|
31
|
+
tokens += estimate_text(content) if content
|
|
32
|
+
|
|
33
|
+
tool_calls = msg[:tool_calls] || msg["tool_calls"]
|
|
34
|
+
if tool_calls && !tool_calls.empty?
|
|
35
|
+
tool_calls_str = tool_calls.is_a?(String) ? Rubik::Platform.clean_utf8(tool_calls) : Rubik::Platform.safe_json_generate(tool_calls)
|
|
36
|
+
tokens += estimate_text(tool_calls_str)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
tool_call_id = msg[:tool_call_id] || msg["tool_call_id"]
|
|
40
|
+
tokens += estimate_text(tool_call_id) if tool_call_id
|
|
41
|
+
|
|
42
|
+
tool_name = msg[:tool_name] || msg["tool_name"]
|
|
43
|
+
tokens += estimate_text(tool_name) if tool_name
|
|
44
|
+
|
|
45
|
+
tokens
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def estimate_history(messages)
|
|
49
|
+
return 0 unless messages.is_a?(Array)
|
|
50
|
+
|
|
51
|
+
messages.sum { |msg| estimate_message(msg) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def estimate_tool_schemas(schemas)
|
|
55
|
+
return 0 if schemas.nil?
|
|
56
|
+
return 0 if schemas.respond_to?(:empty?) && schemas.empty?
|
|
57
|
+
|
|
58
|
+
json_str = schemas.is_a?(String) ? Rubik::Platform.clean_utf8(schemas) : Rubik::Platform.safe_json_generate(schemas)
|
|
59
|
+
estimate_text(json_str)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Prompt
|
|
5
|
+
# Tool teachings keyed by canonical tool name (architecture review
|
|
6
|
+
# 2026-10-01, candidate 1): the sentences that teach HOW a specific tool
|
|
7
|
+
# is used live here, one key per tool, instead of being embedded unnamed
|
|
8
|
+
# in Doctrine.operational_rules_xml's heredoc. Identity (the tool) lives in
|
|
9
|
+
# its Tools::Declaration; prose (the presentation) lives here — a prompt
|
|
10
|
+
# layer concern. test_tool_teachings welds the two: every key must
|
|
11
|
+
# resolve to a declared (Catalog) tool, so a rename that forgets the
|
|
12
|
+
# prompt fails the suite.
|
|
13
|
+
#
|
|
14
|
+
# The strings are interpolated into the operational rules HEREDOC at the
|
|
15
|
+
# same positions they originally occupied — the rendered rules are
|
|
16
|
+
# byte-identical (pinned by the substring tests in
|
|
17
|
+
# test_structured_context_builder, test_agent_command_contract,
|
|
18
|
+
# test_buffer_apply_conflict, test_repl_tool_separation_and_guardrails).
|
|
19
|
+
module ToolTeachings
|
|
20
|
+
TEACHINGS = {
|
|
21
|
+
"replace_file_content" =>
|
|
22
|
+
"For modifying existing files, prefer `replace_file_content` with exact verbatim `target_content` " \
|
|
23
|
+
"(or `old_content`) and `replacement_content` (or `new_content`).",
|
|
24
|
+
"write_file" =>
|
|
25
|
+
"For creating new files or completely rewriting files, use `write_file`.",
|
|
26
|
+
"apply_buffer" =>
|
|
27
|
+
"All edits stage safely into the in-memory buffer. Call `apply_buffer` to persist buffer edits to disk. " \
|
|
28
|
+
"If `apply_buffer` reports `[CONFLICT]` due to external modifications (e.g. from code formatters or " \
|
|
29
|
+
"external processes), inspect latest disk changes with `read_file(path: \"...\", reload: true)` or " \
|
|
30
|
+
"overwrite deliberately with `apply_buffer(force: true)`.",
|
|
31
|
+
"synthesize_tool" =>
|
|
32
|
+
"When you need a specialized capability not available in the built-in toolbelt (such as custom AST " \
|
|
33
|
+
"transformers, data converters, or domain-specific benchmarkers), dynamically create and register your " \
|
|
34
|
+
"own tool at runtime using `synthesize_tool(name: '...', description: '...', code: '->(args) { ... }')`.",
|
|
35
|
+
"storage" =>
|
|
36
|
+
"Persist agent workfiles — goals, plans, memory notes, wiki knowledge, reviews — in the central " \
|
|
37
|
+
"`storage` database (`storage` tool: put/get/list/search/set_ref/delete), NOT as scattered files in " \
|
|
38
|
+
"the repository worktree. Read entities back with `read_file(path: 'rubik://storage/<type>/<id>')`; " \
|
|
39
|
+
"browse with `rubik://storage/index` and `rubik://storage/<type>` listings.",
|
|
40
|
+
"run_command" =>
|
|
41
|
+
"GitHub facts must be verified, not assumed: after `gh pr merge` (which can succeed with EMPTY output) " \
|
|
42
|
+
"confirm the merge with `gh pr view <number> --json state`; `mergeable: UNKNOWN` right after a push means " \
|
|
43
|
+
"GitHub is still recomputing — wait briefly and re-check instead of rebasing; `gh pr checks` exits 1 both " \
|
|
44
|
+
"for failing checks and for repos with no CI — disambiguate with `gh pr view --json statusCheckRollup`. " \
|
|
45
|
+
"In repos that intentionally commit no lockfile, install dependencies with `npm install --no-package-lock` " \
|
|
46
|
+
"(or the package manager's equivalent) so setup does not dirty the worktree with a new lockfile.",
|
|
47
|
+
"show_file" =>
|
|
48
|
+
"When the user asks to SEE a stored entity (review report, plan, ...), use " \
|
|
49
|
+
"`show_file(path: 'rubik://storage/...')` — it renders the full content directly to their terminal " \
|
|
50
|
+
"while your context stays lean; only fall back to read_file when YOU must reason about the content.",
|
|
51
|
+
"vakanuvis" =>
|
|
52
|
+
"The background chronicler keeps compiled knowledge (memory cards, wiki pages) in rubik://storage. " \
|
|
53
|
+
"At task start — and again after any compaction — ask it with `vakanuvis(action: 'recall', query: '...')` " \
|
|
54
|
+
"instead of pre-loading pages: it returns a deterministic digest plus rubik:// source links. When " \
|
|
55
|
+
"dispatching a subagent, paste the relevant recall digest (or its rubik:// link) into the task brief. " \
|
|
56
|
+
"At the end of a task, persist only the durable knowledge with `vakanuvis(action: 'record', topic: '...', " \
|
|
57
|
+
"body: '...')` — memory writes go through record (ref minting, upsert, supersede come free), never " \
|
|
58
|
+
"through raw storage puts. Recall is keyword-AND over card/page text, so END every recorded body and " \
|
|
59
|
+
"digest query with its own vocabulary: include a `Keywords:` line listing 5-10 synonyms in BOTH working " \
|
|
60
|
+
"languages (e.g. TR + EN) — a Turkish query cannot otherwise find an English page. If recall is too " \
|
|
61
|
+
"shallow, queue `vakanuvis(action: 'digest', query: '...')`: it returns a deterministic receipt and " \
|
|
62
|
+
"compiles the wiki page asynchronously. Make the session close a standard digest point: review the " \
|
|
63
|
+
"session's new cards, queue one digest per open topic, and let the home index refresh.",
|
|
64
|
+
"prune_context" =>
|
|
65
|
+
"When long sessions leave stale exploration and superseded tool output weighing on the ROOT window, " \
|
|
66
|
+
"check `prune_context(action: 'status')` (window estimate vs ceiling, staged plan state) and force a " \
|
|
67
|
+
"cleanup with `prune_context(action: 'prune', reason: '...')`: it queues an LLM-proposed, " \
|
|
68
|
+
"harness-validated keep/drop/summarize plan that applies at the NEXT turn boundary — the call returns " \
|
|
69
|
+
"a deterministic receipt and never blocks. Goal/task statements, decisions and recent context are " \
|
|
70
|
+
"always preserved; deterministic compaction remains the emergency safety net."
|
|
71
|
+
}.freeze
|
|
72
|
+
|
|
73
|
+
module_function
|
|
74
|
+
|
|
75
|
+
def teaching_for(tool_name) = TEACHINGS[tool_name.to_s]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/rubik/prompt.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "prompt/context_builder"
|
|
4
|
+
require_relative "prompt/doctrine"
|
|
5
|
+
require_relative "prompt/services"
|
|
6
|
+
require_relative "prompt/token_estimator"
|
|
7
|
+
require_relative "prompt/context_compactor"
|
|
8
|
+
require_relative "prompt/emergency_compactor"
|
|
9
|
+
|
|
10
|
+
module Rubik
|
|
11
|
+
module Prompt
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "tempfile"
|
|
5
|
+
|
|
6
|
+
module Rubik
|
|
7
|
+
module Repair
|
|
8
|
+
class Loop
|
|
9
|
+
attr_reader :max_attempts, :attempts_made, :config
|
|
10
|
+
|
|
11
|
+
def initialize(provider:, buffer_manager:, snapshot_store:, adapter_registry:, max_attempts: nil, config: nil)
|
|
12
|
+
@provider = provider
|
|
13
|
+
@buffer_manager = buffer_manager
|
|
14
|
+
@snapshot_store = snapshot_store
|
|
15
|
+
@adapter_registry = adapter_registry
|
|
16
|
+
@config = config
|
|
17
|
+
@max_attempts = (max_attempts || config&.fetch("repair.max_attempts", 3) || 3).to_i
|
|
18
|
+
@attempts_made = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def repair(diagnostics, target_file, lang = :ruby)
|
|
22
|
+
@attempts_made = 0
|
|
23
|
+
current_diag = diagnostics
|
|
24
|
+
|
|
25
|
+
while @attempts_made < @max_attempts
|
|
26
|
+
@attempts_made += 1
|
|
27
|
+
|
|
28
|
+
@snapshot_store.create_snapshot(@buffer_manager)
|
|
29
|
+
|
|
30
|
+
prompt = build_repair_prompt(current_diag, target_file, @attempts_made)
|
|
31
|
+
res = @provider.complete([{ role: "user", content: prompt }])
|
|
32
|
+
proposed_code = extract_code(res.text)
|
|
33
|
+
|
|
34
|
+
@buffer_manager.write(target_file, proposed_code)
|
|
35
|
+
|
|
36
|
+
# Verify syntax against in-memory buffer via tempfile
|
|
37
|
+
check_res = check_buffer_syntax(target_file, lang)
|
|
38
|
+
|
|
39
|
+
return { ok: true, attempts: @attempts_made, code: proposed_code } if check_res[:ok]
|
|
40
|
+
|
|
41
|
+
current_diag = Rubik::Diagnostics::Parser.parse(check_res[:output], lang)
|
|
42
|
+
@snapshot_store.undo(@buffer_manager)
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
{ ok: false, attempts: @attempts_made, last_diagnostics: current_diag }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def check_buffer_syntax(target_file, lang)
|
|
52
|
+
adapter = @adapter_registry.get(lang.to_s)
|
|
53
|
+
return { ok: true } unless adapter
|
|
54
|
+
|
|
55
|
+
content = @buffer_manager.read(target_file)
|
|
56
|
+
ext = File.extname(target_file)
|
|
57
|
+
|
|
58
|
+
Tempfile.create(["rubik_repair_", ext]) do |tmp|
|
|
59
|
+
tmp.write(content)
|
|
60
|
+
tmp.flush
|
|
61
|
+
adapter.check_syntax(tmp.path)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def build_repair_prompt(diagnostics, target_file, attempt_num)
|
|
66
|
+
diag_str = diagnostics.map { |d| d.format_prompt_snippet(nil, @buffer_manager.root_dir, @buffer_manager) }.join("\n\n")
|
|
67
|
+
current_content = @buffer_manager.read(target_file) || ""
|
|
68
|
+
|
|
69
|
+
<<~PROMPT
|
|
70
|
+
You are repairing code that failed with compiler/test errors.
|
|
71
|
+
Attempt #{attempt_num} of #{@max_attempts}.
|
|
72
|
+
|
|
73
|
+
Target File: #{target_file}
|
|
74
|
+
Current Content:
|
|
75
|
+
```
|
|
76
|
+
#{current_content}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Diagnostics:
|
|
80
|
+
#{diag_str}
|
|
81
|
+
|
|
82
|
+
Output ONLY the corrected code for #{target_file} wrapped in ```code``` blocks.
|
|
83
|
+
PROMPT
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def extract_code(text)
|
|
87
|
+
if text.include?("```")
|
|
88
|
+
text.split("```").at(1).to_s.sub(/^[a-z]+\n/, "").strip
|
|
89
|
+
else
|
|
90
|
+
text.strip
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/lib/rubik/repair.rb
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module REPL
|
|
5
|
+
# Öneri 3 (Kademe 4): Shell'den ayrılan AST ısınma bloğu. warm_ast_graph
|
|
6
|
+
# ısınmayı senkron ya da arka plan thread'inde tetikler; stop_background_indexer
|
|
7
|
+
# ve join_indexer_safely indexer thread'inin çökmesiz teardown'unu (Audit 1)
|
|
8
|
+
# sağlar; perform_warm_ast_graph delta tarama → indeksleme → stale temizliği →
|
|
9
|
+
# cache kalıcılığı zincirini yürütür.
|
|
10
|
+
# Durum Shell'de yaşar (@indexer_thread, @buffer_manager, @context_index,
|
|
11
|
+
# @ast_graph, @cache_store, @logger, @event_bus); mixin yalnızca ısınma
|
|
12
|
+
# politikasını kodlar.
|
|
13
|
+
module AstWarmup
|
|
14
|
+
# Öneri 3 (Kademe 4): warm_ast_graph / stop_background_indexer /
|
|
15
|
+
# join_indexer_safely / perform_warm_ast_graph,
|
|
16
|
+
# lib/rubik/repl/ast_warmup.rb mixin'ine taşındı (AST grafiği ısınma ve
|
|
17
|
+
# arka plan indexer yaşam döngüsü).
|
|
18
|
+
|
|
19
|
+
def warm_ast_graph(async: true)
|
|
20
|
+
if async
|
|
21
|
+
stop_background_indexer
|
|
22
|
+
# Reset the cooperative-stop flag so repeated warm cycles behave the
|
|
23
|
+
# same as the first (per-turn scopes, MCP reconnects, tool epochs).
|
|
24
|
+
@indexer_stop_requested = false
|
|
25
|
+
@indexer_thread = Thread.new do
|
|
26
|
+
perform_warm_ast_graph
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
@logger&.warn("AST graph background indexer thread error: #{e.class}: #{e.message}")
|
|
29
|
+
end
|
|
30
|
+
ctx = @runtime_manager&.context || (respond_to?(:context) ? context : nil)
|
|
31
|
+
if ctx.respond_to?(:register_disposable) && ctx != @indexer_disposable_ctx
|
|
32
|
+
@indexer_disposable_ctx = ctx
|
|
33
|
+
ctx.register_disposable { stop_background_indexer }
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
perform_warm_ast_graph
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Single-homed "workspace roots changed → resync index → re-warm AST"
|
|
41
|
+
# sequence (formerly hand-wired in !workspace add/remove and reactive
|
|
42
|
+
# config wiring). sync_roots already performs additional_roots= +
|
|
43
|
+
# full_scan + fiber reconcile; the sync re-warm keeps symbols fresh
|
|
44
|
+
# before control returns to the caller.
|
|
45
|
+
def sync_workspace_index!
|
|
46
|
+
return unless @context_index
|
|
47
|
+
|
|
48
|
+
Rubik::Runtime::IndexService.sync_roots(
|
|
49
|
+
@runtime_manager, @context_index,
|
|
50
|
+
additional_roots: @buffer_manager&.additional_roots,
|
|
51
|
+
ast_graph: @ast_graph, cache_store: @cache_store
|
|
52
|
+
)
|
|
53
|
+
warm_ast_graph(async: false)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Audit 1: teardown/join of the background indexer never crashes shutdown
|
|
57
|
+
# or blocks symbol lookups; detail surfaced via debug_warn. The bounded
|
|
58
|
+
# join mechanics live in Concurrency::Join; the failure log channel
|
|
59
|
+
# stays here.
|
|
60
|
+
#
|
|
61
|
+
# Cooperative cancellation, NOT Thread#kill: a one-shot warm cycle may run
|
|
62
|
+
# perform_warm_ast_graph's delta-scan -> index -> purge -> save_to_cache
|
|
63
|
+
# chain, and #kill would abort it between save_to_cache and completion,
|
|
64
|
+
# leaving a partially written / inconsistent cache (AGENTS.md §5 leaked
|
|
65
|
+
# side-effects + partial-write). The worker checks @indexer_stop_requested
|
|
66
|
+
# before committing the cache; here we only request + graceful-join.
|
|
67
|
+
def stop_background_indexer
|
|
68
|
+
return unless @indexer_thread
|
|
69
|
+
|
|
70
|
+
@indexer_stop_requested = true
|
|
71
|
+
join_indexer_safely
|
|
72
|
+
@indexer_thread = nil unless @indexer_thread&.alive?
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
debug_warn("Background AST indexer teardown failed: #{e.class}: #{e.message}")
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def join_indexer_safely(timeout = nil)
|
|
79
|
+
result = Rubik::Concurrency::Join.thread(@indexer_thread, timeout: timeout)
|
|
80
|
+
debug_warn("Background AST indexer join failed: #{result.class}: #{result.message}") if result.is_a?(StandardError)
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def perform_warm_ast_graph
|
|
85
|
+
root_d = @buffer_manager&.root_dir || Dir.pwd
|
|
86
|
+
delta = @context_index.incremental_scan
|
|
87
|
+
indexed_files = @ast_graph.file_symbols.keys
|
|
88
|
+
|
|
89
|
+
files_to_index = (delta[:added] + delta[:modified]).dup
|
|
90
|
+
|
|
91
|
+
@context_index.files.each_key do |rel_file|
|
|
92
|
+
break if @indexer_stop_requested
|
|
93
|
+
|
|
94
|
+
full_path = File.join(root_d, rel_file)
|
|
95
|
+
ext = File.extname(rel_file).downcase
|
|
96
|
+
files_to_index << rel_file if Rubik::AST::Extractor.supported_extensions.include?(ext) && !(indexed_files.include?(full_path) || indexed_files.include?(rel_file))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
files_to_index.uniq.each do |rel_file|
|
|
100
|
+
break if @indexer_stop_requested
|
|
101
|
+
|
|
102
|
+
full_path = File.join(root_d, rel_file)
|
|
103
|
+
ext = File.extname(rel_file).downcase
|
|
104
|
+
@ast_graph.index_file(full_path) if Rubik::AST::Extractor.supported_extensions.include?(ext) && File.exist?(full_path)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
delta[:deleted].each do |rel_file|
|
|
108
|
+
break if @indexer_stop_requested
|
|
109
|
+
|
|
110
|
+
full_path = File.join(root_d, rel_file)
|
|
111
|
+
@ast_graph.remove_file(full_path)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
return if @indexer_stop_requested
|
|
115
|
+
|
|
116
|
+
purged = Rubik::Runtime::ASTService.purge_stale_entries!(@ast_graph, @context_index, root_d)
|
|
117
|
+
|
|
118
|
+
if @cache_store && File.directory?(root_d) && !@indexer_stop_requested &&
|
|
119
|
+
(!files_to_index.empty? || !delta[:deleted].empty? || purged.positive?)
|
|
120
|
+
@ast_graph.save_to_cache(@cache_store)
|
|
121
|
+
end
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
@logger&.warn("AST graph warm failed: #{e.message}")
|
|
124
|
+
@event_bus&.emit(:ast_warm_failed, error: e.message)
|
|
125
|
+
nil
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "service_catalog"
|
|
4
|
+
require_relative "../session/replayer"
|
|
5
|
+
|
|
6
|
+
module Rubik
|
|
7
|
+
module REPL
|
|
8
|
+
# Per-dispatch, closed view of the Shell for bang-command handlers —
|
|
9
|
+
# the §7.1 ShellDeps pattern applied to the command axis. Built inside
|
|
10
|
+
# InputExecution#execute_command, AFTER with_output has swapped the
|
|
11
|
+
# capture TeeIO in, so ctx.out is exactly the stream a handler should
|
|
12
|
+
# print to on both the human and the exec_repl_command path (recorder
|
|
13
|
+
# capture and LLM-history truncation keep working unchanged).
|
|
14
|
+
#
|
|
15
|
+
# Closed surface (frozen by test_command_context):
|
|
16
|
+
# · out / output : the active (capture-aware) IO. `output` exists so
|
|
17
|
+
# the UI collaborators (Panel / DiffViewer / Approvals) can take ctx
|
|
18
|
+
# where they previously took the shell — structural duck, UI files
|
|
19
|
+
# untouched.
|
|
20
|
+
# · services : derived from ServiceCatalog.reader_names — the SSOT.
|
|
21
|
+
# Adding a catalog service extends ctx automatically; optional
|
|
22
|
+
# services read as nil (no respond_to? probing).
|
|
23
|
+
# · rebindable : conversation_history / active_loop (get + set) and
|
|
24
|
+
# conversation_route_stamp (write-only) — the ONLY Shell state a
|
|
25
|
+
# command may rebind. Writers delegate to the Shell accessors; ctx
|
|
26
|
+
# never shadows them.
|
|
27
|
+
# · effects : the named verbs below. Anything else raises
|
|
28
|
+
# NoMethodError — extend deliberately, together with the freeze test
|
|
29
|
+
# and the boundary guard.
|
|
30
|
+
#
|
|
31
|
+
# Handlers are invoked as handler.call(args, ctx). There is no
|
|
32
|
+
# register_command, no ivar access and no method_missing: the interface
|
|
33
|
+
# a command sees is exactly this list.
|
|
34
|
+
class CommandContext
|
|
35
|
+
# Shell-owned readers outside the catalog (lifecycle state, table, IO).
|
|
36
|
+
SHELL_READERS = [:indexer_thread, :input, :commands].freeze
|
|
37
|
+
|
|
38
|
+
# Derived from the SSOT at load time; frozen so the surface cannot
|
|
39
|
+
# grow ad hoc.
|
|
40
|
+
SERVICE_READERS = (ServiceCatalog.reader_names + SHELL_READERS).freeze
|
|
41
|
+
|
|
42
|
+
def initialize(shell)
|
|
43
|
+
@shell = shell
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Dynamic, NOT constructor-captured: with_output swaps @output before
|
|
47
|
+
# the handler runs, and refresh_ui! rebinds it afterwards — freezing
|
|
48
|
+
# the IO here would silently detach every command print from the
|
|
49
|
+
# capture/record seam.
|
|
50
|
+
def out
|
|
51
|
+
@shell.output
|
|
52
|
+
end
|
|
53
|
+
alias output out
|
|
54
|
+
|
|
55
|
+
# IO conveniences for stored 2-arity synthesized handlers whose source
|
|
56
|
+
# contract was ->(args, out) { out.puts ... }: they now receive ctx,
|
|
57
|
+
# so ctx itself must print.
|
|
58
|
+
def puts(*)
|
|
59
|
+
out.puts(*)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def print(*)
|
|
63
|
+
out.print(*)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def flush
|
|
67
|
+
out.flush if out.respond_to?(:flush)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
SERVICE_READERS.each do |name|
|
|
71
|
+
define_method(name) { @shell.public_send(name) }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Read-only inspection view (overrides the loop-generated reader):
|
|
75
|
+
# handlers may consult the command table (!help / !commands) but
|
|
76
|
+
# never mutate it — registrations belong to the shell's spine.
|
|
77
|
+
def commands
|
|
78
|
+
@shell.commands.dup.freeze
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Live presentation theme (re-resolved per call: refresh_ui! may swap
|
|
82
|
+
# the theme between registration and invocation).
|
|
83
|
+
def theme
|
|
84
|
+
@shell.theme
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# ── Plugin coeffects (catalog entries expose no shell reader) ───────
|
|
88
|
+
def plugin_manager
|
|
89
|
+
coeffect(Rubik::Coeffects::PLUGIN_MANAGER)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def plugin_storage
|
|
93
|
+
coeffect(Rubik::Coeffects::PLUGIN_STORAGE)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# ── Rebindable conversation state (write-through delegation) ────────
|
|
97
|
+
def conversation_history
|
|
98
|
+
@shell.conversation_history
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def conversation_history=(history)
|
|
102
|
+
@shell.conversation_history = history
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def active_loop
|
|
106
|
+
@shell.active_loop
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def active_loop=(loop_instance)
|
|
110
|
+
@shell.active_loop = loop_instance
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def conversation_route_stamp=(stamp)
|
|
114
|
+
@shell.conversation_route_stamp = stamp
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Additional Shell readers commands legitimately consult (plain reads,
|
|
118
|
+
# no writers — see the rebindable block above for what IS writable).
|
|
119
|
+
def agent_caller
|
|
120
|
+
@shell.agent_caller
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def current_model_spec
|
|
124
|
+
@shell.current_model_spec
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# ── Command effects (named, closed; one-line delegations) ───────────
|
|
128
|
+
def stop
|
|
129
|
+
@shell.stop
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def execute_free_text(text)
|
|
133
|
+
@shell.execute_free_text(text)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def resume_session(identifier = :latest)
|
|
137
|
+
@shell.resume_session(identifier)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def refresh_ui!
|
|
141
|
+
@shell.refresh_ui!
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def render_step_event(step_info)
|
|
145
|
+
@shell.render_step_event(step_info)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def detect_git_branch
|
|
149
|
+
@shell.detect_git_branch
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def sync_context_and_ast(files)
|
|
153
|
+
@shell.sync_context_and_ast(files)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def sync_workspace_index!
|
|
157
|
+
@shell.sync_workspace_index!
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def resync_llm_route!
|
|
161
|
+
@shell.resync_llm_route!
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def refresh_llm_registry!
|
|
165
|
+
@shell.refresh_llm_registry!
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def trim_conversation_window!
|
|
169
|
+
@shell.trim_conversation_window!
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Pruner apply seam (goal yolu): aşamalı budama planını turn başında,
|
|
173
|
+
# orchestrator.run pencereyi tohumlamadan ÖNCE uygular (input_execution
|
|
174
|
+
# yolu Shell'e doğrudan çağırır; !goal buradan geçer).
|
|
175
|
+
def apply_staged_prune_plan!
|
|
176
|
+
@shell.apply_staged_prune_plan!
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def ask_yes_no(prompt, default: "n")
|
|
180
|
+
@shell.ask_yes_no(prompt, default: default)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def orchestrator_loop(opts = {})
|
|
184
|
+
@shell.orchestrator_loop(opts)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def warm_ast_graph(async: true)
|
|
188
|
+
@shell.warm_ast_graph(async: async)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def build_tool_registry
|
|
192
|
+
@shell.build_tool_registry
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def request_paste!
|
|
196
|
+
@shell.request_paste!
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# The one whole-shell handoff a command may still make: the replayer
|
|
200
|
+
# plants @replaying on its receiver and re-enters execute_input, so it
|
|
201
|
+
# must own the real Shell — passing ctx would plant the flag on the
|
|
202
|
+
# wrong object and break replay suppression.
|
|
203
|
+
def session_replayer
|
|
204
|
+
Rubik::Session::Replayer.new(@shell)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
private
|
|
208
|
+
|
|
209
|
+
def coeffect(key)
|
|
210
|
+
Rubik::Coeffects.get(@shell.runtime_manager&.context, key)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|