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,210 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Platform
|
|
5
|
+
# Path resolution, normalization, URI conversion and path safety checks.
|
|
6
|
+
# Extracted from lib/rubik/platform.rb (Plan 86 D-4).
|
|
7
|
+
module Path
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.extend self
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Pure-Ruby cross-platform binary resolver
|
|
13
|
+
def which(cmd)
|
|
14
|
+
return nil if cmd.nil? || cmd.to_s.strip.empty?
|
|
15
|
+
|
|
16
|
+
cmd_str = cmd.to_s.strip.split.first
|
|
17
|
+
return nil if cmd_str.nil? || cmd_str.empty?
|
|
18
|
+
|
|
19
|
+
# Windows shell builtins (e.g. echo, dir, cls)
|
|
20
|
+
return cmd_str if windows? && Rubik::Platform::WINDOWS_SHELL_BUILTINS.include?(cmd_str.downcase)
|
|
21
|
+
|
|
22
|
+
# If full/relative path is provided
|
|
23
|
+
if cmd_str.include?("/") || (windows? && cmd_str.include?("\\"))
|
|
24
|
+
return File.expand_path(cmd_str).gsub("\\", "/") if File.file?(cmd_str) && executable_file?(cmd_str)
|
|
25
|
+
|
|
26
|
+
if windows?
|
|
27
|
+
pathext.each do |ext|
|
|
28
|
+
candidate = "#{cmd_str}#{ext}"
|
|
29
|
+
return File.expand_path(candidate).gsub("\\", "/") if File.file?(candidate) && executable_file?(candidate)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
return nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Search in PATH
|
|
36
|
+
paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
|
|
37
|
+
exts = if windows?
|
|
38
|
+
ext = File.extname(cmd_str)
|
|
39
|
+
ext.empty? ? pathext : ([""] + pathext).uniq
|
|
40
|
+
else
|
|
41
|
+
[""]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
paths.each do |dir|
|
|
45
|
+
next if dir.empty? || !File.directory?(dir)
|
|
46
|
+
|
|
47
|
+
exts.each do |ext|
|
|
48
|
+
target = File.join(dir, "#{cmd_str}#{ext}")
|
|
49
|
+
return File.expand_path(target).gsub("\\", "/") if File.file?(target) && executable_file?(target)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def pathext
|
|
56
|
+
(ENV["PATHEXT"] || ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC;.PS1").split(";").map(&:downcase)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def executable_file?(path)
|
|
60
|
+
return false unless File.file?(path)
|
|
61
|
+
|
|
62
|
+
windows? || File.executable?(path)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# RFC 8089 Compliant File URI (e.g. file:///C:/path/file vs file:///path/file)
|
|
66
|
+
def to_file_uri(path)
|
|
67
|
+
normalized = normalize_path_raw(path)
|
|
68
|
+
if windows? && normalized =~ %r{^([a-zA-Z]):/(.*)$}
|
|
69
|
+
"file:///#{::Regexp.last_match(1)}:/#{::Regexp.last_match(2)}"
|
|
70
|
+
else
|
|
71
|
+
normalized.start_with?("/") ? "file://#{normalized}" : "file:///#{normalized}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def from_file_uri(uri)
|
|
76
|
+
str = uri.to_s.sub(%r{^file://}, "")
|
|
77
|
+
str = CGI.unescape(str)
|
|
78
|
+
if windows?
|
|
79
|
+
# Handles file:///C:/... and file://C:/...
|
|
80
|
+
if str =~ %r{^/?([a-zA-Z]:/.*)$}
|
|
81
|
+
::Regexp.last_match(1)
|
|
82
|
+
else
|
|
83
|
+
str
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
str.start_with?("/") ? str : "/#{str}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def normalize_path_raw(path)
|
|
91
|
+
expanded = File.expand_path(path.to_s)
|
|
92
|
+
expanded.gsub("\\", "/")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Normalizes separators and casing for comparison
|
|
96
|
+
def normalize_path(path)
|
|
97
|
+
raw = normalize_path_raw(path)
|
|
98
|
+
windows? ? raw.downcase : raw
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def path_equal?(lhs, rhs)
|
|
102
|
+
normalize_path(lhs) == normalize_path(rhs)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def path_under?(child, parent)
|
|
106
|
+
n_child = normalize_path(child)
|
|
107
|
+
n_parent = normalize_path(parent)
|
|
108
|
+
return true if n_child == n_parent
|
|
109
|
+
|
|
110
|
+
clean_parent = n_parent.chomp("/")
|
|
111
|
+
clean_child = n_child.chomp("/")
|
|
112
|
+
return true if clean_child == clean_parent
|
|
113
|
+
|
|
114
|
+
n_child.start_with?("#{clean_parent}/")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Realpath for security-boundary containment decisions ONLY (stored
|
|
118
|
+
# buffer keys and every other comparison stay lexical). The deepest
|
|
119
|
+
# EXISTING ancestor is resolved and the not-yet-existing tail is
|
|
120
|
+
# re-attached lexically — a plain missing component cannot be a
|
|
121
|
+
# symlink, and File.expand_path has already collapsed ".." lexically.
|
|
122
|
+
# Symlink components (live or dangling) stop the walk: a dangling link
|
|
123
|
+
# must not be reattached as if it could never resolve.
|
|
124
|
+
# If strict: true (used in containment checks), IO resolution failure
|
|
125
|
+
# returns nil so resolved_path_under? fails closed (unprovable containment).
|
|
126
|
+
# If strict: false, falls back to lexical path for display/diagnostics.
|
|
127
|
+
def resolved_path(path, strict: false)
|
|
128
|
+
expanded = File.expand_path(path.to_s)
|
|
129
|
+
begin
|
|
130
|
+
File.realpath(expanded)
|
|
131
|
+
rescue Errno::ENOENT, Errno::ENOTDIR
|
|
132
|
+
begin
|
|
133
|
+
dir = expanded
|
|
134
|
+
tail = []
|
|
135
|
+
# File.symlink? in the walk condition: a DANGLING link reads as
|
|
136
|
+
# "missing" to File.exist?, but the OS write that follows DOES
|
|
137
|
+
# traverse it — stop there and let realpath's ENOENT fail closed
|
|
138
|
+
# (strict) instead of reattaching it lexically as if it could
|
|
139
|
+
# never be a link.
|
|
140
|
+
until dir == File.dirname(dir) || File.exist?(dir) || File.symlink?(dir)
|
|
141
|
+
tail.unshift(File.basename(dir))
|
|
142
|
+
dir = File.dirname(dir)
|
|
143
|
+
end
|
|
144
|
+
base = File.realpath(dir)
|
|
145
|
+
tail.empty? ? base : File.join(base, *tail)
|
|
146
|
+
rescue SystemCallError => e
|
|
147
|
+
debug_warn "resolved_path: ancestor walk failed for #{expanded}: #{e.message}"
|
|
148
|
+
return nil if strict
|
|
149
|
+
|
|
150
|
+
expanded
|
|
151
|
+
end
|
|
152
|
+
rescue SystemCallError => e
|
|
153
|
+
debug_warn "resolved_path: resolution failed for #{expanded}: #{e.message}"
|
|
154
|
+
return nil if strict
|
|
155
|
+
|
|
156
|
+
expanded
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Best-effort variant for read-only / display / diagnostics (never fails closed)
|
|
161
|
+
def resolved_path_best_effort(path)
|
|
162
|
+
resolved_path(path, strict: false)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Drop-in twin of path_under? that resolves symlinks/junctions on BOTH
|
|
166
|
+
# sides before the prefix comparison. Security boundaries (Buffer
|
|
167
|
+
# containment, Sandbox chdir checks) must use this variant.
|
|
168
|
+
# Fail-closed invariant (W1): IO/permission failure returns false (containment unprovable).
|
|
169
|
+
def resolved_path_under?(child, parent)
|
|
170
|
+
r_child = resolved_path(child, strict: true)
|
|
171
|
+
return false if r_child.nil?
|
|
172
|
+
|
|
173
|
+
r_parent = resolved_path(parent, strict: true)
|
|
174
|
+
return false if r_parent.nil?
|
|
175
|
+
|
|
176
|
+
path_under?(r_child, r_parent)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def dangerous_system_roots
|
|
180
|
+
roots = ["/", "/root", "/home", "/etc", "/usr", "/var", "/bin", "/sbin", "/lib", "/lib64", "/opt", "/boot", "/dev", "/proc", "/sys"]
|
|
181
|
+
if windows?
|
|
182
|
+
# Add expanded Unix root equivalents on current drive (e.g. C:/etc, C:/usr)
|
|
183
|
+
roots += roots.map { |r| File.expand_path(r) }
|
|
184
|
+
roots += ["c:/", "c:/windows", "c:/windows/system32", "c:/program files", "c:/program files (x86)", "c:/users", "c:/programdata"]
|
|
185
|
+
roots << ENV["SystemRoot"] if ENV["SystemRoot"]
|
|
186
|
+
roots << ENV["ProgramFiles"] if ENV["ProgramFiles"]
|
|
187
|
+
roots << ENV["ProgramFiles(x86)"] if ENV["ProgramFiles(x86)"]
|
|
188
|
+
roots << ENV["CommonProgramFiles"] if ENV["CommonProgramFiles"]
|
|
189
|
+
roots << ENV["USERPROFILE"] if ENV["USERPROFILE"]
|
|
190
|
+
roots << "#{ENV['SystemDrive']}/" if ENV["SystemDrive"]
|
|
191
|
+
roots << ENV["SystemDrive"] if ENV["SystemDrive"]
|
|
192
|
+
end
|
|
193
|
+
roots << home_dir
|
|
194
|
+
roots.compact.map { |r| normalize_path(r) }.uniq
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def drive_root?(path)
|
|
198
|
+
return false unless windows?
|
|
199
|
+
|
|
200
|
+
# Purely lexical: a bare drive-relative "D:" expands to the CURRENT
|
|
201
|
+
# directory on that drive, so expanding first would make "is this a
|
|
202
|
+
# drive root?" depend on the process cwd (which drive the workspace
|
|
203
|
+
# lives on). Security callers pass already-expanded absolute paths;
|
|
204
|
+
# compare the literal form here.
|
|
205
|
+
cleaned = path.to_s.tr("\\", "/").downcase.sub(%r{/+\z}, "")
|
|
206
|
+
cleaned =~ /^[a-z]:$/
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Platform
|
|
5
|
+
# Process and environment management (e.g. kill_tree).
|
|
6
|
+
# Extracted from lib/rubik/platform.rb (Plan 86 D-4).
|
|
7
|
+
module Process
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.extend self
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def kill_tree(pid)
|
|
13
|
+
return unless pid&.positive?
|
|
14
|
+
|
|
15
|
+
if windows?
|
|
16
|
+
begin
|
|
17
|
+
# Plan 55 D-1 ile tutarlı dizi-form çağrı: cmd.exe ara katmanı ve kabuk
|
|
18
|
+
# yönlendirmesi ("> NUL 2>&1") yerine out:/err: seçenekleri kullanılır.
|
|
19
|
+
system("taskkill", "/F", "/T", "/PID", pid.to_s, out: File::NULL, err: File::NULL)
|
|
20
|
+
rescue
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
begin
|
|
25
|
+
::Process.kill("TERM", pid)
|
|
26
|
+
rescue
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
begin
|
|
30
|
+
::Process.kill("KILL", pid)
|
|
31
|
+
rescue
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Platform
|
|
5
|
+
# Encoding and JSON hygiene (UTF-8 sanitation, safe JSON generation, XML escaping).
|
|
6
|
+
# This is the growth family that drove platform.rb over 300 lines.
|
|
7
|
+
# Extracted from lib/rubik/platform.rb (Plan 86 D-4).
|
|
8
|
+
module Sanitize
|
|
9
|
+
def self.included(base)
|
|
10
|
+
base.extend self
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Escapes a value for safe interpolation into XML text nodes and
|
|
14
|
+
# attribute values. Used by tool-result envelopes (<tool_result>) and
|
|
15
|
+
# the system-context XML so untrusted content (file contents, paths,
|
|
16
|
+
# user prompts) can neither break the envelope nor smuggle instructions.
|
|
17
|
+
def xml_escape(value)
|
|
18
|
+
CGI.escapeHTML(value.to_s)
|
|
19
|
+
rescue StandardError
|
|
20
|
+
""
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Deeply sanitizes invalid or malformed UTF-8 byte sequences from a string
|
|
24
|
+
def clean_utf8(str)
|
|
25
|
+
return str unless str.is_a?(String)
|
|
26
|
+
return str if str.encoding == Encoding::UTF_8 && str.valid_encoding?
|
|
27
|
+
|
|
28
|
+
if str.valid_encoding?
|
|
29
|
+
begin
|
|
30
|
+
return str.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "")
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
debug_warn("clean_utf8 encode fallback: #{e.class}: #{e.message}")
|
|
33
|
+
# fallback below
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
str.to_s.dup.force_encoding(Encoding::UTF_8).scrub("")
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
debug_warn("clean_utf8 scrub fallback: #{e.class}: #{e.message}")
|
|
40
|
+
begin
|
|
41
|
+
str.to_s.scrub("")
|
|
42
|
+
rescue
|
|
43
|
+
""
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# UTF-8 hygiene for strings whose encoding TAG is untrusted but whose
|
|
48
|
+
# bytes are expected to be UTF-8: HTTP bodies served without a charset
|
|
49
|
+
# arrive from Net::HTTP tagged ASCII-8BIT, and so does process/pipe
|
|
50
|
+
# output. Re-tags the string as UTF-8 and scrubs invalid sequences,
|
|
51
|
+
# PRESERVING valid multibyte content. clean_utf8 is the wrong tool for
|
|
52
|
+
# this: its encode step treats bytes >= 0x80 as undefined when
|
|
53
|
+
# converting FROM binary and silently strips them.
|
|
54
|
+
def safe_utf8(str)
|
|
55
|
+
return str unless str.is_a?(String)
|
|
56
|
+
return str if str.encoding == Encoding::UTF_8 && str.valid_encoding?
|
|
57
|
+
|
|
58
|
+
retagged = str.dup.force_encoding(Encoding::UTF_8)
|
|
59
|
+
retagged.valid_encoding? ? retagged : retagged.scrub("")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Recursively sanitizes hashes, arrays, symbols, and strings for safe UTF-8 encoding
|
|
63
|
+
def clean_utf8_deep(obj, seen = nil)
|
|
64
|
+
case obj
|
|
65
|
+
when String
|
|
66
|
+
clean_utf8(obj)
|
|
67
|
+
when Hash
|
|
68
|
+
seen ||= Set.new
|
|
69
|
+
return "{...}" if seen.include?(obj.object_id)
|
|
70
|
+
|
|
71
|
+
seen.add(obj.object_id)
|
|
72
|
+
|
|
73
|
+
obj.each_with_object({}) do |(k, v), acc|
|
|
74
|
+
cleaned_k = if k.is_a?(String)
|
|
75
|
+
clean_utf8(k)
|
|
76
|
+
else
|
|
77
|
+
(k.is_a?(Symbol) ? clean_utf8(k.to_s).to_sym : clean_utf8_deep(k, seen))
|
|
78
|
+
end
|
|
79
|
+
acc[cleaned_k] = clean_utf8_deep(v, seen)
|
|
80
|
+
end
|
|
81
|
+
when Array
|
|
82
|
+
seen ||= Set.new
|
|
83
|
+
return "[...]" if seen.include?(obj.object_id)
|
|
84
|
+
|
|
85
|
+
seen.add(obj.object_id)
|
|
86
|
+
|
|
87
|
+
obj.map { |item| clean_utf8_deep(item, seen) }
|
|
88
|
+
when Set
|
|
89
|
+
seen ||= Set.new
|
|
90
|
+
return Set.new(["[...]"]) if seen.include?(obj.object_id)
|
|
91
|
+
|
|
92
|
+
seen.add(obj.object_id)
|
|
93
|
+
|
|
94
|
+
obj.to_set { |item| clean_utf8_deep(item, seen) }
|
|
95
|
+
when Symbol
|
|
96
|
+
clean_utf8(obj.to_s).to_sym
|
|
97
|
+
else
|
|
98
|
+
obj
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Generates JSON safely without raising JSON::GeneratorError on malformed UTF-8
|
|
103
|
+
def safe_json_generate(obj, *)
|
|
104
|
+
cleaned = clean_utf8_deep(obj)
|
|
105
|
+
JSON.generate(cleaned, *)
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
debug_warn("safe_json_generate fallback: #{e.class}: #{e.message}")
|
|
108
|
+
fallback = sanitize_for_json_fallback(obj)
|
|
109
|
+
begin
|
|
110
|
+
JSON.generate(fallback, *)
|
|
111
|
+
rescue
|
|
112
|
+
"{}"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Dumps JSON safely without raising JSON::GeneratorError on malformed UTF-8
|
|
117
|
+
def safe_json_dump(obj, *)
|
|
118
|
+
cleaned = clean_utf8_deep(obj)
|
|
119
|
+
JSON.dump(cleaned, *)
|
|
120
|
+
rescue StandardError => e
|
|
121
|
+
debug_warn("safe_json_dump fallback: #{e.class}: #{e.message}")
|
|
122
|
+
fallback = sanitize_for_json_fallback(obj)
|
|
123
|
+
begin
|
|
124
|
+
JSON.dump(fallback, *)
|
|
125
|
+
rescue
|
|
126
|
+
"{}"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def sanitize_for_json_fallback(obj)
|
|
133
|
+
case obj
|
|
134
|
+
when Hash
|
|
135
|
+
obj.each_with_object({}) do |(k, v), acc|
|
|
136
|
+
acc[clean_utf8(k.to_s)] = sanitize_for_json_fallback(v)
|
|
137
|
+
end
|
|
138
|
+
when Array, Set
|
|
139
|
+
obj.map { |item| sanitize_for_json_fallback(item) }
|
|
140
|
+
when String
|
|
141
|
+
clean_utf8(obj)
|
|
142
|
+
when Numeric, TrueClass, FalseClass, NilClass
|
|
143
|
+
obj
|
|
144
|
+
else
|
|
145
|
+
clean_utf8(obj.to_s)
|
|
146
|
+
end
|
|
147
|
+
rescue StandardError => e
|
|
148
|
+
debug_warn("sanitize_for_json_fallback rescue: #{e.class}: #{e.message}")
|
|
149
|
+
begin
|
|
150
|
+
obj.to_s.scrub("")
|
|
151
|
+
rescue
|
|
152
|
+
""
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
require "cgi"
|
|
7
|
+
require "json"
|
|
8
|
+
|
|
9
|
+
require_relative "platform/path"
|
|
10
|
+
require_relative "platform/process"
|
|
11
|
+
require_relative "platform/filesystem"
|
|
12
|
+
require_relative "platform/sanitize"
|
|
13
|
+
require_relative "platform/io_drain"
|
|
14
|
+
|
|
15
|
+
module Rubik
|
|
16
|
+
module Platform
|
|
17
|
+
# Common constants shared across sub-modules
|
|
18
|
+
WINDOWS_SHELL_BUILTINS = [
|
|
19
|
+
"echo", "dir", "cd", "cls", "set", "type", "copy", "move", "del", "md", "rd", "exit", "rem", "ver", "vol"
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# Audit 1: gated debug logging for the platform layer's many intentional
|
|
24
|
+
# rescue-and-fallback paths (UTF-8 sanitation, atomic writes). Follows
|
|
25
|
+
# the codebase's ENV["RUBIK_DEBUG"] convention (see config/store.rb) —
|
|
26
|
+
# silent in normal operation, diagnosable on demand, never raises.
|
|
27
|
+
#
|
|
28
|
+
# $stderr.puts, not Kernel#warn: warn is silenced when $VERBOSE is nil
|
|
29
|
+
# (bundler/rake -W0) — the exact context these diagnostics must survive.
|
|
30
|
+
# Same ruling as Log.emit.
|
|
31
|
+
def debug_warn(msg)
|
|
32
|
+
$stderr.puts "[WARN] #{msg}" if ENV["RUBIK_DEBUG"] # rubocop:disable Style/StderrPuts
|
|
33
|
+
rescue StandardError
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Boot/warm async-knob resolution with test-armor defaulting (arch
|
|
38
|
+
# 2026-10-08 #12): an explicit true/false config value always wins;
|
|
39
|
+
# nil (unset) falls back to the RUBIK_TEST env default — RUBIK_TEST=1
|
|
40
|
+
# resolves false so tests warm/boot synchronously. Consumers:
|
|
41
|
+
# repl/lifecycle.rb#warm_up! (ast.async_warm, mcp.async_load).
|
|
42
|
+
def async_knob(config, key)
|
|
43
|
+
value = config.fetch(key, nil)
|
|
44
|
+
value = ENV["RUBIK_TEST"] != "1" if value.nil?
|
|
45
|
+
value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def host_os
|
|
49
|
+
RbConfig::CONFIG["host_os"] || ""
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def ruby_platform
|
|
53
|
+
RUBY_PLATFORM
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def os_family
|
|
57
|
+
if windows?
|
|
58
|
+
:windows
|
|
59
|
+
elsif macos?
|
|
60
|
+
:macos
|
|
61
|
+
elsif linux?
|
|
62
|
+
:linux
|
|
63
|
+
else
|
|
64
|
+
:unknown
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def windows?
|
|
69
|
+
Gem.win_platform? || !(host_os =~ /mswin|mingw|cygwin|bccwin|wince/i).nil?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def macos?
|
|
73
|
+
(host_os =~ /darwin/i) != nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def linux?
|
|
77
|
+
!windows? && !macos?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def unix?
|
|
81
|
+
!windows?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def dev_null
|
|
85
|
+
File::NULL
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def home_dir
|
|
89
|
+
Dir.home
|
|
90
|
+
rescue
|
|
91
|
+
(ENV["USERPROFILE"] || (ENV.fetch("HOMEDRIVE",
|
|
92
|
+
nil) && ENV.fetch("HOMEPATH",
|
|
93
|
+
nil) && "#{ENV.fetch('HOMEDRIVE',
|
|
94
|
+
nil)}#{ENV.fetch('HOMEPATH', nil)}") || ENV["HOME"] || ".")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def tmp_dir
|
|
98
|
+
Dir.tmpdir
|
|
99
|
+
rescue
|
|
100
|
+
(ENV["TEMP"] || ENV["TMP"] || "/tmp")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Include all sub-modules so the singleton class (class << self) gains
|
|
105
|
+
# their methods via extend in the included hook. Each sub-module
|
|
106
|
+
# `extend`s itself when included, making its methods available as
|
|
107
|
+
# `Rubik::Platform.<method>` for backward compatibility.
|
|
108
|
+
include Platform::Path
|
|
109
|
+
include Platform::Process
|
|
110
|
+
include Platform::FileSystem
|
|
111
|
+
include Platform::Sanitize
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Plugin
|
|
5
|
+
class Definition
|
|
6
|
+
attr_reader :name, :version, :description, :inject, :requires, :factory_block
|
|
7
|
+
|
|
8
|
+
def initialize(name, version: "1.0.0", description: "", inject: [], requires: [], &block)
|
|
9
|
+
@name = name
|
|
10
|
+
@version = version
|
|
11
|
+
@description = description
|
|
12
|
+
@inject = Array(inject)
|
|
13
|
+
@requires = Array(requires)
|
|
14
|
+
@factory_block = block
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Registry
|
|
19
|
+
@definitions = {}
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
attr_reader :definitions
|
|
23
|
+
|
|
24
|
+
def register(definition)
|
|
25
|
+
@definitions[definition.name.to_s] = definition
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get(name)
|
|
29
|
+
@definitions[name.to_s]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def clear!
|
|
33
|
+
@definitions.clear
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Top level DSL
|
|
40
|
+
def self.plugin(name, version: "1.0.0", description: "", inject: [], requires: [], &)
|
|
41
|
+
definition = Plugin::Definition.new(
|
|
42
|
+
name,
|
|
43
|
+
version: version,
|
|
44
|
+
description: description,
|
|
45
|
+
inject: inject,
|
|
46
|
+
requires: requires,
|
|
47
|
+
&
|
|
48
|
+
)
|
|
49
|
+
Plugin::Registry.register(definition)
|
|
50
|
+
end
|
|
51
|
+
end
|