mixdog 0.9.3 → 0.9.4
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/package.json +7 -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/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-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 +215 -29
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- 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-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +23 -63
- 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 +6 -5
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/worker/AGENT.md +8 -4
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +632 -2042
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +0 -11
- 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 +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -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 +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -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/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 +182 -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 +251 -397
- 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/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 +32 -41
- 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 +12 -292
- package/src/runtime/channels/index.mjs +229 -663
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- 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/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 +312 -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 +36 -570
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +201 -1948
- 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/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 +22 -2
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +780 -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/launcher-control.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 +84 -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 +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +319 -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/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 +110 -671
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +30 -9
- 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 +945 -8094
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -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 +324 -0
- package/src/tui/app/model-options.mjs +330 -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 +490 -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 +671 -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 +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +23 -101
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- 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 +19652 -18630
- 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 +122 -562
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- 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 +67 -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 +1 -1
- package/src/workflows/sequential/WORKFLOW.md +1 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- 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/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
|
@@ -39,6 +39,7 @@ import { normalizeErrorMessage } from './path-diagnostics.mjs';
|
|
|
39
39
|
import { invalidateBuiltinResultCache } from './cache-layers.mjs';
|
|
40
40
|
import { resolveOptionalCwd } from './cwd-utils.mjs';
|
|
41
41
|
import { scrubLoaderVars, scrubProviderSecrets } from '../env-scrub.mjs';
|
|
42
|
+
import { resolveSessionCwd, stateFilePath, wrapPowerShellWithCwdProbe, wrapBashWithCwdProbe } from '../shell-state.mjs';
|
|
42
43
|
|
|
43
44
|
// Post-exec drift detection. After a foreground shell command, compare the
|
|
44
45
|
// live mtime+size of files mixdog has already read this session against their
|
|
@@ -113,7 +114,12 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
113
114
|
const requestedCwd = args.cwd ?? args.workdir;
|
|
114
115
|
const cwdResult = resolveOptionalCwd(requestedCwd, workDir);
|
|
115
116
|
if (cwdResult.error) return cwdResult.error;
|
|
116
|
-
|
|
117
|
+
// Session cwd carry-over (Claude Code style, no live shell): when the model
|
|
118
|
+
// passes an explicit cwd it wins and updates the store on the next probe;
|
|
119
|
+
// otherwise reuse the last stored cwd for this session if it still exists.
|
|
120
|
+
const _hasExplicitCwd = typeof requestedCwd === 'string' && requestedCwd.trim() !== '';
|
|
121
|
+
const _sessionCwdKey = options?.sessionId ?? options?.readStateScope ?? options?.callerSessionId ?? null;
|
|
122
|
+
const bashWorkDir = resolveSessionCwd(_sessionCwdKey, _hasExplicitCwd ? cwdResult.cwd : null, cwdResult.cwd);
|
|
117
123
|
const _readStateScope = options?.readStateScope ?? options?.sessionId ?? null;
|
|
118
124
|
const executionMode = resolveExecutionMode(args || {}, args?.run_in_background === true ? 'async' : 'sync');
|
|
119
125
|
const runInBackground = executionMode === 'async';
|
|
@@ -318,8 +324,22 @@ export async function executeBashTool(args, workDir, options = {}) {
|
|
|
318
324
|
try { bashAbortSignal = (await getAbortSignalForSession(options?.sessionId)) || null; }
|
|
319
325
|
catch { bashAbortSignal = null; }
|
|
320
326
|
const combinedBashAbort = _combineAbortSignals(bashAbortSignal, options?.abortSignal || null);
|
|
327
|
+
// Sync path only: chain a trailing cwd probe so the session's final
|
|
328
|
+
// working directory persists to the next shell call. Async jobs run
|
|
329
|
+
// detached and are intentionally excluded (they never reach here). The
|
|
330
|
+
// probe captures the command's exit status first and re-exits with it,
|
|
331
|
+
// so the exit code the model sees is unchanged.
|
|
332
|
+
let syncCommand = wrappedCommand;
|
|
333
|
+
try {
|
|
334
|
+
const _stateFile = stateFilePath(_sessionCwdKey);
|
|
335
|
+
if (_stateFile) {
|
|
336
|
+
syncCommand = (process.platform === 'win32' && shellType === 'powershell')
|
|
337
|
+
? wrapPowerShellWithCwdProbe(wrappedCommand, _stateFile)
|
|
338
|
+
: wrapBashWithCwdProbe(wrappedCommand, _stateFile);
|
|
339
|
+
}
|
|
340
|
+
} catch { syncCommand = wrappedCommand; }
|
|
321
341
|
const result = await execShellCommand({
|
|
322
|
-
shell, shellArg, shellArgs, command:
|
|
342
|
+
shell, shellArg, shellArgs, command: syncCommand,
|
|
323
343
|
env: spawnEnv,
|
|
324
344
|
cwd: bashWorkDir,
|
|
325
345
|
timeoutMs: timeout,
|
|
@@ -42,36 +42,13 @@ export const BUILTIN_TOOLS = [
|
|
|
42
42
|
minItems: 1,
|
|
43
43
|
},
|
|
44
44
|
],
|
|
45
|
-
description: 'File path
|
|
45
|
+
description: 'File path. To batch spans: path[] or {path,offset,limit}[] region objects. Pass arrays directly; JSON strings are legacy recovery only.',
|
|
46
46
|
},
|
|
47
47
|
offset: { type: 'number', minimum: 0, description: 'Numeric lines to skip before reading; 0 starts at line 1. Continue with offset:N.' },
|
|
48
48
|
limit: { type: 'number', minimum: 1, description: 'Numeric max lines to return after offset.' },
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
|
-
{
|
|
53
|
-
name: 'diagnostics',
|
|
54
|
-
title: 'Mixdog Diagnostics',
|
|
55
|
-
annotations: { title: 'Mixdog Diagnostics', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true, compressible: true },
|
|
56
|
-
description: 'Run matching type/lint checker. Default cwd; no LSP.',
|
|
57
|
-
inputSchema: {
|
|
58
|
-
type: 'object',
|
|
59
|
-
properties: {
|
|
60
|
-
path: { type: 'string', description: 'File/dir. Default cwd.' },
|
|
61
|
-
},
|
|
62
|
-
required: [],
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'open_config',
|
|
67
|
-
title: 'Open Config UI',
|
|
68
|
-
// agentHidden: a worker/reviewer session has no business popping the
|
|
69
|
-
// settings UI on the user's machine; it also wastes schema bytes on
|
|
70
|
-
// every agent request. Lead keeps it.
|
|
71
|
-
annotations: { title: 'Open Config UI', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false, agentHidden: true },
|
|
72
|
-
description: 'Open settings UI; returns URL. No params.',
|
|
73
|
-
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
74
|
-
},
|
|
75
52
|
{
|
|
76
53
|
name: 'shell',
|
|
77
54
|
title: 'Mixdog Shell',
|
|
@@ -81,16 +58,10 @@ export const BUILTIN_TOOLS = [
|
|
|
81
58
|
type: 'object',
|
|
82
59
|
properties: {
|
|
83
60
|
command: { type: 'string', description: 'Command.' },
|
|
84
|
-
cwd: { type: 'string', description: 'Working directory.' },
|
|
85
|
-
workdir: { type: 'string', description: 'Alias for cwd; omit if empty.' },
|
|
61
|
+
cwd: { type: 'string', description: 'Working directory. Persists across shell calls within a session — omit to reuse the previous command\'s directory (e.g. after cd); pass an absolute path to change it.' },
|
|
86
62
|
timeout: { type: 'number', description: 'Timeout ms.' },
|
|
87
63
|
merge_stderr: { type: 'boolean', description: 'Merge stderr.' },
|
|
88
64
|
mode: { type: 'string', enum: ['sync', 'async'], description: executionModeSchemaDescription('sync') },
|
|
89
|
-
run_in_background: { type: 'boolean', description: `Legacy alias for mode=async. ${TOOL_ASYNC_EXECUTION_CONTRACT}` },
|
|
90
|
-
persistent: { type: 'boolean', description: 'Persistent shell mode.' },
|
|
91
|
-
session_id: { type: 'string', description: 'Persistent shell id.' },
|
|
92
|
-
create: { type: 'boolean', description: 'Create session_id if missing.' },
|
|
93
|
-
close: { type: 'boolean', description: 'Close persistent session.' },
|
|
94
65
|
shell: { type: 'string', enum: ['bash', 'powershell'], description: 'Force shell. Windows default is PowerShell; bash means Git Bash/POSIX.' },
|
|
95
66
|
},
|
|
96
67
|
required: ['command'],
|
|
@@ -116,7 +87,7 @@ export const BUILTIN_TOOLS = [
|
|
|
116
87
|
name: 'grep',
|
|
117
88
|
title: 'Mixdog Grep',
|
|
118
89
|
annotations: { title: 'Mixdog Grep', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, compressible: true },
|
|
119
|
-
description: 'Search file contents by text/regex in a known scope. Use files_with_matches/count for broad anchors, content_with_context for narrow code answers. One concept → one grep.',
|
|
90
|
+
description: 'Search file contents by text/regex in a known scope. Use files_with_matches/count for broad anchors, content_with_context for narrow code answers. One concept → one grep; multiple scopes = ONE call with path[].',
|
|
120
91
|
inputSchema: {
|
|
121
92
|
type: 'object',
|
|
122
93
|
properties: {
|
|
@@ -127,7 +98,13 @@ export const BUILTIN_TOOLS = [
|
|
|
127
98
|
],
|
|
128
99
|
description: 'Text/regex. Put synonyms in pattern[] as OR in ONE grep; no serial rewording or equivalent repeats.',
|
|
129
100
|
},
|
|
130
|
-
path: {
|
|
101
|
+
path: {
|
|
102
|
+
anyOf: [
|
|
103
|
+
{ type: 'string' },
|
|
104
|
+
{ type: 'array', items: { type: 'string' }, minItems: 1 },
|
|
105
|
+
],
|
|
106
|
+
description: 'Known narrowest file/dir, or path[] to search several scopes in one call; broad scopes return paths first, then refine from returned paths.',
|
|
107
|
+
},
|
|
131
108
|
glob: {
|
|
132
109
|
anyOf: [
|
|
133
110
|
{ type: 'string' },
|
|
@@ -136,9 +113,11 @@ export const BUILTIN_TOOLS = [
|
|
|
136
113
|
description: 'Narrow in same grep; no follow-up grep for equivalent scope changes.',
|
|
137
114
|
},
|
|
138
115
|
output_mode: { type: 'string', enum: ['content_with_context', 'content', 'files_with_matches', 'count'], description: 'Broad scope: files_with_matches/count. Narrow scope: content_with_context; answer from it, skip read unless span is not shown.' },
|
|
139
|
-
context: { type: 'number', minimum: 0, description: 'Lines before/after each match; keep bounded.' },
|
|
140
116
|
head_limit: { type: 'number', minimum: 0, description: 'Max output lines; keep small.' },
|
|
141
117
|
offset: { type: 'number', minimum: 0, description: 'Skip output lines for paging.' },
|
|
118
|
+
'-A': { type: 'number', minimum: 0, description: 'Lines after each match.' },
|
|
119
|
+
'-B': { type: 'number', minimum: 0, description: 'Lines before each match.' },
|
|
120
|
+
'-C': { type: 'number', minimum: 0, description: 'Lines before/after each match.' },
|
|
142
121
|
},
|
|
143
122
|
required: [],
|
|
144
123
|
},
|
|
@@ -147,7 +126,7 @@ export const BUILTIN_TOOLS = [
|
|
|
147
126
|
name: 'glob',
|
|
148
127
|
title: 'Mixdog Glob',
|
|
149
128
|
annotations: { title: 'Mixdog Glob', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, compressible: true },
|
|
150
|
-
description: 'Find files by exact glob. Unknown path/name uses find.',
|
|
129
|
+
description: 'Find files by exact glob. Unknown path/name uses find. Multiple patterns/dirs = ONE call with pattern[]/path[], never parallel single-pattern calls.',
|
|
151
130
|
inputSchema: {
|
|
152
131
|
type: 'object',
|
|
153
132
|
properties: {
|
|
@@ -156,14 +135,14 @@ export const BUILTIN_TOOLS = [
|
|
|
156
135
|
{ type: 'string' },
|
|
157
136
|
{ type: 'array', items: { type: 'string' }, minItems: 1 },
|
|
158
137
|
],
|
|
159
|
-
description: 'Exact glob pattern(s).',
|
|
138
|
+
description: 'Exact glob pattern(s). Batch related patterns as pattern[] in one call.',
|
|
160
139
|
},
|
|
161
140
|
path: {
|
|
162
141
|
anyOf: [
|
|
163
142
|
{ type: 'string' },
|
|
164
143
|
{ type: 'array', items: { type: 'string' }, minItems: 1 },
|
|
165
144
|
],
|
|
166
|
-
description: 'Base directory/directories.',
|
|
145
|
+
description: 'Base directory/directories. Batch multiple roots as path[] in one call.',
|
|
167
146
|
},
|
|
168
147
|
head_limit: { type: 'number', description: 'Max entries.' },
|
|
169
148
|
offset: { type: 'number', description: 'Skip entries.' },
|
|
@@ -175,11 +154,17 @@ export const BUILTIN_TOOLS = [
|
|
|
175
154
|
name: 'find',
|
|
176
155
|
title: 'Mixdog Find Files',
|
|
177
156
|
annotations: { title: 'Mixdog Find Files', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, compressible: true },
|
|
178
|
-
description: 'Find files by partial path/name. Exact structure uses glob. Returns verified paths.',
|
|
157
|
+
description: 'Find files by partial path/name. Exact structure uses glob. Returns verified paths. Multiple names = ONE call with query[].',
|
|
179
158
|
inputSchema: {
|
|
180
159
|
type: 'object',
|
|
181
160
|
properties: {
|
|
182
|
-
query: {
|
|
161
|
+
query: {
|
|
162
|
+
anyOf: [
|
|
163
|
+
{ type: 'string' },
|
|
164
|
+
{ type: 'array', items: { type: 'string' }, minItems: 1 },
|
|
165
|
+
],
|
|
166
|
+
description: 'Partial path/name words (not file contents), or query[] to batch several lookups in one call.',
|
|
167
|
+
},
|
|
183
168
|
path: { type: 'string', description: 'Base directory.' },
|
|
184
169
|
head_limit: { type: 'number', description: 'Max paths.' },
|
|
185
170
|
},
|
|
@@ -190,11 +175,17 @@ export const BUILTIN_TOOLS = [
|
|
|
190
175
|
name: 'list',
|
|
191
176
|
title: 'Mixdog List Directory',
|
|
192
177
|
annotations: { title: 'Mixdog List Directory', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, compressible: true },
|
|
193
|
-
description: 'List known directory entries. Use glob for broad discovery.',
|
|
178
|
+
description: 'List known directory entries. Use glob for broad discovery. Multiple dirs = ONE call with path[].',
|
|
194
179
|
inputSchema: {
|
|
195
180
|
type: 'object',
|
|
196
181
|
properties: {
|
|
197
|
-
path: {
|
|
182
|
+
path: {
|
|
183
|
+
anyOf: [
|
|
184
|
+
{ type: 'string' },
|
|
185
|
+
{ type: 'array', items: { type: 'string' }, minItems: 1 },
|
|
186
|
+
],
|
|
187
|
+
description: 'Directory, or path[] to list several directories in one call.',
|
|
188
|
+
},
|
|
198
189
|
head_limit: { type: 'number', description: 'Max entries.' },
|
|
199
190
|
offset: { type: 'number', description: 'Skip N entries for paging.' },
|
|
200
191
|
},
|
|
@@ -60,6 +60,26 @@ function listGuardPath(p) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export async function executeListTool(args, workDir, options = {}) {
|
|
63
|
+
if (Array.isArray(args.path)) {
|
|
64
|
+
const list = [...new Set(args.path.map((p) => (typeof p === 'string' ? p.trim() : '')).filter((p) => p.length > 0))];
|
|
65
|
+
const capped = list.length > 10;
|
|
66
|
+
const targets = capped ? list.slice(0, 10) : list;
|
|
67
|
+
if (targets.length > 1) {
|
|
68
|
+
const sections = [];
|
|
69
|
+
for (const p of targets) {
|
|
70
|
+
let body;
|
|
71
|
+
try {
|
|
72
|
+
body = await executeListTool({ ...args, path: p }, workDir, options);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
body = `Error: ${normalizeErrorMessage(err instanceof Error ? err.message : String(err))}`;
|
|
75
|
+
}
|
|
76
|
+
sections.push(`# list ${p}\n${body}`);
|
|
77
|
+
}
|
|
78
|
+
if (capped) sections.push(`... [capped at 10 of ${list.length} paths]`);
|
|
79
|
+
return sections.join('\n\n');
|
|
80
|
+
}
|
|
81
|
+
args.path = targets[0];
|
|
82
|
+
}
|
|
63
83
|
if (typeof args.fuzzy === 'string' && args.fuzzy.length > 0) {
|
|
64
84
|
return executeFuzzyFindTool({ ...args, query: args.fuzzy }, workDir, options);
|
|
65
85
|
}
|
|
@@ -302,6 +322,26 @@ export async function executeTreeTool(args, workDir, options = {}) {
|
|
|
302
322
|
// routes here for hidden backward compatibility, but the model-facing tool is
|
|
303
323
|
// `find`.
|
|
304
324
|
export async function executeFuzzyFindTool(args, workDir, options = {}) {
|
|
325
|
+
if (Array.isArray(args.query)) {
|
|
326
|
+
const list = [...new Set(args.query.map((q) => (typeof q === 'string' ? q.trim() : '')).filter((q) => q.length > 0))];
|
|
327
|
+
const capped = list.length > 10;
|
|
328
|
+
const targets = capped ? list.slice(0, 10) : list;
|
|
329
|
+
if (targets.length > 1) {
|
|
330
|
+
const sections = [];
|
|
331
|
+
for (const q of targets) {
|
|
332
|
+
let body;
|
|
333
|
+
try {
|
|
334
|
+
body = await executeFuzzyFindTool({ ...args, query: q }, workDir, options);
|
|
335
|
+
} catch (err) {
|
|
336
|
+
body = `Error: ${normalizeErrorMessage(err instanceof Error ? err.message : String(err))}`;
|
|
337
|
+
}
|
|
338
|
+
sections.push(`# find ${q}\n${body}`);
|
|
339
|
+
}
|
|
340
|
+
if (capped) sections.push(`... [capped at 10 of ${list.length} queries]`);
|
|
341
|
+
return sections.join('\n\n');
|
|
342
|
+
}
|
|
343
|
+
args.query = targets[0];
|
|
344
|
+
}
|
|
305
345
|
const query = String(args.query ?? args.fuzzy ?? '').trim();
|
|
306
346
|
if (!query) return 'Error: find requires query.';
|
|
307
347
|
const inputPath = normalizeInputPath(args.path) || '.';
|
|
@@ -181,6 +181,35 @@ async function assertRgAvailable() {
|
|
|
181
181
|
throw e;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
// ── PCRE2 capability detection ───────────────────────────────────────────
|
|
185
|
+
// ripgrep's default regex engine (Rust `regex` crate) intentionally rejects
|
|
186
|
+
// lookaround/backreferences ("look-around ... is not supported" /
|
|
187
|
+
// "backreferences ... not supported"). Some rg builds are compiled with the
|
|
188
|
+
// optional `pcre2` feature, which adds `-P`/`--pcre2` support for exactly
|
|
189
|
+
// those constructs. Detect capability once via `rg --pcre2-version`: it
|
|
190
|
+
// exits 0 and prints a version when PCRE2 is linked in, and exits non-zero
|
|
191
|
+
// ("PCRE2 is not available...") when it is not. Cached permanently on
|
|
192
|
+
// success; failure is cached briefly so a mid-session rg upgrade/reinstall
|
|
193
|
+
// is picked up without a daemon restart (mirrors rgFallbackUsable's TTL).
|
|
194
|
+
const RG_PCRE2_FAIL_TTL_MS = 30000;
|
|
195
|
+
let _rgPcre2Supported = null;
|
|
196
|
+
let _rgPcre2FailAt = 0;
|
|
197
|
+
export async function rgSupportsPcre2() {
|
|
198
|
+
if (_rgPcre2Supported === true) return true;
|
|
199
|
+
if (_rgPcre2Supported === false && (Date.now() - _rgPcre2FailAt) < RG_PCRE2_FAIL_TTL_MS) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
await ensureRgResolved();
|
|
204
|
+
await execFileAsync(rgExecutable(), ['--pcre2-version'], { windowsHide: true, timeout: 3000 });
|
|
205
|
+
_rgPcre2Supported = true;
|
|
206
|
+
} catch {
|
|
207
|
+
_rgPcre2Supported = false;
|
|
208
|
+
_rgPcre2FailAt = Date.now();
|
|
209
|
+
}
|
|
210
|
+
return _rgPcre2Supported;
|
|
211
|
+
}
|
|
212
|
+
|
|
184
213
|
// Cap rg stdout accumulation so a runaway producer (huge repo, accidental
|
|
185
214
|
// match-all) cannot balloon the JS string heap. Mirrors CC's ripgrep.ts cap.
|
|
186
215
|
const MAX_RG_STDOUT_BYTES = 20 * 1024 * 1024; // 20MB
|
|
@@ -29,6 +29,7 @@ export function buildGrepCacheKey(parts) {
|
|
|
29
29
|
multilineMode,
|
|
30
30
|
fileType,
|
|
31
31
|
onlyMatching,
|
|
32
|
+
pcre2,
|
|
32
33
|
} = parts;
|
|
33
34
|
return [
|
|
34
35
|
'grep',
|
|
@@ -46,6 +47,7 @@ export function buildGrepCacheKey(parts) {
|
|
|
46
47
|
multilineMode ? 'm1' : 'm0',
|
|
47
48
|
onlyMatching ? 'o1' : 'o0',
|
|
48
49
|
Array.isArray(fileType) ? fileType.join('\x01') : (fileType || ''),
|
|
50
|
+
pcre2 ? 'p1' : 'p0',
|
|
49
51
|
].join('|');
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -64,6 +66,7 @@ export function buildGrepRgArgs(parts) {
|
|
|
64
66
|
fileType,
|
|
65
67
|
onlyMatching,
|
|
66
68
|
fixedStrings = false,
|
|
69
|
+
pcre2 = false,
|
|
67
70
|
} = parts;
|
|
68
71
|
// `--hidden`: search dotfiles/dot-dirs (.github, .mixdog) that
|
|
69
72
|
// rg skips by default. The DEFAULT_IGNORE_GLOBS below still exclude .git and
|
|
@@ -84,6 +87,11 @@ export function buildGrepRgArgs(parts) {
|
|
|
84
87
|
}
|
|
85
88
|
if (caseInsensitive) rgArgs.push('-i');
|
|
86
89
|
if (fixedStrings) rgArgs.push('-F');
|
|
90
|
+
// PCRE2 engine: opt-in only when the caller already confirmed (via
|
|
91
|
+
// rgSupportsPcre2() in search-tool.mjs) that the installed rg binary was
|
|
92
|
+
// built with PCRE2 support. Enables lookaround/backreference patterns
|
|
93
|
+
// that the default Rust regex engine rejects outright.
|
|
94
|
+
if (pcre2) rgArgs.push('-P');
|
|
87
95
|
if (multilineMode) rgArgs.push('-U', '--multiline-dotall');
|
|
88
96
|
if (Array.isArray(fileType)) {
|
|
89
97
|
for (const t of fileType) if (t) rgArgs.push('--type', t);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search path diagnostics: ENOENT recovery + path-shape helpers shared by
|
|
3
|
+
* grep/glob in search-tool.mjs. Extracted verbatim so search-tool.mjs stays a
|
|
4
|
+
* thin executor; search-tool.mjs re-exports these for unchanged importers.
|
|
5
|
+
*/
|
|
6
|
+
import { statSync } from 'fs';
|
|
7
|
+
import { isAbsolute, resolve } from 'path';
|
|
8
|
+
import { findBySuffixStrip, findFileByBasename } from './path-diagnostics.mjs';
|
|
9
|
+
import { normalizeOutputPath, resolveAgainstCwd } from './path-utils.mjs';
|
|
10
|
+
|
|
11
|
+
// Deterministic ENOENT recovery: when a grep path does not exist, surface
|
|
12
|
+
// indexed files that share the missing path's basename, turning a guessed or
|
|
13
|
+
// misplaced path (e.g. session/result-compression.mjs vs the real
|
|
14
|
+
// tools/result-compression.mjs) into the actual file in one step. Exact
|
|
15
|
+
// basename only — no stem/token fuzzing — so the hint is high-signal and
|
|
16
|
+
// noise-free. Invariant: every ENOENT runs the same basename lookup; there is
|
|
17
|
+
// no "guessed a lot" branch. Returns '' (appends nothing) when no same-named
|
|
18
|
+
// indexed file exists or the glob child is unavailable.
|
|
19
|
+
export async function _suggestIndexedPaths(missingPath, executeChildBuiltinTool, workDir) {
|
|
20
|
+
if (typeof executeChildBuiltinTool !== 'function') return '';
|
|
21
|
+
const base = String(missingPath).replace(/\\/g, '/').split('/').pop();
|
|
22
|
+
// Skip when there is no usable basename or it carries glob magic (the
|
|
23
|
+
// pattern, not a literal filename, would not map to a real file).
|
|
24
|
+
if (!base || /[*?[\]{}]/.test(base)) return '';
|
|
25
|
+
try {
|
|
26
|
+
const out = await executeChildBuiltinTool('glob', { pattern: `**/${base}`, head_limit: 6 }, workDir);
|
|
27
|
+
if (typeof out !== 'string') return '';
|
|
28
|
+
// Drop ONLY the exact diagnostic forms glob emits: the empty-result line
|
|
29
|
+
// ("(no files found ...", "(no entries after offset ...") and the
|
|
30
|
+
// suffix/warning lines ("... [N more entries]", "... [warning] ...").
|
|
31
|
+
// A broad startsWith('(' / '...' / 'Error') would wrongly drop real
|
|
32
|
+
// paths like ErrorBoundary.mjs or a "(draft)/x.mjs" leading segment.
|
|
33
|
+
const hits = out.split('\n')
|
|
34
|
+
.filter((s) => s && !/^\(no (?:files found|entries\b)/.test(s) && !/^\.\.\. \[/.test(s))
|
|
35
|
+
.map((s) => s.trim())
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.slice(0, 5);
|
|
38
|
+
return hits.length ? `\n[path not found here; same-named indexed file(s): ${hits.join(', ')}]` : '';
|
|
39
|
+
} catch {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Models occasionally glue a quoted segment onto an otherwise-unquoted path
|
|
45
|
+
// (e.g. `C:/Project/mixdog/"src/x.mjs"` — prefix bare, trailing segment
|
|
46
|
+
// wrapped in literal quote chars from a mis-escaped interpolation). But
|
|
47
|
+
// quote chars CAN be legit path content (POSIX `src/Bob's/file.mjs`), so
|
|
48
|
+
// stripping is existence-guarded: keep the original if it stats, only fall
|
|
49
|
+
// back to the stripped variant otherwise. Cost is one statSync and only on
|
|
50
|
+
// the rare quote-containing path.
|
|
51
|
+
export function stripEmbeddedPathQuotes(p) {
|
|
52
|
+
if (typeof p !== 'string' || !p) return p;
|
|
53
|
+
if (!p.includes('"') && !p.includes("'")) return p;
|
|
54
|
+
try { statSync(p); return p; } catch { /* fall through to stripped */ }
|
|
55
|
+
return p.replace(/['"]/g, '');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Reuse read's own ENOENT recovery (path-diagnostics findBySuffixStrip /
|
|
59
|
+
// findFileByBasename — the exact fs scans read-single-tool.mjs already runs
|
|
60
|
+
// for its "same filename exists at" hint) so a grep/glob path miss redirects
|
|
61
|
+
// to the real location instead of dead-ending. No new fs scan is added here;
|
|
62
|
+
// this only calls the two helpers read already calls, with a caller-supplied
|
|
63
|
+
// action verb ("Search"/"List") in place of read's "Read". Gated to genuine
|
|
64
|
+
// not-found codes: EACCES/EPERM etc. keep their real failure semantics and
|
|
65
|
+
// must not trigger same-basename guidance or the BFS scan.
|
|
66
|
+
const NOT_FOUND_CODES = new Set(['ENOENT', 'ENOTDIR']);
|
|
67
|
+
export function buildNotFoundHint(workDir, missingPath, actionVerb, errCode = 'ENOENT') {
|
|
68
|
+
if (!NOT_FOUND_CODES.has(String(errCode || 'ENOENT'))) return '';
|
|
69
|
+
const suffixHit = findBySuffixStrip(workDir, missingPath);
|
|
70
|
+
if (suffixHit) {
|
|
71
|
+
return ` Not found at this path; the same filename exists at: "${normalizeOutputPath(suffixHit)}". ${actionVerb} that path directly.`;
|
|
72
|
+
}
|
|
73
|
+
const elsewhere = findFileByBasename(workDir, missingPath);
|
|
74
|
+
if (elsewhere.length) {
|
|
75
|
+
return ` Not found at this path; the same filename exists at: ${elsewhere.map((p) => `"${normalizeOutputPath(p)}"`).join(', ')}. ${actionVerb} that path directly.`;
|
|
76
|
+
}
|
|
77
|
+
return '';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function relativePathPrefix(pathPrefix, workDir) {
|
|
81
|
+
if (!workDir) return pathPrefix;
|
|
82
|
+
const cwdFwd = workDir.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
83
|
+
const absFwd = String(pathPrefix || '').replace(/\\/g, '/');
|
|
84
|
+
const haystack = process.platform === 'win32' ? absFwd.toLocaleLowerCase() : absFwd;
|
|
85
|
+
const needle = process.platform === 'win32' ? cwdFwd.toLocaleLowerCase() : cwdFwd;
|
|
86
|
+
if (haystack.startsWith(needle + '/') || haystack === needle) {
|
|
87
|
+
return absFwd.slice(cwdFwd.length + 1) || '.';
|
|
88
|
+
}
|
|
89
|
+
return pathPrefix;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function relativeSearchResultPath(path, workDir) {
|
|
93
|
+
const normalizedWorkDir = normalizeOutputPath(workDir);
|
|
94
|
+
const normalizedAbs = normalizeOutputPath(path);
|
|
95
|
+
if (normalizedAbs.startsWith(normalizedWorkDir + '/') || normalizedAbs.startsWith(normalizedWorkDir + '\\')) {
|
|
96
|
+
return normalizedAbs.slice(normalizedWorkDir.length + 1);
|
|
97
|
+
}
|
|
98
|
+
return normalizedAbs;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function resolveSearchScope(root, workDir) {
|
|
102
|
+
return isAbsolute(root) ? resolve(root) : resolveAgainstCwd(root, workDir);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function isUncOrSmbPath(path) {
|
|
106
|
+
if (typeof path !== 'string' || !path) return false;
|
|
107
|
+
return path.startsWith('\\\\') || path.startsWith('//');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function uncRefusalMessage(toolName, original, resolved) {
|
|
111
|
+
const shown = normalizeOutputPath(resolved || original || '');
|
|
112
|
+
return `Error: ${toolName} refuses UNC/SMB path ${JSON.stringify(shown)}; remote share access is blocked to prevent NTLM credential leaks`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function basePathDiagnostic(basePaths, workDir) {
|
|
116
|
+
return basePaths.map((basePath) => {
|
|
117
|
+
const resolved = resolveSearchScope(basePath, workDir);
|
|
118
|
+
try {
|
|
119
|
+
const st = statSync(resolved);
|
|
120
|
+
return `${normalizeOutputPath(basePath)}: ${st.isDirectory() ? 'path exists (dir)' : 'path exists (file)'}`;
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return `${normalizeOutputPath(basePath)}: path does not exist (${err?.code || 'ENOENT'})`
|
|
123
|
+
+ buildNotFoundHint(workDir, resolved, 'Search', err?.code);
|
|
124
|
+
}
|
|
125
|
+
}).join('; ');
|
|
126
|
+
}
|