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,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "../../coeffects"
|
|
5
|
+
|
|
6
|
+
module Rubik
|
|
7
|
+
module REPL
|
|
8
|
+
module Commands
|
|
9
|
+
# !prune — KÖK pencere budayıcısının kullanıcı tarafı (context-pruner
|
|
10
|
+
# planı 2026-10-09). !storage emsali: coeffect üzerinden çözer, kapalıyken
|
|
11
|
+
# dürüstçe kapalı der (knob operator-only: subagents.pruner.*).
|
|
12
|
+
# "now" zorunlu budama kuyruklar (deterministik makbuz; LLM asla
|
|
13
|
+
# bekletilmez — plan turn sınırında uygulanır), "status" durum raporu.
|
|
14
|
+
module Pruner
|
|
15
|
+
extend Base
|
|
16
|
+
|
|
17
|
+
USAGE_PRUNE = "Usage: !prune [now|status]"
|
|
18
|
+
|
|
19
|
+
def self.register(shell)
|
|
20
|
+
handler = lambda do |args, ctx|
|
|
21
|
+
harness = resolve_pruner(ctx)
|
|
22
|
+
unless harness
|
|
23
|
+
ctx.out.puts unbound_message(ctx)
|
|
24
|
+
next
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sub = args.first.to_s.downcase
|
|
28
|
+
if ["status", ""].include?(sub)
|
|
29
|
+
ctx.out.puts harness.status_text
|
|
30
|
+
elsif ["now", "run", "force"].include?(sub)
|
|
31
|
+
ctx.out.puts harness.prune(reason: "user")
|
|
32
|
+
else
|
|
33
|
+
ctx.out.puts USAGE_PRUNE
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
shell.register_command(
|
|
38
|
+
"prune",
|
|
39
|
+
description: "Clean the ROOT conversation window via the context pruner (!prune status for details)",
|
|
40
|
+
usage: USAGE_PRUNE,
|
|
41
|
+
audience: :both,
|
|
42
|
+
&handler
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.resolve_pruner(ctx)
|
|
47
|
+
rt = ctx.respond_to?(:runtime_manager) ? ctx.runtime_manager : nil
|
|
48
|
+
rt&.context ? Rubik::Coeffects.get(rt.context, Rubik::Coeffects::PRUNER) : nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Knob kapalı ile AÇIK-AMA-BAĞLANAMAMIŞ ayrımı (review 2026-10-09):
|
|
52
|
+
# ikincisi ayarı zaten açık olan operatörü sonsuz "enable et" döngüsüne
|
|
53
|
+
# sokardı — teşhis (boot warn + dmesg) adı adına söylenir.
|
|
54
|
+
def self.unbound_message(ctx)
|
|
55
|
+
knob = begin
|
|
56
|
+
ctx.config.fetch("subagents.pruner.enabled", true)
|
|
57
|
+
rescue StandardError
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
if knob == true
|
|
61
|
+
"Context pruner is ENABLED but did not bind (boot-order gap or missing supervisor — see the " \
|
|
62
|
+
"startup warning / dmesg). Fix the reported cause and restart the session."
|
|
63
|
+
else
|
|
64
|
+
"Context pruner is off (explicit opt-out). An operator can re-enable it with: " \
|
|
65
|
+
"!config set subagents.pruner.enabled true [--project|--global], then restart the session."
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "../../session"
|
|
5
|
+
|
|
6
|
+
module Rubik
|
|
7
|
+
module REPL
|
|
8
|
+
module Commands
|
|
9
|
+
module Session
|
|
10
|
+
extend Base
|
|
11
|
+
|
|
12
|
+
# Whole-command synopsis: shared by the spine registration (usage:)
|
|
13
|
+
# and the unknown-subcommand fallback branch, so the two cannot drift.
|
|
14
|
+
USAGE_SESSION = "Usage: !session <list | resume [id] | save <name> | info | prune [days] [--apply] [--all]>"
|
|
15
|
+
PRUNE_DEFAULT_DAYS = 7
|
|
16
|
+
|
|
17
|
+
def self.register(shell)
|
|
18
|
+
shell.register_command("history", description: "List recent session history", audience: :both) do |args, ctx|
|
|
19
|
+
limit = (args.first || 20).to_i
|
|
20
|
+
hist = ctx.recorder.read_history(limit)
|
|
21
|
+
hist.each_with_index do |item, idx|
|
|
22
|
+
ctx.out.puts " #{idx + 1}: #{item['payload']}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Plan 91 — explicit multi-line paste mode: collects lines until a
|
|
27
|
+
# lone "." line, then dispatches the joined text as one prompt turn.
|
|
28
|
+
shell.register_command("paste", description: "Multi-line paste mode (finish with lone '.')") do |_args, ctx|
|
|
29
|
+
ctx.request_paste!
|
|
30
|
+
ctx.out.puts "[Rubik] Paste modu bir sonraki prompt'ta başlayacak."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
shell.register_command("replay", description: "Replay session file") do |args, ctx|
|
|
34
|
+
path = args.first
|
|
35
|
+
replayer = ctx.session_replayer
|
|
36
|
+
if replayer.replay_file(path)
|
|
37
|
+
ctx.out.puts "Replay completed."
|
|
38
|
+
else
|
|
39
|
+
ctx.out.puts "Session file not found: #{path}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
shell.register_command("session", description: "Manage recorded sessions (list|resume|info|prune)",
|
|
44
|
+
usage: USAGE_SESSION) do |args, ctx|
|
|
45
|
+
sub = args.first&.downcase || "list"
|
|
46
|
+
root_d = ctx.buffer_manager&.root_dir || Dir.pwd
|
|
47
|
+
home_d = ctx.config&.home_dir || ENV.fetch("HOME", nil)
|
|
48
|
+
|
|
49
|
+
case sub
|
|
50
|
+
when "list"
|
|
51
|
+
sessions = Rubik::Session::Manager.list_sessions(root_d, home_d, config: ctx.config)
|
|
52
|
+
if sessions.empty?
|
|
53
|
+
ctx.out.puts "No recorded sessions found for this workspace."
|
|
54
|
+
else
|
|
55
|
+
ctx.out.puts "=== Recorded Sessions (#{sessions.size}) ==="
|
|
56
|
+
sessions.each_with_index do |s, idx|
|
|
57
|
+
mark = s[:path] == ctx.recorder.session_file ? " [CURRENT]" : ""
|
|
58
|
+
ctx.out.puts " #{idx + 1}. #{s[:id]} (#{s[:scope]}) - #{s[:event_count]} events - Last: \"#{s[:last_prompt]}\"#{mark}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
when "resume", "continue", "load"
|
|
62
|
+
target_id = args[1]
|
|
63
|
+
if target_id.nil?
|
|
64
|
+
picked = interactive_session_pick(ctx)
|
|
65
|
+
if picked == :picker_cancelled
|
|
66
|
+
ctx.out.puts "[Rubik::Session] Resume iptal edildi."
|
|
67
|
+
next
|
|
68
|
+
end
|
|
69
|
+
target_id = picked || :latest
|
|
70
|
+
end
|
|
71
|
+
res = ctx.resume_session(target_id)
|
|
72
|
+
ctx.out.puts "Resume failed: #{res[:error]}" unless res[:ok]
|
|
73
|
+
when "info"
|
|
74
|
+
ctx.out.puts "Active Session File: #{ctx.recorder.session_file}"
|
|
75
|
+
ctx.out.puts "Total Events in Session: #{ctx.recorder.events.size}"
|
|
76
|
+
when "prune", "clean", "cleanup"
|
|
77
|
+
run_prune(args.drop(1), ctx)
|
|
78
|
+
when "save"
|
|
79
|
+
target_name = args[1]
|
|
80
|
+
if target_name.nil? || target_name.strip.empty?
|
|
81
|
+
ctx.out.puts "Usage: !session save <name>"
|
|
82
|
+
else
|
|
83
|
+
safe_name = File.basename(target_name.strip.sub(/\.jsonl$/, ""))
|
|
84
|
+
if safe_name.empty? || safe_name.start_with?(".")
|
|
85
|
+
ctx.out.puts "Error: Invalid session name '#{target_name}'."
|
|
86
|
+
next
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
target_dir = File.join(root_d, ".rubik", "sessions")
|
|
90
|
+
FileUtils.mkdir_p(target_dir)
|
|
91
|
+
target_file = File.join(target_dir, "#{safe_name}.jsonl")
|
|
92
|
+
|
|
93
|
+
if ctx.recorder.session_file && File.exist?(ctx.recorder.session_file)
|
|
94
|
+
FileUtils.cp(ctx.recorder.session_file, target_file)
|
|
95
|
+
else
|
|
96
|
+
File.open(target_file, "w") do |f|
|
|
97
|
+
ctx.recorder.events.each { |ev| f.puts(JSON.dump(ev)) }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
ctx.recorder.switch_session_file(target_file, cleanup_empty_current: false)
|
|
101
|
+
ctx.out.puts "[Rubik::Session] Session saved to #{target_file}"
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
ctx.out.puts USAGE_SESSION
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Interactive session picker (Antigravity /resume style): arrow-key
|
|
110
|
+
# selection over recorded sessions on capable TTYs. Returns the
|
|
111
|
+
# session id, :picker_cancelled on Ctrl+C (caller aborts the
|
|
112
|
+
# resume), or nil in pipes/tests so `resume` falls back to :latest.
|
|
113
|
+
class << self
|
|
114
|
+
# `!session prune [days] [--apply] [--all]` — dry-run by default:
|
|
115
|
+
# prints the blast radius of files that WOULD be deleted and only
|
|
116
|
+
# unlinks once the operator passes --apply. The active session file
|
|
117
|
+
# is always protected. `--all` widens scope to ~/.rubik/sessions.
|
|
118
|
+
def run_prune(rest, ctx)
|
|
119
|
+
apply = rest.any? { |a| ["--apply", "--yes", "-y", "apply", "delete"].include?(a.downcase) }
|
|
120
|
+
include_global = rest.any? { |a| ["--all", "-a", "all", "global"].include?(a.downcase) }
|
|
121
|
+
day_arg = rest.find { |a| a =~ /\A\d+(\.\d+)?\z/ }
|
|
122
|
+
days = day_arg ? day_arg.to_f : PRUNE_DEFAULT_DAYS
|
|
123
|
+
days = PRUNE_DEFAULT_DAYS unless days.positive?
|
|
124
|
+
|
|
125
|
+
root_d = ctx.buffer_manager&.root_dir || Dir.pwd
|
|
126
|
+
home_d = ctx.config&.home_dir || ENV.fetch("HOME", nil)
|
|
127
|
+
active = ctx.recorder&.session_file
|
|
128
|
+
|
|
129
|
+
report = Rubik::Session::Manager.prune_sessions(
|
|
130
|
+
days: days, root_dir: root_d, home_dir: home_d,
|
|
131
|
+
exclude_file: active, apply: apply, include_global: include_global, config: ctx.config
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
scope_label = include_global ? "project + global" : "project"
|
|
135
|
+
size_mb = report[:bytes] / 1_048_576.0
|
|
136
|
+
|
|
137
|
+
if report[:candidates].zero?
|
|
138
|
+
ctx.out.puts "[Rubik::Session] No #{scope_label} sessions older than #{format_days(days)} to prune."
|
|
139
|
+
return
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
verb = apply ? "Deleted" : "Would delete"
|
|
143
|
+
ctx.out.puts "[Rubik::Session] #{verb} #{report[:candidates]} #{scope_label} session(s) " \
|
|
144
|
+
"older than #{format_days(days)} (#{format('%.1f', size_mb)} MB)."
|
|
145
|
+
unless report[:sample].empty?
|
|
146
|
+
ctx.out.puts " e.g. #{report[:sample].join(', ')}#{', …' if report[:candidates] > report[:sample].size}"
|
|
147
|
+
end
|
|
148
|
+
ctx.out.puts " [CURRENT] active session protected: #{File.basename(active.to_s)}" if report[:protected_skipped].positive?
|
|
149
|
+
|
|
150
|
+
ctx.out.puts " Dry run — nothing removed. Re-run with '--apply' to delete." unless apply
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def format_days(days)
|
|
154
|
+
days == days.to_i ? "#{days.to_i}d" : "#{days.round(1)}d"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def interactive_session_pick(ctx)
|
|
158
|
+
return nil unless Rubik::UI::Capability.interactive?(input: ctx.input, output: ctx.output)
|
|
159
|
+
|
|
160
|
+
root_d = ctx.buffer_manager&.root_dir || Dir.pwd
|
|
161
|
+
home_d = ctx.config&.home_dir || ENV.fetch("HOME", nil)
|
|
162
|
+
sessions = Rubik::Session::Manager.list_sessions(root_d, home_d, config: ctx.config)
|
|
163
|
+
return nil if sessions.empty?
|
|
164
|
+
|
|
165
|
+
prompt = Rubik::UI::Approvals.build_prompt(ctx)
|
|
166
|
+
prompt.select("Devam edilecek oturumu seç:") do |menu|
|
|
167
|
+
sessions.first(15).each do |s|
|
|
168
|
+
menu.choice "#{s[:id]} · #{s[:event_count]} olay · \"#{s[:last_prompt].to_s[0, 40]}\"", s[:id]
|
|
169
|
+
end
|
|
170
|
+
menu.choice "← en son oturum (:latest)", :latest
|
|
171
|
+
end
|
|
172
|
+
rescue Interrupt
|
|
173
|
+
Rubik::REPL::InterruptHandler.restore_console_after_interrupt
|
|
174
|
+
:picker_cancelled
|
|
175
|
+
rescue StandardError => e
|
|
176
|
+
Rubik::Log.warn(:repl, "session picker failed (#{e.class}: #{e.message}); resume selection skipped")
|
|
177
|
+
nil
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module REPL
|
|
5
|
+
module Commands
|
|
6
|
+
module Skills
|
|
7
|
+
# Whole-command synopsis: shared by the spine registration (usage:)
|
|
8
|
+
# and the unknown-subcommand fallback branch, so the two cannot drift.
|
|
9
|
+
USAGE_SKILL = "Usage: !skill [list|show <name>|info <name>|reload]"
|
|
10
|
+
|
|
11
|
+
def self.register(shell)
|
|
12
|
+
handler = proc do |args, ctx|
|
|
13
|
+
manager = ctx.skill_manager
|
|
14
|
+
unless manager
|
|
15
|
+
ctx.out.puts "Skill manager is not initialized."
|
|
16
|
+
next
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
args = args.split if args.is_a?(String)
|
|
20
|
+
subcommand = args[0] || "list"
|
|
21
|
+
|
|
22
|
+
case subcommand
|
|
23
|
+
when "list" then list_skills(ctx, manager)
|
|
24
|
+
when "show" then show_skill(ctx, manager, args[1])
|
|
25
|
+
when "info" then info_skill(ctx, manager, args[1])
|
|
26
|
+
when "reload" then reload_skills(ctx, manager)
|
|
27
|
+
else
|
|
28
|
+
ctx.out.puts "Unknown subcommand: #{subcommand}. #{USAGE_SKILL}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
shell.register_command("skill", description: "Agent skills (agentskills.io): list, show <name>, info <name>, reload",
|
|
33
|
+
usage: USAGE_SKILL, audience: :both, &handler)
|
|
34
|
+
shell.register_command("skills", description: "List discovered agent skills", usage: USAGE_SKILL, audience: :human) do |_args, ctx|
|
|
35
|
+
handler.call(["list"], ctx)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.list_skills(ctx, manager)
|
|
40
|
+
summary = manager.status_summary
|
|
41
|
+
skills = manager.all
|
|
42
|
+
ctx.out.puts "🧠 Agent Skills — #{skills.size} active (#{summary[:skipped]} skipped, #{summary[:notes]} notes)"
|
|
43
|
+
if skills.empty?
|
|
44
|
+
ctx.out.puts "No skills discovered. Add .rubik/skills/<name>/SKILL.md (project), " \
|
|
45
|
+
"~/.rubik/skills/<name>/SKILL.md (user), or extend the skills.paths config."
|
|
46
|
+
else
|
|
47
|
+
skills.each do |skill|
|
|
48
|
+
desc = skill.description.length > 72 ? "#{skill.description[0, 69]}..." : skill.description
|
|
49
|
+
ctx.out.puts format(" %<name>-24s [%<scope>-7s] %<desc>s", name: skill.name, scope: skill.scope, desc: desc)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
print_shadowed(ctx, summary[:shadowed])
|
|
53
|
+
print_issues(ctx, manager)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.show_skill(ctx, manager, name)
|
|
57
|
+
skill = manager.find(name.to_s)
|
|
58
|
+
return ctx.out.puts "Unknown skill '#{name}'. Use '!skill list'." unless skill
|
|
59
|
+
|
|
60
|
+
ctx.out.puts Rubik::Platform.read_text(skill.path)
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
ctx.out.puts "Error reading skill '#{name}': #{e.message}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.info_skill(ctx, manager, name)
|
|
66
|
+
skill = manager.find(name.to_s)
|
|
67
|
+
return ctx.out.puts "Unknown skill '#{name}'. Use '!skill list'." unless skill
|
|
68
|
+
|
|
69
|
+
ctx.out.puts "=== Skill '#{skill.name}' ==="
|
|
70
|
+
ctx.out.puts " Scope : #{skill.scope}"
|
|
71
|
+
ctx.out.puts " Directory : #{skill.root}"
|
|
72
|
+
ctx.out.puts " Description : #{skill.description}"
|
|
73
|
+
ctx.out.puts " License : #{skill.license || '-'}"
|
|
74
|
+
ctx.out.puts " Compatibility : #{skill.compatibility || '-'}"
|
|
75
|
+
allowed = skill.allowed_tools.empty? ? "-" : "#{skill.allowed_tools.join(' ')} (advisory, not enforced)"
|
|
76
|
+
ctx.out.puts " Allowed tools : #{allowed}"
|
|
77
|
+
meta = skill.metadata.empty? ? "-" : skill.metadata.map { |k, v| "#{k}=#{v}" }.join(", ")
|
|
78
|
+
ctx.out.puts " Metadata : #{meta}"
|
|
79
|
+
aux = skill.auxiliary_files
|
|
80
|
+
resources = aux.empty? ? "none" : aux.map { |dir, files| "#{dir}/ (#{files.size})" }.join(", ")
|
|
81
|
+
ctx.out.puts " Resources : #{resources}"
|
|
82
|
+
skill.warnings.each { |warning| ctx.out.puts " ⚠ #{warning.field} #{warning.message}" }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.reload_skills(ctx, manager)
|
|
86
|
+
manager.reload!
|
|
87
|
+
summary = manager.status_summary
|
|
88
|
+
scopes = summary[:by_scope].map { |scope, count| "#{scope}: #{count}" }.join(", ")
|
|
89
|
+
ctx.out.puts "🔄 Skills reloaded: #{summary[:total]} active (#{scopes.empty? ? 'none' : scopes}), " \
|
|
90
|
+
"#{summary[:skipped]} skipped, #{summary[:notes]} notes."
|
|
91
|
+
print_shadowed(ctx, summary[:shadowed])
|
|
92
|
+
print_issues(ctx, manager)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.print_shadowed(ctx, shadowed)
|
|
96
|
+
shadowed.each do |name, entry|
|
|
97
|
+
losers = entry[:loser_scopes].join(", ")
|
|
98
|
+
ctx.out.puts " ⇢ '#{name}' shadowed: #{losers} copy ignored (winner: #{entry[:winner_scope]})"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.print_issues(ctx, manager)
|
|
103
|
+
manager.issues.each do |issue|
|
|
104
|
+
mark = issue.error? ? "⚠" : "·"
|
|
105
|
+
ctx.out.puts " #{mark} #{File.basename(issue.path)}: #{issue.field} #{issue.message}"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "../../coeffects"
|
|
5
|
+
|
|
6
|
+
module Rubik
|
|
7
|
+
module REPL
|
|
8
|
+
module Commands
|
|
9
|
+
# !storage — merkezi rubik://storage FS-DB'ye kullanıcı tarafı bakış
|
|
10
|
+
# (storage-show-surface planı B): "raporu göster" isteğini modeli tamamen
|
|
11
|
+
# devre dışı bırakarak karşılar. Depo araç-eylemlerinin aksine bu komut
|
|
12
|
+
# sadece OKUR — yazma düzlemi storage aracında kalır.
|
|
13
|
+
module Storage
|
|
14
|
+
extend Base
|
|
15
|
+
|
|
16
|
+
USAGE_STORAGE = "Usage: !storage [<type|id|type.row|row|hash-prefix|ref>] [refs]"
|
|
17
|
+
|
|
18
|
+
def self.register(shell)
|
|
19
|
+
handler = lambda do |args, ctx|
|
|
20
|
+
store = resolve_store(ctx)
|
|
21
|
+
unless store
|
|
22
|
+
ctx.out.puts "Central storage is not available in this runtime (config storage.enabled=false or standalone construction)."
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
dispatch(args, ctx, store)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
shell.register_command(
|
|
30
|
+
"storage",
|
|
31
|
+
description: "Browse the central rubik://storage database (entities and refs)",
|
|
32
|
+
usage: USAGE_STORAGE,
|
|
33
|
+
audience: :both,
|
|
34
|
+
&handler
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.dispatch(args, ctx, store)
|
|
39
|
+
return render_refs_table(ctx, store) if args.include?("refs")
|
|
40
|
+
return ctx.out.print(Rubik::Storage::Render.project(store.project, store.project_summary)) if args.empty?
|
|
41
|
+
|
|
42
|
+
selector = args.first.to_s
|
|
43
|
+
return render_type_listing(ctx, store, selector.downcase) if Rubik::Storage::Store::TYPES.include?(selector.downcase)
|
|
44
|
+
|
|
45
|
+
record = resolve_any(store, selector)
|
|
46
|
+
if record
|
|
47
|
+
ctx.out.print(Rubik::Storage::Render.card(record, project: store.project, refs: store.refs))
|
|
48
|
+
else
|
|
49
|
+
ctx.out.puts "Storage entity not found: '#{selector}' in project '#{store.project}'. " \
|
|
50
|
+
"Try '!storage' for counts, '!storage refs' for aliases, or '!storage <type>' for a listing."
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Store#get tip ister; kullanıcı seçicisi tipsiz gelir — tüm tiplerde
|
|
55
|
+
# çöz (tam id → type.row → row → hash öneki → alias zinciri meta
|
|
56
|
+
# referanslı olduğu için yalnız eşleşen tipin kayıtlarında tutarlı).
|
|
57
|
+
def self.resolve_any(store, selector)
|
|
58
|
+
Rubik::Storage::Store::TYPES.lazy.map { |t| store.get(type: t, id: selector) }.find(&:itself)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.render_type_listing(ctx, store, type)
|
|
62
|
+
summaries = store.list(type: type).map(&:to_h)
|
|
63
|
+
ctx.out.print(Rubik::Storage::Render.listing(project: store.project, type: type,
|
|
64
|
+
summaries: summaries, refs: store.refs))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.render_refs_table(ctx, store)
|
|
68
|
+
refs = store.refs
|
|
69
|
+
if refs.empty?
|
|
70
|
+
ctx.out.puts "No refs defined in project '#{store.project}'."
|
|
71
|
+
return
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
ctx.out.puts "Refs in '#{store.project}':"
|
|
75
|
+
refs.sort.each { |name, id| ctx.out.puts " #{name} → #{id}" }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.resolve_store(ctx)
|
|
79
|
+
rt = ctx.respond_to?(:runtime_manager) ? ctx.runtime_manager : nil
|
|
80
|
+
rt&.context ? Rubik::Coeffects.get(rt.context, Rubik::Coeffects::STORAGE_STORE) : nil
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "../../orchestrator/subagent/budget"
|
|
5
|
+
require_relative "../../orchestrator/subagent/notices"
|
|
6
|
+
|
|
7
|
+
module Rubik
|
|
8
|
+
module REPL
|
|
9
|
+
module Commands
|
|
10
|
+
# REPL bang command (!subagents) for monitoring and supervising autonomous
|
|
11
|
+
# subagents (Plan 94 - Linux Init PID 1 Supervisor Model).
|
|
12
|
+
module Subagents
|
|
13
|
+
extend Base
|
|
14
|
+
|
|
15
|
+
# Whole-command synopsis: shared by the spine registration (usage:)
|
|
16
|
+
# and the unknown-subcommand fallback branch, so the two cannot drift.
|
|
17
|
+
USAGE_SUBAGENTS = "Usage: !subagents [list | status <id> | wait [id] [sec] | extend <id> [steps] | resume [id] | reuse <id> <prompt> | clean | kill <id> | kill-all]"
|
|
18
|
+
|
|
19
|
+
def self.register(shell)
|
|
20
|
+
handler = lambda do |args, ctx|
|
|
21
|
+
subcmd = args.first&.downcase
|
|
22
|
+
sup = resolve_supervisor(ctx)
|
|
23
|
+
|
|
24
|
+
unless sup
|
|
25
|
+
ctx.out.puts "SubagentSupervisor is not available."
|
|
26
|
+
return
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
case subcmd
|
|
30
|
+
when nil, "list" then render_list(ctx, sup)
|
|
31
|
+
when "status" then render_status(ctx, sup, args[1])
|
|
32
|
+
when "types" then render_types(ctx, sup)
|
|
33
|
+
when "extend", "grant", "steps" then handle_extend(ctx, sup, args[1], args[2])
|
|
34
|
+
when "resume", "retry" then handle_resume(ctx, sup, args[1])
|
|
35
|
+
when "reuse", "redeploy" then handle_reuse(ctx, sup, args[1], args[2..])
|
|
36
|
+
when "clean", "reap" then handle_clean(ctx, sup)
|
|
37
|
+
when "wait" then handle_wait(ctx, sup, args[1], args[2])
|
|
38
|
+
when "kill" then handle_kill(ctx, sup, args[1])
|
|
39
|
+
when "kill-all", "killall"
|
|
40
|
+
sup.kill_all
|
|
41
|
+
ctx.out.puts "All subagents terminated."
|
|
42
|
+
else
|
|
43
|
+
ctx.out.puts "Unknown !subagents subcommand: '#{subcmd}'. #{USAGE_SUBAGENTS}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
description = "Supervise and manage autonomous subagents (list, status, types, extend, resume, kill, kill-all)"
|
|
48
|
+
shell.register_command("subagents", description: description, usage: USAGE_SUBAGENTS, audience: :both, &handler)
|
|
49
|
+
shell.register_command("subagent", description: "Alias for !subagents", usage: USAGE_SUBAGENTS, audience: :both, &handler)
|
|
50
|
+
# NB: no "agents" alias — System owns !agents (the interactive
|
|
51
|
+
# agent/fiber board); a second registration here would silently
|
|
52
|
+
# overwrite it (register_command only refuses custom-over-builtin).
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.resolve_supervisor(ctx) = ctx.subagent_supervisor
|
|
56
|
+
|
|
57
|
+
def self.render_list(ctx, sup)
|
|
58
|
+
instances = sup.list
|
|
59
|
+
if instances.empty?
|
|
60
|
+
ctx.out.puts "No subagents currently running or tracked."
|
|
61
|
+
return
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
ctx.out.puts "=== Subagent Process Hierarchy (#{instances.size} tracked, #{sup.active_count} active) ==="
|
|
65
|
+
instances.each do |inst|
|
|
66
|
+
dur = if inst.running?
|
|
67
|
+
"up #{inst.uptime.round(1)}s"
|
|
68
|
+
elsif inst.state == :queued
|
|
69
|
+
"queued (waiting on: #{inst.depends_on.join(', ')})"
|
|
70
|
+
else
|
|
71
|
+
"ended (#{inst.uptime.round(1)}s)"
|
|
72
|
+
end
|
|
73
|
+
ctx.out.puts " • [#{inst.id}] #{inst.state.to_s.upcase} (role: #{inst.role}, type: #{inst.type_name}, #{dur})"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.render_status(ctx, sup, id)
|
|
78
|
+
if id.to_s.strip.empty?
|
|
79
|
+
ctx.out.puts "Usage: !subagents status <subagent_id>"
|
|
80
|
+
return
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
inst = sup.get(id)
|
|
84
|
+
unless inst
|
|
85
|
+
ctx.out.puts "Subagent '#{id}' not found."
|
|
86
|
+
return
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
ctx.out.puts "Subagent: #{inst.id} (Parent: #{inst.parent_id})"
|
|
90
|
+
ctx.out.puts " Role: #{inst.role}"
|
|
91
|
+
ctx.out.puts " Type: #{inst.type_name}"
|
|
92
|
+
ctx.out.puts " State: #{inst.state.to_s.upcase}"
|
|
93
|
+
ctx.out.puts " Steps: #{inst.current_step}/#{inst.max_steps}"
|
|
94
|
+
ctx.out.puts " Uptime: #{inst.uptime.round(2)}s"
|
|
95
|
+
ctx.out.puts " Workspace: #{inst.workspace}"
|
|
96
|
+
ctx.out.puts " Transcript: #{inst.transcript_uri}"
|
|
97
|
+
ctx.out.puts " Last Error: #{inst.last_error}" if inst.last_error
|
|
98
|
+
ctx.out.puts " Result: #{inst.result.to_s.slice(0, 150)}..." if inst.result
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def self.render_types(ctx, sup)
|
|
102
|
+
types = sup.type_registry.list
|
|
103
|
+
ctx.out.puts "=== Available Subagent Archetypes (#{types.size}) ==="
|
|
104
|
+
types.each do |t|
|
|
105
|
+
flags = []
|
|
106
|
+
flags << "write" if t.enable_write_tools
|
|
107
|
+
flags << "nesting" if t.enable_subagent_tools
|
|
108
|
+
flag_str = flags.empty? ? "read-only" : flags.join(", ")
|
|
109
|
+
ctx.out.puts " • #{t.name.ljust(12)} [#{flag_str}]: #{t.description}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.handle_kill(ctx, sup, id)
|
|
114
|
+
if id.to_s.strip.empty?
|
|
115
|
+
ctx.out.puts "Usage: !subagents kill <subagent_id>"
|
|
116
|
+
return
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
if sup.kill(id)
|
|
120
|
+
ctx.out.puts "Subagent '#{id}' and child tree killed."
|
|
121
|
+
else
|
|
122
|
+
ctx.out.puts "Subagent '#{id}' not found."
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def self.handle_extend(ctx, sup, id, steps_arg)
|
|
127
|
+
if id.to_s.strip.empty?
|
|
128
|
+
ctx.out.puts "Usage: !subagents extend <subagent_id> [additional_steps]"
|
|
129
|
+
return
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
steps = Rubik::Orchestrator::Subagent::Budget.normalize_grant(steps_arg)
|
|
133
|
+
if sup.extend_budget(id, steps)
|
|
134
|
+
inst = sup.get(id)
|
|
135
|
+
ctx.out.puts "Extended budget for #{id} by #{steps} steps (total: #{inst.max_steps}, state: #{inst.state.to_s.upcase})."
|
|
136
|
+
else
|
|
137
|
+
ctx.out.puts "Could not extend budget for #{id} (not found or inactive)."
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.handle_resume(ctx, sup, id)
|
|
142
|
+
if id.to_s.strip.empty?
|
|
143
|
+
resumed = sup.resume_all
|
|
144
|
+
return ctx.out.puts "No paused subagents to resume." if resumed.empty?
|
|
145
|
+
|
|
146
|
+
ctx.out.puts "Resumed #{resumed.size} subagent(s): #{resumed.join(', ')}."
|
|
147
|
+
elsif sup.resume(id)
|
|
148
|
+
inst = sup.get(id)
|
|
149
|
+
ctx.out.puts "Resumed subagent '#{id}' (state: #{inst.state.to_s.upcase})."
|
|
150
|
+
else
|
|
151
|
+
ctx.out.puts "Could not resume subagent '#{id}' (not found or not in pausable state)."
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Context-preserving reuse of a finished subagent (maestro persona).
|
|
156
|
+
def self.handle_reuse(ctx, sup, id, words)
|
|
157
|
+
prompt = Array(words).join(" ").strip
|
|
158
|
+
return ctx.out.puts "Usage: !subagents reuse <subagent_id> <new task prompt>" if id.to_s.strip.empty? || prompt.empty?
|
|
159
|
+
|
|
160
|
+
begin
|
|
161
|
+
inst = sup.redeploy(id, prompt: prompt)
|
|
162
|
+
ctx.out.puts "Redeployed '#{id}' with a new task — context preserved (state: #{inst.state.to_s.upcase})."
|
|
163
|
+
rescue ArgumentError => e
|
|
164
|
+
ctx.out.puts e.message
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def self.handle_clean(ctx, sup)
|
|
169
|
+
reaped = sup.reap_finished!(force: true)
|
|
170
|
+
return ctx.out.puts "No finished subagents to reap." if reaped.empty?
|
|
171
|
+
|
|
172
|
+
ctx.out.puts "Reaped #{reaped.size} finished subagent(s): #{reaped.join(', ')} (transcripts persist)."
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.handle_wait(ctx, sup, arg1, arg2)
|
|
176
|
+
ids = nil
|
|
177
|
+
timeout = Rubik::Orchestrator::Subagent::Notices::DEFAULT_WAIT_TIMEOUT
|
|
178
|
+
if arg1 =~ /^\d+$/
|
|
179
|
+
timeout = arg1.to_i
|
|
180
|
+
elsif arg1 && !arg1.empty?
|
|
181
|
+
ids = [arg1]
|
|
182
|
+
timeout = arg2.to_i if arg2 =~ /^\d+$/
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
ctx.out.puts "Waiting for subagents (timeout: #{timeout}s)..."
|
|
186
|
+
results = sup.wait_for(ids, timeout: timeout)
|
|
187
|
+
return ctx.out.puts "No active subagents tracked." if results.empty?
|
|
188
|
+
|
|
189
|
+
ctx.out.puts "=== Subagents Wait Finished (#{results.size}) ==="
|
|
190
|
+
results.each { |r| ctx.out.puts " • #{r[:id].ljust(12)} [#{r[:state].to_s.upcase}]: #{r[:role]}" }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|