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,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-spinner"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module UI
|
|
7
|
+
# Live status spinner shown while the orchestrator queries the LLM:
|
|
8
|
+
# cycling Turkish verbs (Claude-Code-style) plus elapsed seconds, drawn
|
|
9
|
+
# on a single terminal line. Owns a background thread whose lifecycle is
|
|
10
|
+
# tied to #stop/#kill; the Runtime RenderService registers `kill` as a
|
|
11
|
+
# context inverse so disposed shells never leak spinner threads.
|
|
12
|
+
class Spinner
|
|
13
|
+
TICK_SECONDS = 0.12
|
|
14
|
+
ELAPSED_STEP = 1.0
|
|
15
|
+
|
|
16
|
+
def initialize(io, theme, config: nil)
|
|
17
|
+
@io = io
|
|
18
|
+
@theme = theme
|
|
19
|
+
@config = config
|
|
20
|
+
@thread = nil
|
|
21
|
+
@mutex = Mutex.new
|
|
22
|
+
@spinner = nil
|
|
23
|
+
@started_at = nil
|
|
24
|
+
@last_elapsed_shown = -1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def enabled?
|
|
28
|
+
@config ? @config.fetch("ui.spinner", true) != false : true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def spinning?
|
|
32
|
+
@thread&.alive? == true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Starts spinning with the next verb from the theme's rotation.
|
|
36
|
+
# No-op when the surface is disabled (non-TTY, tests, quiet terminals).
|
|
37
|
+
def start(step: nil, max_steps: nil)
|
|
38
|
+
return unless enabled?
|
|
39
|
+
return if spinning?
|
|
40
|
+
|
|
41
|
+
verb = next_verb
|
|
42
|
+
step_part = step && max_steps ? " [#{step}/#{max_steps}]" : ""
|
|
43
|
+
@started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
44
|
+
@last_elapsed_shown = -1
|
|
45
|
+
|
|
46
|
+
@mutex.synchronize do
|
|
47
|
+
# The message template carries :spinner and :msg tokens so #update
|
|
48
|
+
# actually substitutes the verb/step/elapsed text; `clear: true`
|
|
49
|
+
# makes tty-spinner erase its own line on stop (no stray frame).
|
|
50
|
+
@spinner = TTY::Spinner.new(":spinner :msg", output: @io, format: :spin,
|
|
51
|
+
hide_cursor: true, clear: true)
|
|
52
|
+
@spinner.update(msg: "#{@theme.paint(verb, :accent)}#{step_part}")
|
|
53
|
+
@spinner.spin
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@thread = Thread.new do
|
|
57
|
+
loop do
|
|
58
|
+
sleep TICK_SECONDS
|
|
59
|
+
break unless spinning?
|
|
60
|
+
|
|
61
|
+
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started_at)
|
|
62
|
+
@mutex.synchronize do
|
|
63
|
+
next unless @spinner && !@spinner.done?
|
|
64
|
+
|
|
65
|
+
if elapsed - @last_elapsed_shown >= ELAPSED_STEP
|
|
66
|
+
@last_elapsed_shown = elapsed
|
|
67
|
+
@spinner.update(msg: current_message(verb, step_part, elapsed))
|
|
68
|
+
end
|
|
69
|
+
@spinner.spin
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
# A dead tick thread must not take the REPL down, but the
|
|
72
|
+
# failure is never silent (AGENTS.md §5): report, then exit.
|
|
73
|
+
warn "[rubik] spinner tick failed: #{e.class}: #{e.message}"
|
|
74
|
+
Thread.exit
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Stops the spinner and clears its line so the caller can print freely.
|
|
81
|
+
# `final:` optionally leaves a completion note on the cleared line.
|
|
82
|
+
def stop(final: nil)
|
|
83
|
+
thread = @thread
|
|
84
|
+
@thread = nil
|
|
85
|
+
thread&.kill
|
|
86
|
+
thread&.join(0.3)
|
|
87
|
+
|
|
88
|
+
@mutex.synchronize do
|
|
89
|
+
if @spinner
|
|
90
|
+
begin
|
|
91
|
+
@spinner.stop
|
|
92
|
+
@io.print(final) unless final.nil?
|
|
93
|
+
@io.flush if @io.respond_to?(:flush)
|
|
94
|
+
rescue StandardError => e
|
|
95
|
+
# Cleanup must never mask the caller's in-flight exception
|
|
96
|
+
# (AGENTS.md §5): report the IO failure and still nil @spinner
|
|
97
|
+
# so repeated stops stay idempotent.
|
|
98
|
+
warn "[rubik] spinner cleanup failed: #{e.class}: #{e.message}"
|
|
99
|
+
ensure
|
|
100
|
+
@spinner = nil
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
alias kill stop
|
|
106
|
+
alias dispose stop
|
|
107
|
+
|
|
108
|
+
def current_message(verb, step_part, elapsed)
|
|
109
|
+
seconds = elapsed.to_i
|
|
110
|
+
suffix = seconds.positive? ? " · #{seconds}s" : ""
|
|
111
|
+
"#{@theme.paint(verb, :accent)}#{step_part}#{suffix} (ctrl+c: kes)"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def next_verb
|
|
115
|
+
@verb_index = ((@verb_index || -1) + 1) % @theme.spinner_verbs.size
|
|
116
|
+
@theme.spinner_verbs[@verb_index]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../prompt/token_estimator"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module UI
|
|
7
|
+
# Compact context strip printed above the prompt line before every read:
|
|
8
|
+
# active model, estimated context-window usage, dirty buffer count and
|
|
9
|
+
# sandbox state. Customizable via `ui.statusline_template` placeholders:
|
|
10
|
+
# {project} {branch} {model} {ctx} {dirty} {sandbox} {verbosity}
|
|
11
|
+
module Statusline
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def render(shell, theme)
|
|
15
|
+
return nil unless shell.config.fetch("ui.statusline", true)
|
|
16
|
+
|
|
17
|
+
template = shell.config.fetch("ui.statusline_template", default_template)
|
|
18
|
+
# `!config set ui.statusline_template "{a} {b}"` can arrive parsed as
|
|
19
|
+
# a YAML flow-mapping Hash; coerce any non-String back to the default.
|
|
20
|
+
template = default_template unless template.is_a?(String)
|
|
21
|
+
|
|
22
|
+
routed = current_route(shell)
|
|
23
|
+
units_str = units_segment(shell)
|
|
24
|
+
values = {
|
|
25
|
+
"project" => File.basename(shell.buffer_manager&.root_dir || Dir.pwd),
|
|
26
|
+
"branch" => shell.detect_git_branch || "-",
|
|
27
|
+
"model" => model_label(routed),
|
|
28
|
+
"ctx" => context_usage(shell, routed),
|
|
29
|
+
"dirty" => dirty_segment(shell, theme),
|
|
30
|
+
"sandbox" => sandbox_segment(shell),
|
|
31
|
+
"verbosity" => shell.config.fetch("ui.verbosity", "normal"),
|
|
32
|
+
"units" => units_str
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
line = template.gsub(/\{(\w+)\}/) { values.fetch(::Regexp.last_match(1)) { "{#{::Regexp.last_match(1)}}" } }
|
|
36
|
+
line += " · [#{units_str}]" if !template.include?("{units}") && active_units_count(shell).positive?
|
|
37
|
+
theme.paint(line, :dim)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def default_template
|
|
41
|
+
"{model} · ctx {ctx} · {dirty} · sandbox {sandbox}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def current_route(shell)
|
|
45
|
+
shell.router.route(task_type: :default)
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
# Edge-triggered warn: render runs before every prompt read, so an
|
|
48
|
+
# unguarded warn would spam one line per redraw on a broken router.
|
|
49
|
+
unless @route_failure_warned
|
|
50
|
+
@route_failure_warned = true
|
|
51
|
+
Rubik::Log.warn(:ui, "statusline route lookup failed (#{e.class}: #{e.message}); model shows '-'")
|
|
52
|
+
end
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# "provider/model" label shared with the banner.
|
|
57
|
+
def model_label(routed)
|
|
58
|
+
return "-" unless routed
|
|
59
|
+
|
|
60
|
+
provider = routed[:spec].is_a?(Hash) ? (routed[:spec]["provider"] || routed[:spec][:provider]) : nil
|
|
61
|
+
"#{provider || 'unknown'}/#{routed[:model]}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Estimated share of the context window consumed by the in-memory
|
|
65
|
+
# dialogue history, e.g. "34%" (or "-" when the limit is unknown).
|
|
66
|
+
def context_usage(shell, routed)
|
|
67
|
+
limit = routed&.fetch(:context_limit, nil).to_i
|
|
68
|
+
return "-" unless limit.positive?
|
|
69
|
+
|
|
70
|
+
tokens = Rubik::Prompt::TokenEstimator.estimate_history(shell.conversation_history || [])
|
|
71
|
+
pct = ((tokens.to_f / limit) * 100).round
|
|
72
|
+
pct = 100 if pct > 100
|
|
73
|
+
"#{pct}%"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def dirty_segment(shell, theme)
|
|
77
|
+
count = shell.buffer_manager&.dirty? ? (shell.buffer_manager.dirty_files&.size || 0) : 0
|
|
78
|
+
count.zero? ? "temiz" : "#{count} #{theme.glyph(:dirty)}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def sandbox_segment(shell)
|
|
82
|
+
if shell.config.fetch("sandbox.enabled", false)
|
|
83
|
+
net_str = shell.config.fetch("sandbox.allow_network", false) ? "ağ-açık" : "izole"
|
|
84
|
+
"#{shell.sandbox_runner&.driver&.name || 'sandbox'}:#{net_str}"
|
|
85
|
+
else
|
|
86
|
+
"kapalı"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def active_units_count(shell)
|
|
91
|
+
running, paused = active_units_breakdown(shell)
|
|
92
|
+
running + paused
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def active_units_breakdown(shell)
|
|
96
|
+
if shell.respond_to?(:init) && shell.init
|
|
97
|
+
# Kernel branch (translated unit states): Unit::PAUSED_STATES is
|
|
98
|
+
# the single spelling. :active deliberately covers :waiting_input
|
|
99
|
+
# here — the fallback branch's executing? below does not; the
|
|
100
|
+
# planes disagree on that one state, on purpose.
|
|
101
|
+
running = shell.init.units_by_state(:active).size
|
|
102
|
+
paused = shell.init.units_by_state(*Rubik::Kernel::Unit::PAUSED_STATES).size
|
|
103
|
+
[running, paused]
|
|
104
|
+
elsif shell.respond_to?(:subagent_supervisor) && shell.subagent_supervisor
|
|
105
|
+
insts = shell.subagent_supervisor.instances.values
|
|
106
|
+
running = insts.count(&:executing?)
|
|
107
|
+
paused = insts.count(&:parked?)
|
|
108
|
+
[running, paused]
|
|
109
|
+
else
|
|
110
|
+
[0, 0]
|
|
111
|
+
end
|
|
112
|
+
rescue StandardError => e
|
|
113
|
+
unless @units_failure_warned
|
|
114
|
+
@units_failure_warned = true
|
|
115
|
+
Rubik::Log.warn(:ui, "statusline unit-state lookup failed (#{e.class}: #{e.message}); showing 0 units")
|
|
116
|
+
end
|
|
117
|
+
[0, 0]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def units_segment(shell)
|
|
121
|
+
running, paused = active_units_breakdown(shell)
|
|
122
|
+
if running.positive? && paused.positive?
|
|
123
|
+
"⚙️ #{running} running, ⏸️ #{paused} paused"
|
|
124
|
+
elsif running.positive?
|
|
125
|
+
"⚙️ #{running} units running"
|
|
126
|
+
elsif paused.positive?
|
|
127
|
+
"⏸️ #{paused} units paused"
|
|
128
|
+
else
|
|
129
|
+
"0 units"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pastel"
|
|
4
|
+
require_relative "../platform"
|
|
5
|
+
require_relative "capability"
|
|
6
|
+
|
|
7
|
+
module Rubik
|
|
8
|
+
module UI
|
|
9
|
+
# Single source of truth for the Rubik display language: palette, glyph
|
|
10
|
+
# sets (unicode / ascii fallback) and spinner verbs. All visual components
|
|
11
|
+
# decorate through a Theme so `ui.color: never` / NO_COLOR / non-TTY pipes
|
|
12
|
+
# collapse every surface back to plain deterministic text.
|
|
13
|
+
class Theme
|
|
14
|
+
# Color-blind safe default palette: positive states (success/added)
|
|
15
|
+
# live on the blue axis (:cyan) and negative states (error/removed) on
|
|
16
|
+
# :red, so no distinction ever rests on the red-vs-green axis (the
|
|
17
|
+
# classic deuteranopia/protanopia confusion). Structural/meta text uses
|
|
18
|
+
# :blue to keep :cyan unambiguous as the "positive" hue. Named ANSI
|
|
19
|
+
# codes render differently per terminal theme, but the axis separation
|
|
20
|
+
# holds in all of them.
|
|
21
|
+
STYLES = {
|
|
22
|
+
accent: [:bright_red],
|
|
23
|
+
success: [:cyan],
|
|
24
|
+
error: [:red, :bold],
|
|
25
|
+
warn: [:yellow],
|
|
26
|
+
info: [:blue],
|
|
27
|
+
dim: [:bright_black],
|
|
28
|
+
thought: [:bright_black, :italic],
|
|
29
|
+
bold: [:bold],
|
|
30
|
+
added: [:cyan],
|
|
31
|
+
removed: [:red]
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
GLYPHS = {
|
|
35
|
+
unicode: {
|
|
36
|
+
action: "⏺", branch: "⎿", spinner: "✻", thought: "💭",
|
|
37
|
+
ok: "✔", err: "✖", warn: "⚠", retry: "↻", finish: "🏁",
|
|
38
|
+
bolt: "⚡", clock: "⏳", dirty: "✎", rail: "│", dash: "─",
|
|
39
|
+
corner_tl: "╭", corner_tr: "╮", corner_bl: "╰", corner_br: "╯",
|
|
40
|
+
cursor: "›", preview: "↳", ellipsis: "…"
|
|
41
|
+
}.freeze,
|
|
42
|
+
ascii: {
|
|
43
|
+
action: "*", branch: "`", spinner: "*", thought: "o",
|
|
44
|
+
ok: "+", err: "x", warn: "!", retry: "r", finish: "#",
|
|
45
|
+
bolt: "!", clock: "~", dirty: "*", rail: "|", dash: "-",
|
|
46
|
+
corner_tl: "+", corner_tr: "+", corner_bl: "+", corner_br: "+",
|
|
47
|
+
cursor: ">", preview: "->", ellipsis: "..."
|
|
48
|
+
}.freeze
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
51
|
+
DEFAULT_SPINNER_VERBS = [
|
|
52
|
+
"Düşünüyor", "Kavuruyor", "Karıştırıyor", "Damıtıyor", "Biçimlendiriyor", "Harmanlıyor", "Tarıyor", "Planlıyor", "Doğruluyor", "Tamamlıyor"
|
|
53
|
+
].freeze
|
|
54
|
+
|
|
55
|
+
attr_reader :pastel, :glyphs, :spinner_verbs
|
|
56
|
+
|
|
57
|
+
def initialize(enabled: true, glyph_set: :unicode, spinner_verbs: DEFAULT_SPINNER_VERBS)
|
|
58
|
+
@pastel = Pastel.new(enabled: enabled)
|
|
59
|
+
set_name = GLYPHS.key?(glyph_set.to_sym) ? glyph_set.to_sym : :unicode
|
|
60
|
+
@glyphs = GLYPHS[set_name]
|
|
61
|
+
verbs = Array(spinner_verbs)
|
|
62
|
+
@spinner_verbs = verbs.empty? ? DEFAULT_SPINNER_VERBS : verbs
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Builds the theme the Shell subsystem should use for a given output IO
|
|
66
|
+
# and config. Called on Shell init and ui.* refresh — not on the
|
|
67
|
+
# per-token hot path. Unicode glyphs are downgraded to the ascii set
|
|
68
|
+
# when the output's external encoding cannot encode them (redirected
|
|
69
|
+
# legacy-codepage consoles on Windows), preventing conversion errors.
|
|
70
|
+
def self.for(io, config = nil)
|
|
71
|
+
enabled = Capability.color?(io, config)
|
|
72
|
+
glyph_set = (config&.fetch("ui.glyphs", "unicode") || "unicode").to_sym
|
|
73
|
+
glyph_set = :ascii if glyph_set == :unicode && !unicode_supported?(io)
|
|
74
|
+
verbs = config&.fetch("ui.spinner_verbs", nil)
|
|
75
|
+
new(enabled: enabled, glyph_set: glyph_set, spinner_verbs: verbs)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.unicode_supported?(io)
|
|
79
|
+
return true unless io.respond_to?(:external_encoding)
|
|
80
|
+
|
|
81
|
+
enc = io.external_encoding
|
|
82
|
+
return true if enc.nil? # default external (UTF-8 on modern Rubies)
|
|
83
|
+
|
|
84
|
+
"⏺".encode(enc)
|
|
85
|
+
true
|
|
86
|
+
rescue Encoding::UndefinedConversionError, Encoding::ConverterNotFoundError
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def color?
|
|
91
|
+
pastel.enabled?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Applies a named style. Returns plain text verbatim when color is off.
|
|
95
|
+
def paint(text, style_name)
|
|
96
|
+
return text.to_s unless color?
|
|
97
|
+
|
|
98
|
+
style = STYLES[style_name]
|
|
99
|
+
unless style
|
|
100
|
+
# Unknown styles used to fall through silently (the banner's :yellow
|
|
101
|
+
# hint rendered unstyled for months); surface that in debug mode
|
|
102
|
+
# instead of swallowing it (warned once per style).
|
|
103
|
+
if ENV["RUBIK_DEBUG"]
|
|
104
|
+
@warned_styles ||= Set.new
|
|
105
|
+
Rubik::Platform.debug_warn("UI::Theme#paint: unknown style #{style_name.inspect}, rendering plain") if @warned_styles.add?(style_name)
|
|
106
|
+
end
|
|
107
|
+
return text.to_s
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
pastel.decorate(text.to_s, *style)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def glyph(name)
|
|
114
|
+
glyphs.fetch(name, "")
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/rubik/ui.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ui/capability"
|
|
4
|
+
require_relative "ui/theme"
|
|
5
|
+
require_relative "ui/frame"
|
|
6
|
+
require_relative "ui/banner"
|
|
7
|
+
require_relative "ui/statusline"
|
|
8
|
+
require_relative "ui/spinner"
|
|
9
|
+
require_relative "ui/renderer"
|
|
10
|
+
require_relative "ui/markdown_stream"
|
|
11
|
+
require_relative "ui/approvals"
|
|
12
|
+
require_relative "ui/panel"
|
|
13
|
+
require_relative "ui/diff_viewer"
|
|
14
|
+
|
|
15
|
+
require_relative "runtime/service_registration"
|
|
16
|
+
|
|
17
|
+
module Rubik
|
|
18
|
+
# Terminal display layer (Plan 51): theme, capability detection, agent-flow
|
|
19
|
+
# renderer, live spinner, markdown streaming and interactive approval /
|
|
20
|
+
# panel surfaces. Registered into the Runtime as a coeffect service so
|
|
21
|
+
# synthesized tools and plugins can resolve `ui` and `theme` through the
|
|
22
|
+
# context, and so spinner threads are disposed with their context (LIFO
|
|
23
|
+
# inverse), never leaked.
|
|
24
|
+
module UI
|
|
25
|
+
module RenderService
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# Wires the display layer into the runtime context.
|
|
29
|
+
# Returns the Renderer; also provides `ui` (renderer) and `theme`.
|
|
30
|
+
# Idempotent on re-registration (`Shell#refresh_ui!`): service values
|
|
31
|
+
# are swapped, the delete-inverses are not stacked, and the single
|
|
32
|
+
# spinner-kill inverse resolves the CURRENT renderer at dispose time,
|
|
33
|
+
# so repeated refreshes never grow the Omega accumulator.
|
|
34
|
+
# `output:` lets callers bind to the real terminal when the shell's
|
|
35
|
+
# current @output is a transient capture TeeIO.
|
|
36
|
+
def register(context_or_manager, shell:, output: nil)
|
|
37
|
+
Rubik::Runtime::ServiceRegistration.call(context_or_manager) do |ctx|
|
|
38
|
+
next nil unless ctx
|
|
39
|
+
|
|
40
|
+
out = output || shell.output
|
|
41
|
+
theme = Theme.for(out, shell.config)
|
|
42
|
+
renderer = Renderer.new(config: shell.config, output: out, theme: theme)
|
|
43
|
+
|
|
44
|
+
existing_renderer = ctx.get("ui")
|
|
45
|
+
existing_renderer.spinner&.kill if existing_renderer.respond_to?(:spinner) && existing_renderer.spinner
|
|
46
|
+
|
|
47
|
+
first_registration = !ctx.has?("ui")
|
|
48
|
+
ctx.provide("ui", renderer)
|
|
49
|
+
ctx.provide("theme", theme)
|
|
50
|
+
ctx.on_dispose { (ctx.get("ui") || renderer)&.spinner&.kill } if first_registration
|
|
51
|
+
|
|
52
|
+
renderer
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/rubik/uris.rb
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
# rubik:// sanal URI gramerinin TEK evi (uri-resolver-seam planı
|
|
5
|
+
# 2026-10-01). Şemalar üç alanı kapsar (storage = FS-DB, subagent =
|
|
6
|
+
# orchestrator yapısı, goal = canlı orchestrator anlık görüntüsü) —
|
|
7
|
+
# dolayısıyla gramer storage'a ait değildir; bağımlılıksız yaprak olarak
|
|
8
|
+
# burada yaşar (coeffects.rb/platform.rb öncülü). Desenler, çözümleyiciler
|
|
9
|
+
# ve URI kurucuları yalnızca bu modüldedir; bekçi testi
|
|
10
|
+
# (test_architectural_boundaries.rb) lib/ içinde başka rubik:// regexi
|
|
11
|
+
# yasaklar. Buradaki desenler büyük/küçük harf duyarsızdır (\i) — mevcut
|
|
12
|
+
# çağrı yüzeyleri hiçbir değişiklik olmadan aynen kalır.
|
|
13
|
+
module Uris
|
|
14
|
+
SCHEME = "rubik"
|
|
15
|
+
STORAGE = "storage"
|
|
16
|
+
GOAL = "goal"
|
|
17
|
+
SUBAGENT = "subagent"
|
|
18
|
+
|
|
19
|
+
# Desenler — lib/ içinde izin verilen TEK rubik:// regexleri.
|
|
20
|
+
# Şema segmenti: [a-z][a-z0-9_-]* (çözümleme anahtarları küçük harfe
|
|
21
|
+
# iner; scheme_of). Önek ya da URI sonunda olmalı; yarıda kesilmez.
|
|
22
|
+
SCHEME_PREFIX = %r{\Arubik://([a-z][a-z0-9_-]*)}i
|
|
23
|
+
STORAGE_PREFIX = %r{\Arubik://storage(?:/|\z)}i
|
|
24
|
+
STORAGE_RELATIVE = %r{\Arubik://storage/(.*)\z}i
|
|
25
|
+
GOAL_STATE = %r{\Arubik://goal(?:/state)?(?:\.xml)?\z}i
|
|
26
|
+
# Yapılandırılmış Σ okuma yüzeyi (goal-ledger planı A): /state regexi
|
|
27
|
+
# /ledger'ı EŞLEMEZ — iki ayrı gramer, tek adapter içinde iç dispatch.
|
|
28
|
+
GOAL_LEDGER = %r{\Arubik://goal/ledger(?:\.xml)?\z}i
|
|
29
|
+
SUBAGENT_TRANSCRIPT = %r{\Arubik://subagent/([^/]+)/transcript(?:\.jsonl)?(?:#.*)?\z}i
|
|
30
|
+
|
|
31
|
+
# TranscriptIndexer ref sözleşimi: "subagent-<storage-id>".
|
|
32
|
+
TRANSCRIPT_REF_PREFIX = "subagent-"
|
|
33
|
+
|
|
34
|
+
module_function
|
|
35
|
+
|
|
36
|
+
# -> "storage"|"goal"|"subagent"|... ya da nil (rubik:// değil).
|
|
37
|
+
# to_s: nil/sembol girdiler dürüst nil düşer (NoMethodError yok).
|
|
38
|
+
def scheme_of(uri)
|
|
39
|
+
m = uri.to_s.match(SCHEME_PREFIX)
|
|
40
|
+
m[1].downcase if m
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Kanonik soyma (uri_reader.rb'nin tarihsel biçimi): önek + ilk eğik
|
|
44
|
+
# çizgi düşer. -> "uriproj/goal/1" | nil. Çıplak kök ("rubik://storage")
|
|
45
|
+
# boş dize döndürür — UriReader bunu malformed olarak raporlar.
|
|
46
|
+
def storage_path(uri)
|
|
47
|
+
uri.sub(STORAGE_PREFIX, "") if STORAGE_PREFIX.match?(uri)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Kozmetik görüntüleme soyması (show_file stub'ı, ui/renderer
|
|
51
|
+
# kısaltması): zorunlu eğik çizgi, gerisi aynen. -> "proj/type/id" |
|
|
52
|
+
# nil. Kanonik storage_path'ten FARKLIDIR (ör. "rubik://storage" çıplak
|
|
53
|
+
# kökü soymaz) — yalnızca sunumda kullanılır, ayrıştırma için değil.
|
|
54
|
+
def storage_relative(uri)
|
|
55
|
+
m = uri.match(STORAGE_RELATIVE)
|
|
56
|
+
m[1] if m
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def storage_entity(project:, type:, id:)
|
|
60
|
+
"#{SCHEME}://#{STORAGE}/#{project}/#{type}/#{id}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def storage_project(project:)
|
|
64
|
+
"#{SCHEME}://#{STORAGE}/#{project}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def storage_ref(project:, ref:)
|
|
68
|
+
"#{SCHEME}://#{STORAGE}/#{project}/refs/#{ref}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def storage_listing(project:, type:)
|
|
72
|
+
"#{SCHEME}://#{STORAGE}/#{project}/#{type}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def storage_index
|
|
76
|
+
"#{SCHEME}://#{STORAGE}/index"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def goal_state
|
|
80
|
+
"#{SCHEME}://#{GOAL}/state"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def goal_ledger
|
|
84
|
+
"#{SCHEME}://#{GOAL}/ledger"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def subagent_transcript(storage_id)
|
|
88
|
+
"#{SCHEME}://#{SUBAGENT}/#{storage_id}/transcript"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# TranscriptIndexer ref sözleşimi: "subagent-<storage-id>". storage-id
|
|
92
|
+
# kendisi zaten "subagent-" önekli doğar (ör. "subagent-1-12456-...") —
|
|
93
|
+
# öneki körlemesine eklemek çift önekli, çözümlenemez alias üretiyordu
|
|
94
|
+
# (js-tangram olay raporu 2026-10-04, olay #4). Önekli girdi aynen
|
|
95
|
+
# kullanılır.
|
|
96
|
+
def transcript_ref(storage_id)
|
|
97
|
+
id = storage_id.to_s
|
|
98
|
+
id.start_with?(TRANSCRIPT_REF_PREFIX) ? id : "#{TRANSCRIPT_REF_PREFIX}#{id}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
data/lib/rubik.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rubik/version"
|
|
4
|
+
require_relative "rubik/log"
|
|
5
|
+
require_relative "rubik/platform"
|
|
6
|
+
require_relative "rubik/concurrency"
|
|
7
|
+
require_relative "rubik/uris"
|
|
8
|
+
require_relative "rubik/config"
|
|
9
|
+
require_relative "rubik/event_bus"
|
|
10
|
+
require_relative "rubik/repl"
|
|
11
|
+
require_relative "rubik/repl/terminal_guard"
|
|
12
|
+
require_relative "rubik/ui"
|
|
13
|
+
require_relative "rubik/buffer"
|
|
14
|
+
require_relative "rubik/session"
|
|
15
|
+
require_relative "rubik/context"
|
|
16
|
+
require_relative "rubik/storage"
|
|
17
|
+
require_relative "rubik/ast"
|
|
18
|
+
require_relative "rubik/llm"
|
|
19
|
+
require_relative "rubik/adapter"
|
|
20
|
+
require_relative "rubik/diagnostics"
|
|
21
|
+
require_relative "rubik/tools"
|
|
22
|
+
require_relative "rubik/orchestrator"
|
|
23
|
+
require_relative "rubik/repair"
|
|
24
|
+
require_relative "rubik/security"
|
|
25
|
+
require_relative "rubik/sandbox"
|
|
26
|
+
require_relative "rubik/plugin"
|
|
27
|
+
require_relative "rubik/benchmark"
|
|
28
|
+
require_relative "rubik/lsp"
|
|
29
|
+
require_relative "rubik/mcp"
|
|
30
|
+
require_relative "rubik/runtime"
|
|
31
|
+
require_relative "rubik/skills"
|
|
32
|
+
require_relative "rubik/prompt"
|
|
33
|
+
require_relative "rubik/kernel/record"
|
|
34
|
+
require_relative "rubik/kernel/dmesg"
|
|
35
|
+
require_relative "rubik/kernel/dispatcher"
|
|
36
|
+
require_relative "rubik/kernel/unit"
|
|
37
|
+
require_relative "rubik/kernel/init"
|
|
38
|
+
|
|
39
|
+
module Rubik
|
|
40
|
+
class Error < StandardError; end
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
attr_accessor :current_shell, :runtime_manager
|
|
44
|
+
|
|
45
|
+
# Deprecated read bridge (review Öneri 4 sunset): resolves the runtime
|
|
46
|
+
# manager from the ambient root. New code must go through the context:
|
|
47
|
+
# the 'shell' coeffect (Rubik::Coeffects::SHELL) via ctx.require or
|
|
48
|
+
# Manager#context. Warns once per process.
|
|
49
|
+
def runtime
|
|
50
|
+
warn_ambient_read_once
|
|
51
|
+
runtime_manager || current_shell&.runtime_manager
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def warn_ambient_read_once
|
|
57
|
+
return if defined?(@ambient_warned) && @ambient_warned
|
|
58
|
+
|
|
59
|
+
@ambient_warned = true
|
|
60
|
+
warn "[DEPRECATION] Rubik.runtime is deprecated (sunset). Inject the " \
|
|
61
|
+
"'shell' coeffect (Rubik::Coeffects::SHELL) or read the manager " \
|
|
62
|
+
"from the owning Shell instead."
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|