mixdog 0.9.3 → 0.9.5
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.
- package/README.md +112 -38
- package/package.json +10 -3
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/r5-orchestrated-task.json +7 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/scheduler.mjs +27 -113
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +5 -13
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +11 -9
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +356 -0
- package/src/tui/app/model-options.mjs +334 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +489 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +677 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +13553 -12384
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine.mjs +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/channels/lib/holidays.mjs +0 -138
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/input-editing.selection.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/paste-fix.test.mjs +0 -119
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* extension-pickers.mjs — MCP / Skills / Plugins / Hooks picker cluster.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from App.jsx behavior-preservingly as a dependency-injection
|
|
5
|
+
* factory. These openers drive setPicker + setSettingsPrompt and read live
|
|
6
|
+
* store state, so they can't be pure. Every function body is the original App
|
|
7
|
+
* logic verbatim, with closure identifiers threaded through the factory
|
|
8
|
+
* argument. `disabledSkills` is read fresh via getDisabledSkills() so toggles
|
|
9
|
+
* observe the latest state at call time. openMcpPicker is exposed for the
|
|
10
|
+
* plugin-detail enable-mcp path (also aliased on the App body).
|
|
11
|
+
*/
|
|
12
|
+
export function createExtensionPickers({
|
|
13
|
+
store,
|
|
14
|
+
theme,
|
|
15
|
+
clean,
|
|
16
|
+
copyToClipboard,
|
|
17
|
+
setPicker,
|
|
18
|
+
setProviderPrompt,
|
|
19
|
+
setChannelPrompt,
|
|
20
|
+
setHookPrompt,
|
|
21
|
+
setSettingsPrompt,
|
|
22
|
+
getDisabledSkills,
|
|
23
|
+
setDisabledSkills,
|
|
24
|
+
}) {
|
|
25
|
+
const mcpStatus = () => {
|
|
26
|
+
let status;
|
|
27
|
+
try {
|
|
28
|
+
status = store.mcpStatus?.() || { servers: [] };
|
|
29
|
+
} catch (e) {
|
|
30
|
+
store.pushNotice(`mcp status failed: ${e?.message || e}`, 'error');
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return { ...status, servers: status.servers || [] };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const openMcpServersPicker = (options = {}) => {
|
|
37
|
+
const status = mcpStatus();
|
|
38
|
+
if (!status) return;
|
|
39
|
+
const servers = status.servers || [];
|
|
40
|
+
const items = [];
|
|
41
|
+
if (servers.length === 0) {
|
|
42
|
+
items.push({
|
|
43
|
+
value: 'empty',
|
|
44
|
+
label: 'No MCP servers',
|
|
45
|
+
description: 'no configured MCP servers',
|
|
46
|
+
_action: 'noop',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
for (const server of servers) {
|
|
50
|
+
const enabled = server.enabled !== false;
|
|
51
|
+
items.push({
|
|
52
|
+
value: `server:${server.name}`,
|
|
53
|
+
label: server.name,
|
|
54
|
+
marker: enabled ? '●' : '○',
|
|
55
|
+
markerColor: enabled ? theme.success : theme.inactive,
|
|
56
|
+
description: `${server.status || 'unknown'} · ${server.transport || 'unknown'} · ${server.toolCount || 0} tools${server.error ? ` · ${server.error}` : ''}`,
|
|
57
|
+
_action: 'server',
|
|
58
|
+
_server: server,
|
|
59
|
+
_enabled: enabled,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
setProviderPrompt(null);
|
|
63
|
+
setChannelPrompt(null);
|
|
64
|
+
setHookPrompt(null);
|
|
65
|
+
setSettingsPrompt(null);
|
|
66
|
+
const toggleServer = (item) => {
|
|
67
|
+
if (item._action !== 'server' || !item._server?.name) return;
|
|
68
|
+
void store.setMcpServerEnabled?.(item._server.name, !item._enabled)
|
|
69
|
+
.then(() => openMcpServersPicker({ highlightValue: `server:${item._server.name}` }))
|
|
70
|
+
.catch((e) => store.pushNotice(`mcp toggle failed: ${e?.message || e}`, 'error'));
|
|
71
|
+
};
|
|
72
|
+
setPicker({
|
|
73
|
+
title: 'MCP servers',
|
|
74
|
+
description: 'Enable or disable configured MCP servers.',
|
|
75
|
+
initialIndex: Math.max(0, items.findIndex((entry) => entry.value === options?.highlightValue)),
|
|
76
|
+
items,
|
|
77
|
+
onSelect: (_value, item) => toggleServer(item),
|
|
78
|
+
onLeft: (item) => toggleServer(item),
|
|
79
|
+
onRight: (item) => toggleServer(item),
|
|
80
|
+
onCancel: () => {
|
|
81
|
+
setPicker(null);
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const openMcpPicker = () => {
|
|
87
|
+
openMcpServersPicker();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const skillsStatus = () => {
|
|
91
|
+
let status;
|
|
92
|
+
try {
|
|
93
|
+
status = store.skillsStatus?.() || { skills: [] };
|
|
94
|
+
} catch (e) {
|
|
95
|
+
store.pushNotice(`skills status failed: ${e?.message || e}`, 'error');
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return { ...status, skills: status.skills || [] };
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const openProjectSkillsPicker = () => {
|
|
102
|
+
const status = skillsStatus();
|
|
103
|
+
if (!status) return;
|
|
104
|
+
const skills = status.skills || [];
|
|
105
|
+
const items = [];
|
|
106
|
+
if (skills.length === 0) {
|
|
107
|
+
items.push({
|
|
108
|
+
value: 'empty',
|
|
109
|
+
label: 'No project skills',
|
|
110
|
+
description: 'no project skills available',
|
|
111
|
+
_action: 'noop',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
for (const skill of skills) {
|
|
115
|
+
items.push({
|
|
116
|
+
value: skill.name,
|
|
117
|
+
label: skill.name,
|
|
118
|
+
description: `${skill.source || 'skill'} · ${skill.description || skill.filePath || ''}`,
|
|
119
|
+
_action: 'view',
|
|
120
|
+
_skill: skill,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
setProviderPrompt(null);
|
|
124
|
+
setChannelPrompt(null);
|
|
125
|
+
setHookPrompt(null);
|
|
126
|
+
setSettingsPrompt(null);
|
|
127
|
+
setPicker({
|
|
128
|
+
title: 'Project skills',
|
|
129
|
+
description: 'Skills bundled with this project.',
|
|
130
|
+
items,
|
|
131
|
+
onSelect: (_value, item) => {
|
|
132
|
+
setPicker(null);
|
|
133
|
+
if (item._action !== 'view') return;
|
|
134
|
+
openSkillDetailPicker(item._skill);
|
|
135
|
+
},
|
|
136
|
+
onCancel: () => {
|
|
137
|
+
setPicker(null);
|
|
138
|
+
void openSkillsPicker();
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const openSkillsPicker = (options = {}) => {
|
|
144
|
+
const status = skillsStatus();
|
|
145
|
+
if (!status) return;
|
|
146
|
+
const skills = status.skills || [];
|
|
147
|
+
const disabledSet = options.disabledOverride instanceof Set ? options.disabledOverride : getDisabledSkills();
|
|
148
|
+
const items = [];
|
|
149
|
+
if (skills.length === 0) {
|
|
150
|
+
items.push({
|
|
151
|
+
value: 'empty',
|
|
152
|
+
label: 'No skills',
|
|
153
|
+
description: 'no project skills available',
|
|
154
|
+
_action: 'noop',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
for (const skill of skills) {
|
|
158
|
+
const enabled = !disabledSet.has(skill.name);
|
|
159
|
+
items.push({
|
|
160
|
+
value: skill.name,
|
|
161
|
+
label: skill.name,
|
|
162
|
+
marker: enabled ? '●' : '○',
|
|
163
|
+
markerColor: enabled ? theme.success : theme.inactive,
|
|
164
|
+
description: `${skill.source || 'skill'} · ${skill.description || skill.filePath || ''}`,
|
|
165
|
+
_action: 'skill',
|
|
166
|
+
_skill: skill,
|
|
167
|
+
_enabled: enabled,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
setProviderPrompt(null);
|
|
171
|
+
setChannelPrompt(null);
|
|
172
|
+
setHookPrompt(null);
|
|
173
|
+
setSettingsPrompt(null);
|
|
174
|
+
const toggleSkill = (item) => {
|
|
175
|
+
if (item._action !== 'skill' || !item._skill?.name) return;
|
|
176
|
+
const name = item._skill.name;
|
|
177
|
+
const next = new Set(disabledSet);
|
|
178
|
+
if (item._enabled) next.add(name); else next.delete(name);
|
|
179
|
+
setDisabledSkills(next);
|
|
180
|
+
store.pushNotice(`skill ${item._enabled ? 'disabled' : 'enabled'}: ${name}`, 'info');
|
|
181
|
+
openSkillsPicker({ highlightValue: name, disabledOverride: next });
|
|
182
|
+
};
|
|
183
|
+
setPicker({
|
|
184
|
+
title: 'Skills',
|
|
185
|
+
description: 'Enable or disable project skills.',
|
|
186
|
+
initialIndex: Math.max(0, items.findIndex((entry) => entry.value === options.highlightValue)),
|
|
187
|
+
items,
|
|
188
|
+
onSelect: (_value, item) => toggleSkill(item),
|
|
189
|
+
onLeft: (item) => toggleSkill(item),
|
|
190
|
+
onRight: (item) => toggleSkill(item),
|
|
191
|
+
onCancel: () => {
|
|
192
|
+
setPicker(null);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const openSkillDetailPicker = (skill) => {
|
|
198
|
+
const disabled = getDisabledSkills().has(skill.name);
|
|
199
|
+
setPicker({
|
|
200
|
+
title: `Skill · ${skill.name}`,
|
|
201
|
+
description: clean(skill.description) || 'Enable, disable, or run this skill.',
|
|
202
|
+
items: [
|
|
203
|
+
{
|
|
204
|
+
value: 'use',
|
|
205
|
+
label: 'Use skill',
|
|
206
|
+
description: disabled ? 'enable this skill first' : 'write a request with this skill',
|
|
207
|
+
_action: disabled ? 'noop' : 'use',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
value: disabled ? 'enable' : 'disable',
|
|
211
|
+
label: disabled ? 'Enable skill' : 'Disable skill',
|
|
212
|
+
description: disabled ? 'show and allow this skill in the TUI' : 'hide use action until re-enabled',
|
|
213
|
+
_action: disabled ? 'enable' : 'disable',
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
onSelect: (_value, item) => {
|
|
217
|
+
setPicker(null);
|
|
218
|
+
if (item._action === 'enable') {
|
|
219
|
+
setDisabledSkills((current) => {
|
|
220
|
+
const next = new Set(current);
|
|
221
|
+
next.delete(skill.name);
|
|
222
|
+
return next;
|
|
223
|
+
});
|
|
224
|
+
store.pushNotice(`skill enabled: ${skill.name}`, 'info');
|
|
225
|
+
openSkillsPicker();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (item._action === 'disable') {
|
|
229
|
+
setDisabledSkills((current) => {
|
|
230
|
+
const next = new Set(current);
|
|
231
|
+
next.add(skill.name);
|
|
232
|
+
return next;
|
|
233
|
+
});
|
|
234
|
+
store.pushNotice(`skill disabled: ${skill.name}`, 'info');
|
|
235
|
+
openSkillsPicker();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (item._action === 'use') {
|
|
239
|
+
setSettingsPrompt({
|
|
240
|
+
kind: 'skill-use',
|
|
241
|
+
label: `Skill · ${skill.name}`,
|
|
242
|
+
hint: 'Write the request to run with this skill.',
|
|
243
|
+
skillName: skill.name,
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
onCancel: () => {
|
|
249
|
+
setPicker(null);
|
|
250
|
+
void openSkillsPicker();
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const pluginStatus = () => {
|
|
256
|
+
let status;
|
|
257
|
+
try {
|
|
258
|
+
status = store.pluginsStatus?.() || { plugins: [] };
|
|
259
|
+
} catch (e) {
|
|
260
|
+
store.pushNotice(`plugins status failed: ${e?.message || e}`, 'error');
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
return { ...status, plugins: status.plugins || [] };
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const beginAddPlugin = () => {
|
|
267
|
+
setPicker(null);
|
|
268
|
+
setSettingsPrompt({ kind: 'plugin-add', label: 'Plugin URL', hint: 'Git URL, owner/repo, or local path' });
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const openPluginDetailPicker = (p) => {
|
|
272
|
+
setPicker({
|
|
273
|
+
title: p.title || p.name,
|
|
274
|
+
description: clean(p.description) || 'Update, MCP, or uninstall this plugin.',
|
|
275
|
+
items: [
|
|
276
|
+
{
|
|
277
|
+
value: 'info',
|
|
278
|
+
label: 'Plugin info',
|
|
279
|
+
description: `${p.sourceType || p.source}${p.version ? ` · ${p.version}` : ''} · skills ${p.skillCount || 0}`,
|
|
280
|
+
_action: 'info',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
value: 'update',
|
|
284
|
+
label: p.sourceType === 'local' ? 'Refresh metadata' : 'Update plugin',
|
|
285
|
+
description: p.sourceType === 'local' ? 'rescan local plugin manifest' : 'pull latest from source URL',
|
|
286
|
+
_action: 'update',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
value: 'enable-mcp',
|
|
290
|
+
label: p.mcpScript ? (p.mcpEnabled ? 'Refresh MCP server' : 'Enable MCP server') : 'No MCP script',
|
|
291
|
+
description: p.mcpScript ? `${p.mcpServerName || 'plugin-mcp'} · ${p.mcpEnabled ? 'configured' : p.mcpScript}` : 'plugin does not expose scripts/run-mcp.mjs or mcp/server.mjs',
|
|
292
|
+
_action: p.mcpScript ? 'enable-mcp' : 'noop',
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
value: 'copy-root',
|
|
296
|
+
label: 'Copy root path',
|
|
297
|
+
description: p.root,
|
|
298
|
+
_action: 'copy-root',
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
value: 'copy-mcp-name',
|
|
302
|
+
label: p.mcpScript ? 'Copy MCP server name' : 'No MCP server name',
|
|
303
|
+
description: p.mcpServerName || '',
|
|
304
|
+
_action: p.mcpScript ? 'copy-mcp-name' : 'noop',
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
value: 'uninstall',
|
|
308
|
+
label: 'Uninstall plugin',
|
|
309
|
+
description: p.managed === false ? 'remove from registry only' : 'remove registry entry and installed files',
|
|
310
|
+
_action: 'uninstall',
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
onSelect: (_detailValue, detail) => {
|
|
314
|
+
setPicker(null);
|
|
315
|
+
if (detail._action === 'info') {
|
|
316
|
+
store.pushNotice([
|
|
317
|
+
`${p.title || p.name}${p.version ? ` ${p.version}` : ''}`,
|
|
318
|
+
`source: ${p.sourceType || p.source}${p.sourceUrl ? ` / ${p.sourceUrl}` : ''}`,
|
|
319
|
+
`skills: ${p.skillCount || 0}`,
|
|
320
|
+
`mcp: ${p.mcpScript ? `${p.mcpEnabled ? 'enabled' : 'available'} (${p.mcpServerName || 'plugin-mcp'})` : '(none)'}`,
|
|
321
|
+
`root: ${p.root}`,
|
|
322
|
+
p.description ? `\n${p.description}` : '',
|
|
323
|
+
].filter(Boolean).join('\n'), 'info');
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (detail._action === 'update') {
|
|
327
|
+
void store.updatePlugin?.(p)
|
|
328
|
+
.then(() => openInstalledPluginsPicker())
|
|
329
|
+
.catch((e) => store.pushNotice(`plugin update failed: ${e?.message || e}`, 'error'));
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (detail._action === 'enable-mcp') {
|
|
333
|
+
void store.enablePluginMcp?.(p)
|
|
334
|
+
.then(() => openMcpPicker())
|
|
335
|
+
.catch((e) => store.pushNotice(`plugin MCP enable failed: ${e?.message || e}`, 'error'));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (detail._action === 'copy-root') {
|
|
339
|
+
void copyToClipboard(p.root)
|
|
340
|
+
.then(() => store.pushNotice(`copied plugin root: ${p.name}`, 'plain'))
|
|
341
|
+
.catch((e) => store.pushNotice(`copy failed: ${e?.message || e}`, 'error'));
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (detail._action === 'copy-mcp-name') {
|
|
345
|
+
void copyToClipboard(p.mcpServerName || '')
|
|
346
|
+
.then(() => store.pushNotice(`copied plugin MCP server: ${p.mcpServerName}`, 'plain'))
|
|
347
|
+
.catch((e) => store.pushNotice(`copy failed: ${e?.message || e}`, 'error'));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (detail._action === 'uninstall') {
|
|
351
|
+
void store.removePlugin?.(p)
|
|
352
|
+
.then(() => openInstalledPluginsPicker())
|
|
353
|
+
.catch((e) => store.pushNotice(`plugin uninstall failed: ${e?.message || e}`, 'error'));
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
onCancel: () => {
|
|
357
|
+
setPicker(null);
|
|
358
|
+
void openInstalledPluginsPicker();
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const openInstalledPluginsPicker = () => {
|
|
364
|
+
const status = pluginStatus();
|
|
365
|
+
if (!status) return;
|
|
366
|
+
const plugins = status.plugins || [];
|
|
367
|
+
const items = [];
|
|
368
|
+
if (plugins.length === 0) {
|
|
369
|
+
items.push({
|
|
370
|
+
value: 'empty',
|
|
371
|
+
label: 'No installed plugins',
|
|
372
|
+
description: 'Esc back · add from Plugins > Add plugin',
|
|
373
|
+
_action: 'noop',
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
for (const plugin of plugins) {
|
|
377
|
+
items.push({
|
|
378
|
+
value: `${plugin.id || plugin.name}:${plugin.version || ''}`,
|
|
379
|
+
label: plugin.title || plugin.name,
|
|
380
|
+
description: `${plugin.sourceType || plugin.source}${plugin.version ? ` · ${plugin.version}` : ''} · skills ${plugin.skillCount || 0}${plugin.mcpScript ? ` · mcp ${plugin.mcpEnabled ? 'enabled' : plugin.mcpScript}` : ''}`,
|
|
381
|
+
_action: 'plugin',
|
|
382
|
+
_plugin: plugin,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
setProviderPrompt(null);
|
|
386
|
+
setChannelPrompt(null);
|
|
387
|
+
setHookPrompt(null);
|
|
388
|
+
setSettingsPrompt(null);
|
|
389
|
+
setPicker({
|
|
390
|
+
title: 'Installed plugins',
|
|
391
|
+
description: 'Open an installed plugin to manage it.',
|
|
392
|
+
items,
|
|
393
|
+
onSelect: (_value, item) => {
|
|
394
|
+
setPicker(null);
|
|
395
|
+
if (item._action !== 'plugin') return;
|
|
396
|
+
openPluginDetailPicker(item._plugin);
|
|
397
|
+
},
|
|
398
|
+
onCancel: () => {
|
|
399
|
+
setPicker(null);
|
|
400
|
+
void openPluginsPicker();
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const openPluginsPicker = () => {
|
|
406
|
+
const status = pluginStatus();
|
|
407
|
+
if (!status) return;
|
|
408
|
+
setProviderPrompt(null);
|
|
409
|
+
setChannelPrompt(null);
|
|
410
|
+
setHookPrompt(null);
|
|
411
|
+
setSettingsPrompt(null);
|
|
412
|
+
setPicker({
|
|
413
|
+
title: 'Plugins',
|
|
414
|
+
description: 'Add or manage local plugin integrations.',
|
|
415
|
+
items: [
|
|
416
|
+
{
|
|
417
|
+
value: 'installed',
|
|
418
|
+
label: 'Installed plugins',
|
|
419
|
+
description: `${status.count || 0} installed`,
|
|
420
|
+
_action: 'installed',
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
value: 'add',
|
|
424
|
+
label: 'Add plugin',
|
|
425
|
+
description: 'Git URL, owner/repo, or local path',
|
|
426
|
+
_action: 'add',
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
onSelect: (_value, item) => {
|
|
430
|
+
setPicker(null);
|
|
431
|
+
if (item._action === 'installed') {
|
|
432
|
+
openInstalledPluginsPicker();
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (item._action === 'add') beginAddPlugin();
|
|
436
|
+
},
|
|
437
|
+
onCancel: () => {
|
|
438
|
+
setPicker(null);
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const openHooksPicker = () => {
|
|
444
|
+
let status;
|
|
445
|
+
try {
|
|
446
|
+
status = store.hooksStatus?.() || { events: [], recent: [] };
|
|
447
|
+
} catch (e) {
|
|
448
|
+
store.pushNotice(`hooks status failed: ${e?.message || e}`, 'error');
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const rules = status.rules || [];
|
|
452
|
+
const items = [
|
|
453
|
+
...(rules.length ? rules.map((rule) => ({
|
|
454
|
+
value: `rule:${rule.index}`,
|
|
455
|
+
label: `${rule.tool} -> ${rule.action}`,
|
|
456
|
+
marker: rule.enabled ? '●' : '○',
|
|
457
|
+
markerColor: rule.enabled ? theme.success : theme.inactive,
|
|
458
|
+
description: `${rule.match ? `match ${rule.match} · ` : ''}${rule.reason || 'Enter toggle'}`,
|
|
459
|
+
_action: 'rule',
|
|
460
|
+
_rule: rule,
|
|
461
|
+
})) : [{
|
|
462
|
+
value: 'rules:none',
|
|
463
|
+
label: 'No rules',
|
|
464
|
+
description: status.rulesPath || 'hooks.json not configured',
|
|
465
|
+
_action: 'noop',
|
|
466
|
+
}]),
|
|
467
|
+
];
|
|
468
|
+
setProviderPrompt(null);
|
|
469
|
+
setChannelPrompt(null);
|
|
470
|
+
setHookPrompt(null);
|
|
471
|
+
setSettingsPrompt(null);
|
|
472
|
+
const toggleRule = (item) => {
|
|
473
|
+
if (item._action !== 'rule') return;
|
|
474
|
+
try {
|
|
475
|
+
store.setHookRuleEnabled?.(item._rule.index, !item._rule.enabled);
|
|
476
|
+
void openHooksPicker();
|
|
477
|
+
} catch (e) {
|
|
478
|
+
store.pushNotice(`hook toggle failed: ${e?.message || e}`, 'error');
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
setPicker({
|
|
482
|
+
title: 'Hooks',
|
|
483
|
+
description: 'Before-tool hook rules; Enter toggles a rule.',
|
|
484
|
+
items,
|
|
485
|
+
onSelect: (_value, item) => toggleRule(item),
|
|
486
|
+
onLeft: (item) => toggleRule(item),
|
|
487
|
+
onRight: (item) => toggleRule(item),
|
|
488
|
+
onCancel: () => {
|
|
489
|
+
setPicker(null);
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
return {
|
|
495
|
+
openMcpServersPicker,
|
|
496
|
+
openMcpPicker,
|
|
497
|
+
openProjectSkillsPicker,
|
|
498
|
+
openSkillsPicker,
|
|
499
|
+
openSkillDetailPicker,
|
|
500
|
+
beginAddPlugin,
|
|
501
|
+
openPluginDetailPicker,
|
|
502
|
+
openInstalledPluginsPicker,
|
|
503
|
+
openPluginsPicker,
|
|
504
|
+
openHooksPicker,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* input-parsers.mjs — pure text-to-structure parsers for slash-command inputs
|
|
3
|
+
* (hook rules, MCP servers, skills, memory commands + memory row tables).
|
|
4
|
+
* Extracted verbatim from App.jsx; no hooks, no App state, no closures.
|
|
5
|
+
*/
|
|
6
|
+
export function parseHookRuleInput(text) {
|
|
7
|
+
const parts = String(text || '').split('|').map((part) => part.trim());
|
|
8
|
+
const [tool, actionRaw, match, reason, patchText] = parts;
|
|
9
|
+
const action = String(actionRaw || '').toLowerCase();
|
|
10
|
+
if (!tool || !action) return { error: 'usage: tool | allow|deny|modify | match(optional) | reason(optional) | json patch(optional)' };
|
|
11
|
+
if (!['allow', 'deny', 'block', 'modify', 'rewrite'].includes(action)) {
|
|
12
|
+
return { error: 'hook action must be allow, deny, block, modify, or rewrite' };
|
|
13
|
+
}
|
|
14
|
+
const rule = { tool, action };
|
|
15
|
+
if (match) rule.match = match;
|
|
16
|
+
if (reason) rule.reason = reason;
|
|
17
|
+
if (patchText) {
|
|
18
|
+
try {
|
|
19
|
+
const patch = JSON.parse(patchText);
|
|
20
|
+
if (!patch || typeof patch !== 'object' || Array.isArray(patch)) return { error: 'json patch must be an object' };
|
|
21
|
+
rule.patch = patch;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return { error: `invalid json patch: ${e?.message || e}` };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if ((action === 'modify' || action === 'rewrite') && !rule.patch) {
|
|
27
|
+
return { error: 'modify/rewrite needs a json patch object in the last field' };
|
|
28
|
+
}
|
|
29
|
+
return { rule };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function parseMcpServerInput(text) {
|
|
33
|
+
const parts = String(text || '').split('|').map((part) => part.trim());
|
|
34
|
+
const [name, commandOrUrl, argsText = '', cwd = ''] = parts;
|
|
35
|
+
if (!name || !commandOrUrl) return { error: 'usage: name | command-or-url | args(optional) | cwd(optional)' };
|
|
36
|
+
if (/^(?:https?|wss?):\/\//i.test(commandOrUrl)) return { server: { name, url: commandOrUrl } };
|
|
37
|
+
return {
|
|
38
|
+
server: {
|
|
39
|
+
name,
|
|
40
|
+
command: commandOrUrl,
|
|
41
|
+
args: argsText.split(/\s+/).filter(Boolean),
|
|
42
|
+
cwd,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function parseSkillInput(text) {
|
|
48
|
+
const parts = String(text || '').split('|').map((part) => part.trim());
|
|
49
|
+
const [name, description = 'Project skill.'] = parts;
|
|
50
|
+
if (!name) return { error: 'usage: name | description(optional)' };
|
|
51
|
+
return { skill: { name, description } };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function parseMemoryCommand(text) {
|
|
55
|
+
const parts = String(text || '').trim().split(/\s+/).filter(Boolean);
|
|
56
|
+
const action = parts[0] || 'status';
|
|
57
|
+
const out = { action };
|
|
58
|
+
for (const part of parts.slice(1)) {
|
|
59
|
+
const [key, ...rest] = part.split('=');
|
|
60
|
+
if (!key || rest.length === 0) continue;
|
|
61
|
+
const raw = rest.join('=');
|
|
62
|
+
const num = Number(raw);
|
|
63
|
+
out[key] = Number.isFinite(num) && raw.trim() !== '' ? num : raw;
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function parseMemoryStatusRows(text) {
|
|
69
|
+
return String(text || '')
|
|
70
|
+
.split('\n')
|
|
71
|
+
.map((line) => line.trim())
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.map((line, index) => {
|
|
74
|
+
const sep = line.indexOf(':');
|
|
75
|
+
const label = sep === -1 ? line : line.slice(0, sep);
|
|
76
|
+
const description = sep === -1 ? '' : line.slice(sep + 1).trim();
|
|
77
|
+
return {
|
|
78
|
+
value: `status-${index}`,
|
|
79
|
+
label,
|
|
80
|
+
description,
|
|
81
|
+
_line: line,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function parseMemoryCoreRows(text) {
|
|
87
|
+
let currentProjectId = null;
|
|
88
|
+
return String(text || '')
|
|
89
|
+
.split('\n')
|
|
90
|
+
.filter((line) => line.trim())
|
|
91
|
+
.map((line, index) => {
|
|
92
|
+
const raw = line.trim();
|
|
93
|
+
if (raw.endsWith(':') && !raw.includes('id=')) {
|
|
94
|
+
const label = raw.slice(0, -1);
|
|
95
|
+
currentProjectId = label === 'COMMON' ? null : label;
|
|
96
|
+
// Group headers (COMMON / project slug) are scope markers, not
|
|
97
|
+
// selectable rows — drop them and carry the scope on each entry's
|
|
98
|
+
// meta column instead (blank for common).
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const match = raw.match(/^id=(\d+)\s+\[([^\]]*)\]\s+(.+?)(?:\s+—\s+(.+))?$/);
|
|
102
|
+
if (match) {
|
|
103
|
+
const [, id, category, element, summary = ''] = match;
|
|
104
|
+
return {
|
|
105
|
+
value: `core-${id}`,
|
|
106
|
+
// Display is summary-first: session injection only ever uses the
|
|
107
|
+
// summary sentence (buildSessionCoreMemoryPayload), so id/category/
|
|
108
|
+
// element are UI noise. They stay in the hidden _fields for
|
|
109
|
+
// edit/delete plumbing.
|
|
110
|
+
label: `#${id}`,
|
|
111
|
+
meta: currentProjectId || '',
|
|
112
|
+
description: summary || element,
|
|
113
|
+
_line: raw,
|
|
114
|
+
_action: 'core-entry',
|
|
115
|
+
_id: Number(id),
|
|
116
|
+
_category: category,
|
|
117
|
+
_element: element,
|
|
118
|
+
_summary: summary || element,
|
|
119
|
+
_projectId: currentProjectId,
|
|
120
|
+
// Raw pre-edit values, distinct from the (possibly later-mutated)
|
|
121
|
+
// _element/_summary above -- beginEditCoreMemory reads these to
|
|
122
|
+
// decide whether the row's element duplicated its summary (single-
|
|
123
|
+
// sentence entry) before deciding whether to touch element on edit.
|
|
124
|
+
_origElement: element,
|
|
125
|
+
_origSummary: summary,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
value: `core-${index}`,
|
|
130
|
+
label: raw,
|
|
131
|
+
description: '',
|
|
132
|
+
_line: raw,
|
|
133
|
+
};
|
|
134
|
+
})
|
|
135
|
+
.filter(Boolean);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function parseMemoryCandidateRows(text) {
|
|
139
|
+
const trimmed = String(text || '').trim();
|
|
140
|
+
// op:'candidates' returns this exact sentinel (index.mjs ~3158) when the
|
|
141
|
+
// resolved scope has none — treat as an empty list, not an inert text row.
|
|
142
|
+
if (!trimmed || /^core candidates:\s*none$/i.test(trimmed)) return [];
|
|
143
|
+
// Backend row shape (index.mjs ~3164), one candidate per line, no group
|
|
144
|
+
// headers:
|
|
145
|
+
// id=<n> project=<COMMON|slug> [<category>] score=<x.xx|-> <element> — <summary> (<reason>)
|
|
146
|
+
const rowPattern = /^id=(\d+)\s+project=(\S+)\s+\[([^\]]*)\]\s+score=(\S+)\s+(.+?)\s+—\s+(.+?)\s+\(([^)]*)\)$/;
|
|
147
|
+
return trimmed
|
|
148
|
+
.split('\n')
|
|
149
|
+
.filter((line) => line.trim())
|
|
150
|
+
.map((line, index) => {
|
|
151
|
+
const raw = line.trim();
|
|
152
|
+
const match = raw.match(rowPattern);
|
|
153
|
+
if (match) {
|
|
154
|
+
const [, id, project, category, score, element, summary, reason] = match;
|
|
155
|
+
return {
|
|
156
|
+
value: `candidate-${id}`,
|
|
157
|
+
label: `#${id} [${category}] ${element}`,
|
|
158
|
+
meta: project === 'COMMON' ? 'common' : project,
|
|
159
|
+
description: `${summary}${score !== '-' ? ` (score ${score})` : ''} — ${reason}`,
|
|
160
|
+
_line: raw,
|
|
161
|
+
_action: 'candidate-entry',
|
|
162
|
+
_id: Number(id),
|
|
163
|
+
_projectId: project === 'COMMON' ? null : project,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
value: `candidate-${index}`,
|
|
168
|
+
label: raw,
|
|
169
|
+
description: '',
|
|
170
|
+
_line: raw,
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Backend "core" op errors are flattened to plain text by store.memoryControl
|
|
176
|
+
// (isError is dropped -- see engine.mjs memoryControl / toolResponseText in
|
|
177
|
+
// mixdog-session-runtime.mjs). Success text always uses a past-tense verb
|
|
178
|
+
// ("core added/edited/deleted/promoted...", "core candidate dismissed...");
|
|
179
|
+
// every declared failure in index.mjs's core-op handling uses the op word
|
|
180
|
+
// followed by either ":" (validation message) or " failed" (caught
|
|
181
|
+
// exception) -- e.g. "core add: project_id required...", "core edit failed:
|
|
182
|
+
// no entry with id=5". That shape is used here to recover the error signal
|
|
183
|
+
// since store.memoryControl resolves instead of rejecting on isError.
|
|
184
|
+
export function memoryCoreResultErrorText(text) {
|
|
185
|
+
const value = String(text || '').trim();
|
|
186
|
+
if (/^core (add|edit|delete|promote|dismiss)(:| failed)/i.test(value)) return value;
|
|
187
|
+
// Broader catch-all for other flattened "core" failures that don't name an
|
|
188
|
+
// op word right after "core" (e.g. "core: memory data dir is not
|
|
189
|
+
// initialized", "core requires op: ..."), plus any bare error/failed lead-in.
|
|
190
|
+
if (/^core:.*(not initialized|failed|error)/i.test(value)) return value;
|
|
191
|
+
if (/^(error|failed)\b/i.test(value)) return value;
|
|
192
|
+
return null;
|
|
193
|
+
}
|