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