mixdog 0.9.2 → 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 +8 -3
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- 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/build-tui.mjs +13 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/hook-bus-test.mjs +191 -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/path-suffix-test.mjs +57 -0
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +207 -0
- 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 +30 -67
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +5 -2
- package/src/agents/heavy-worker/AGENT.md +21 -11
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +3 -2
- package/src/agents/worker/AGENT.md +21 -11
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +933 -3731
- 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/repl.mjs +5 -5
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/agent/30-explorer.md +8 -11
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +2 -9
- package/src/rules/shared/01-tool.md +11 -5
- 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/context/collect.mjs +51 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- 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 +97 -1343
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
- 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 +44 -1014
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- 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/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
- 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 +41 -1142
- 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 +303 -2119
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
- 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/providers/opencode-go-usage.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
- 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-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
- 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/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -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/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
- 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 +3 -3
- 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 +33 -42
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- 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 -87
- 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 +78 -304
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
- 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 +1 -23
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- 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 +241 -894
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/crash-log.mjs +106 -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/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +9 -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/telegram-format.mjs +19 -22
- 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/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +258 -2050
- package/src/runtime/memory/lib/core-memory-store.mjs +351 -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/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -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 +72 -837
- 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-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +20 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +238 -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 +6 -14
- 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/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- 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 +52 -2
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +291 -0
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-capabilities.mjs +130 -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 +126 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- 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/session-text.mjs +100 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +358 -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 +155 -677
- package/src/standalone/channel-worker.mjs +7 -9
- package/src/standalone/explore-tool.mjs +40 -12
- 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 +110 -746
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +125 -0
- package/src/standalone/provider-admin.mjs +15 -19
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1163 -7571
- 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 +259 -80
- 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 +56 -588
- 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/components/tool-output-format.mjs +2 -2
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +18034 -17188
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -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/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +405 -1385
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +60 -10
- package/src/tui/keyboard-protocol.mjs +2 -2
- package/src/tui/lib/voice-recorder.mjs +35 -19
- package/src/tui/markdown/format-token.mjs +11 -9
- package/src/tui/paste-attachments.mjs +38 -0
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/tui/themes/base.mjs +2 -2
- package/src/tui/themes/kanagawa.mjs +4 -4
- package/src/tui/themes/teal.mjs +4 -5
- package/src/tui/themes/utils.mjs +1 -1
- 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 -462
- 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 -9
- package/src/workflows/sequential/WORKFLOW.md +16 -11
- package/src/workflows/solo/WORKFLOW.md +5 -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/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- 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/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/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
|
@@ -41,13 +41,16 @@ function graphBinDir(dataDir) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
async function loadManifest(dataDir) {
|
|
44
|
+
// Bundled manifest FIRST: it always matches the installed mixdog version.
|
|
45
|
+
// A stale cached manifest.json from an older install must never shadow it
|
|
46
|
+
// (caused sha256 mismatches on already-installed machines after upgrades).
|
|
47
|
+
if (existsSync(BUNDLED_MANIFEST_PATH)) {
|
|
48
|
+
return JSON.parse(readFileSync(BUNDLED_MANIFEST_PATH, 'utf8'));
|
|
49
|
+
}
|
|
44
50
|
const cached = join(graphBinDir(dataDir), 'manifest.json');
|
|
45
51
|
if (existsSync(cached)) {
|
|
46
52
|
try { return JSON.parse(readFileSync(cached, 'utf8')); } catch { /* fall through */ }
|
|
47
53
|
}
|
|
48
|
-
if (existsSync(BUNDLED_MANIFEST_PATH)) {
|
|
49
|
-
return JSON.parse(readFileSync(BUNDLED_MANIFEST_PATH, 'utf8'));
|
|
50
|
-
}
|
|
51
54
|
const res = await fetch(MANIFEST_URL, { signal: AbortSignal.timeout(30_000) });
|
|
52
55
|
if (!res.ok) throw new Error(`[graph-fetcher] manifest fetch failed: ${res.status} ${res.statusText}`);
|
|
53
56
|
return res.json();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Shared literal constants for the apply_patch modules. Extracted from
|
|
2
|
+
// patch.mjs verbatim so parsing/dispatch behavior is unchanged.
|
|
3
|
+
|
|
4
|
+
export const DEV_NULL = /^\/dev\/null$/;
|
|
5
|
+
export const V4A_EOF_MARKER = '*** End of File';
|
|
6
|
+
export const V4A_MOVE_TO_PREFIX = '*** Move to:';
|
|
7
|
+
|
|
8
|
+
export const UNIFIED_HUNK_HEADER_RE = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/;
|
|
9
|
+
export const UNIFIED_HUNK_HEADER_CAPTURE_RE = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@(.*)$/;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Native-engine dispatch + failure-context formatting for apply_patch. Moved
|
|
2
|
+
// verbatim from patch.mjs; native protocol, cache/snapshot side effects, and
|
|
3
|
+
// output formatting are unchanged.
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { performance } from 'node:perf_hooks';
|
|
7
|
+
import {
|
|
8
|
+
normalizeOutputPath,
|
|
9
|
+
invalidateBuiltinResultCache,
|
|
10
|
+
recordReadSnapshotForPath,
|
|
11
|
+
clearReadSnapshotForPath,
|
|
12
|
+
} from '../builtin.mjs';
|
|
13
|
+
import { markCodeGraphDirtyPaths } from '../code-graph-state.mjs';
|
|
14
|
+
import { classifyEntry, stripDiffPrefix, resolveEntryPath } from './paths.mjs';
|
|
15
|
+
import {
|
|
16
|
+
getNativePatchServer,
|
|
17
|
+
scheduleNativePatchIdleClose,
|
|
18
|
+
nativePatchTraceEnabled,
|
|
19
|
+
patchTraceEnabled,
|
|
20
|
+
ioTrace,
|
|
21
|
+
} from './native-server.mjs';
|
|
22
|
+
import {
|
|
23
|
+
extractNativeFailurePath,
|
|
24
|
+
nativeFailureMatchesEntry,
|
|
25
|
+
findFirstFailingUnifiedHunk,
|
|
26
|
+
firstFailingUnifiedHunkLineDetail,
|
|
27
|
+
firstMeaningfulUnifiedHunkLine,
|
|
28
|
+
firstMeaningfulUnifiedEntryLine,
|
|
29
|
+
compactPatchPreviewLine,
|
|
30
|
+
nearestPatchLineHint,
|
|
31
|
+
normalizeTypographic,
|
|
32
|
+
splitBufferLinesForPatch,
|
|
33
|
+
splitTextLinesForPatch,
|
|
34
|
+
} from './matcher.mjs';
|
|
35
|
+
|
|
36
|
+
export function formatNativeFailureContext(parsed, basePath, failedPath = '', options = {}) {
|
|
37
|
+
const entries = Array.isArray(parsed) ? parsed : [];
|
|
38
|
+
const entry = entries.find((candidate) => classifyEntry(candidate) !== 'create' && nativeFailureMatchesEntry(candidate, failedPath))
|
|
39
|
+
|| entries.find((candidate) => classifyEntry(candidate) !== 'create');
|
|
40
|
+
const headerName = entry?.oldFileName;
|
|
41
|
+
const displayPath = headerName ? normalizeOutputPath(stripDiffPrefix(headerName)) : '';
|
|
42
|
+
const fuzz = Number.isFinite(options?.fuzz) && options.fuzz > 0 ? Math.floor(options.fuzz) : 0;
|
|
43
|
+
let sourceLines = null;
|
|
44
|
+
let sourceByteLines = null;
|
|
45
|
+
try {
|
|
46
|
+
const fullPath = resolveEntryPath(basePath, entry.oldFileName);
|
|
47
|
+
const raw = readFileSync(fullPath); // Buffer — no 'utf8' decode
|
|
48
|
+
sourceByteLines = splitBufferLinesForPatch(raw);
|
|
49
|
+
sourceLines = splitTextLinesForPatch(raw.toString('utf8'));
|
|
50
|
+
} catch {}
|
|
51
|
+
const failingHunk = sourceByteLines ? findFirstFailingUnifiedHunk(entry, sourceByteLines, fuzz) : null;
|
|
52
|
+
const failingDetail = (failingHunk && sourceByteLines)
|
|
53
|
+
? firstFailingUnifiedHunkLineDetail(sourceByteLines, failingHunk)
|
|
54
|
+
: null;
|
|
55
|
+
const expected = failingDetail || firstMeaningfulUnifiedHunkLine(failingHunk) || firstMeaningfulUnifiedEntryLine(entry);
|
|
56
|
+
if (!entry || !expected?.line) return '';
|
|
57
|
+
const expectedText = JSON.stringify(compactPatchPreviewLine(expected.line));
|
|
58
|
+
let nearest = '';
|
|
59
|
+
let normalizeHint = '';
|
|
60
|
+
if (sourceLines) {
|
|
61
|
+
nearest = nearestPatchLineHint(sourceLines, expected.line, expected.preferredLine);
|
|
62
|
+
const wantNorm = normalizeTypographic(expected.line);
|
|
63
|
+
if (wantNorm) {
|
|
64
|
+
for (let i = 0; i < sourceLines.length; i++) {
|
|
65
|
+
if (sourceLines[i] === expected.line) break; // exact match exists; not a normalization issue
|
|
66
|
+
if (
|
|
67
|
+
sourceLines[i].trim() !== expected.line.trim()
|
|
68
|
+
&& normalizeTypographic(sourceLines[i]) === wantNorm
|
|
69
|
+
) {
|
|
70
|
+
normalizeHint = `context matches after Unicode normalization at line ${i + 1} — source may contain typographic dashes/quotes/NBSP`;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return ` expected first old/context line${displayPath ? ` in ${displayPath}` : ''}: ${expectedText}${nearest ? `; ${nearest}` : ''}${normalizeHint ? `; ${normalizeHint}` : ''}; use exact current lines, no stubs.`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Dispatch the (already validated, header-rewritten) patch to the native
|
|
80
|
+
// engine. Throws on any native error; on success returns the formatted
|
|
81
|
+
// human-readable response string. Never silently falls back to JS — the
|
|
82
|
+
// caller MUST surface throws as `Error: ...` strings.
|
|
83
|
+
export async function dispatchNativePatch({ entries, basePath, nativePatchStr, fuzz, rejectPartial, dryRun, readStateScope, signal, parsed }) {
|
|
84
|
+
const nativeStart = performance.now();
|
|
85
|
+
let stats;
|
|
86
|
+
try {
|
|
87
|
+
stats = await getNativePatchServer().apply(basePath, nativePatchStr, { fuzz, rejectPartial, dryRun, signal });
|
|
88
|
+
} catch (err) {
|
|
89
|
+
scheduleNativePatchIdleClose();
|
|
90
|
+
const msg = err?.message || String(err);
|
|
91
|
+
const failedPath = extractNativeFailurePath(msg, parsed);
|
|
92
|
+
return `Error: native patch failed — ${msg}${formatNativeFailureContext(parsed, basePath, failedPath, { fuzz })}`;
|
|
93
|
+
}
|
|
94
|
+
const afterInvalidateStart = performance.now();
|
|
95
|
+
const failedDisplaySet = new Set();
|
|
96
|
+
for (const f of stats.failures || []) {
|
|
97
|
+
if (!f?.path) continue;
|
|
98
|
+
failedDisplaySet.add(normalizeOutputPath(f.path));
|
|
99
|
+
failedDisplaySet.add(normalizeOutputPath(stripDiffPrefix(f.path)));
|
|
100
|
+
}
|
|
101
|
+
const writtenEntries = entries.filter((entry) => !failedDisplaySet.has(entry.displayPath));
|
|
102
|
+
const fullPaths = writtenEntries.map((entry) => entry.fullPath);
|
|
103
|
+
if (!dryRun) invalidateBuiltinResultCache(fullPaths);
|
|
104
|
+
const afterInvalidate = performance.now();
|
|
105
|
+
if (!dryRun) markCodeGraphDirtyPaths(fullPaths);
|
|
106
|
+
const afterDirty = performance.now();
|
|
107
|
+
if (!dryRun) {
|
|
108
|
+
for (let i = 0; i < writtenEntries.length; i++) {
|
|
109
|
+
const entry = writtenEntries[i];
|
|
110
|
+
if (entry.kind === 'delete') {
|
|
111
|
+
clearReadSnapshotForPath(entry.fullPath, readStateScope);
|
|
112
|
+
} else {
|
|
113
|
+
const snapshotMeta = {
|
|
114
|
+
source: 'apply_patch_native',
|
|
115
|
+
isPartialView: false,
|
|
116
|
+
};
|
|
117
|
+
const contentHash = stats.contentHashes?.[i] || null;
|
|
118
|
+
if (contentHash) snapshotMeta.contentHash = contentHash;
|
|
119
|
+
recordReadSnapshotForPath(entry.fullPath, readStateScope, snapshotMeta);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const afterSnapshot = performance.now();
|
|
124
|
+
ioTrace('apply_patch_native', {
|
|
125
|
+
files: writtenEntries.length,
|
|
126
|
+
dryRun,
|
|
127
|
+
partial: stats.partial,
|
|
128
|
+
failed: stats.failures.length,
|
|
129
|
+
roundtripMs: Number(stats.roundtripMs.toFixed(3)),
|
|
130
|
+
rustTotalMs: Number(stats.totalMs.toFixed(3)),
|
|
131
|
+
invalidateMs: Number((afterInvalidate - afterInvalidateStart).toFixed(3)),
|
|
132
|
+
dirtyMs: Number((afterDirty - afterInvalidate).toFixed(3)),
|
|
133
|
+
snapshotMs: Number((afterSnapshot - afterDirty).toFixed(3)),
|
|
134
|
+
contentHashes: (stats.contentHashes || []).filter(Boolean).length,
|
|
135
|
+
});
|
|
136
|
+
if (nativePatchTraceEnabled()) {
|
|
137
|
+
process.stderr.write(
|
|
138
|
+
`[patch-native-trace] files=${writtenEntries.length} partial=${stats.partial ? 1 : 0} failed=${stats.failures.length} roundtrip_ms=${stats.roundtripMs.toFixed(3)} rust_total_ms=${stats.totalMs.toFixed(3)} rust_hash_ms=${stats.hashMs.toFixed(3)} invalidate_ms=${(afterInvalidate - afterInvalidateStart).toFixed(3)} dirty_ms=${(afterDirty - afterInvalidate).toFixed(3)} snapshot_ms=${(afterSnapshot - afterDirty).toFixed(3)} total_js_ms=${(afterSnapshot - nativeStart).toFixed(3)} content_hashes=${(stats.contentHashes || []).filter(Boolean).length}\n`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (patchTraceEnabled()) {
|
|
142
|
+
process.stderr.write(`[patch-native] applied files=${writtenEntries.length} partial=${stats.partial ? 1 : 0} ms=${stats.totalMs.toFixed(3)}\n`);
|
|
143
|
+
}
|
|
144
|
+
scheduleNativePatchIdleClose();
|
|
145
|
+
const verb = dryRun ? 'checked' : 'applied';
|
|
146
|
+
const verbLabel = dryRun ? 'Checked' : 'Applied';
|
|
147
|
+
const countLabel = (count, singular, plural = `${singular}s`) => `${count} ${count === 1 ? singular : plural}`;
|
|
148
|
+
const kindLabel = (kind) => {
|
|
149
|
+
const text = String(kind || '').trim();
|
|
150
|
+
return text ? `${text.charAt(0).toUpperCase()}${text.slice(1).toLowerCase()}` : 'Update';
|
|
151
|
+
};
|
|
152
|
+
const summary = stats.partial
|
|
153
|
+
? `Error: Patch Partially ${verbLabel} (${countLabel(writtenEntries.length, 'File')} ${verb} · ${countLabel(stats.failures.length, 'File')} Skipped) (Native)`
|
|
154
|
+
: `${verbLabel} ${countLabel(writtenEntries.length, 'File')} (Native)${dryRun ? ' Dry Run' : ''}`;
|
|
155
|
+
const lines = [summary];
|
|
156
|
+
for (const entry of writtenEntries) {
|
|
157
|
+
const added = entry.added || 0;
|
|
158
|
+
const removed = entry.removed || 0;
|
|
159
|
+
const parts = [];
|
|
160
|
+
if (added > 0) parts.push(`+${countLabel(added, 'Line')}`);
|
|
161
|
+
if (removed > 0) parts.push(`-${countLabel(removed, 'Line')}`);
|
|
162
|
+
const detail = parts.join(' · ');
|
|
163
|
+
lines.push(detail
|
|
164
|
+
? ` OK ${kindLabel(entry.kind)} ${entry.displayPath} — ${detail}`
|
|
165
|
+
: ` OK ${kindLabel(entry.kind)} ${entry.displayPath}`);
|
|
166
|
+
}
|
|
167
|
+
for (const f of stats.failures || []) {
|
|
168
|
+
lines.push(` SKIP ${f.path || '(unknown)'} — ${f.reason}${formatNativeFailureContext(parsed, basePath, f.path, { fuzz })}`);
|
|
169
|
+
}
|
|
170
|
+
return lines.join('\n');
|
|
171
|
+
}
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
// Diagnostic byte-parity matcher, line splitters, typographic normalization,
|
|
2
|
+
// nearest-line hints, and the V4A line-sequence locator. Moved verbatim from
|
|
3
|
+
// patch.mjs; matching/fuzz semantics mirror the native engine and are
|
|
4
|
+
// unchanged.
|
|
5
|
+
|
|
6
|
+
import { classifyEntry, stripDiffPrefix } from './paths.mjs';
|
|
7
|
+
import { normalizeOutputPath } from '../builtin.mjs';
|
|
8
|
+
|
|
9
|
+
export function collectUnifiedOldLines(hunk) {
|
|
10
|
+
const oldLines = [];
|
|
11
|
+
let lastWasOld = false;
|
|
12
|
+
for (const raw of hunk?.lines || []) {
|
|
13
|
+
if (typeof raw !== 'string' || raw.length === 0) continue;
|
|
14
|
+
const tag = raw[0];
|
|
15
|
+
if (tag === '-' || tag === ' ') {
|
|
16
|
+
oldLines.push({ tag, line: raw.slice(1), hasNewline: true });
|
|
17
|
+
lastWasOld = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (tag === '\\') {
|
|
21
|
+
if (lastWasOld && oldLines.length > 0) oldLines[oldLines.length - 1].hasNewline = false;
|
|
22
|
+
lastWasOld = false;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
lastWasOld = false;
|
|
26
|
+
}
|
|
27
|
+
return oldLines;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function collectUnifiedOps(hunk) {
|
|
31
|
+
const ops = [];
|
|
32
|
+
for (const raw of hunk?.lines || []) {
|
|
33
|
+
if (typeof raw !== 'string' || raw.length === 0) continue;
|
|
34
|
+
const tag = raw[0];
|
|
35
|
+
if (tag === ' ') ops.push('context');
|
|
36
|
+
else if (tag === '-') ops.push('delete');
|
|
37
|
+
else if (tag === '+') ops.push('add');
|
|
38
|
+
}
|
|
39
|
+
return ops;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function computeUnifiedChangeBand(ops) {
|
|
43
|
+
let first = null;
|
|
44
|
+
let last = null;
|
|
45
|
+
let oldCursor = 0;
|
|
46
|
+
for (const op of ops) {
|
|
47
|
+
if (op === 'context') {
|
|
48
|
+
oldCursor += 1;
|
|
49
|
+
} else if (op === 'delete') {
|
|
50
|
+
const pos = (oldCursor + 1) * 2;
|
|
51
|
+
first = first === null ? pos : Math.min(first, pos);
|
|
52
|
+
last = last === null ? pos : Math.max(last, pos);
|
|
53
|
+
oldCursor += 1;
|
|
54
|
+
} else {
|
|
55
|
+
const pos = oldCursor * 2 + 1;
|
|
56
|
+
first = first === null ? pos : Math.min(first, pos);
|
|
57
|
+
last = last === null ? pos : Math.max(last, pos);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { first, last };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function firstMeaningfulUnifiedHunkLine(hunk) {
|
|
64
|
+
for (const { line } of collectUnifiedOldLines(hunk)) {
|
|
65
|
+
if (line.trim()) return { line, preferredLine: Math.max(0, (Number(hunk?.oldStart) || 1) - 1) };
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function firstFailingUnifiedHunkLineDetail(sourceLines, hunk) {
|
|
71
|
+
const oldLines = collectUnifiedOldLines(hunk);
|
|
72
|
+
if (!oldLines.length) return null;
|
|
73
|
+
const lineEq = (actual, expected) => {
|
|
74
|
+
const ab = toLineBytes(actual);
|
|
75
|
+
const eb = toLineBytes(expected);
|
|
76
|
+
return ab.equals(eb) || byteTrimPatchWhitespace(ab).equals(byteTrimPatchWhitespace(eb));
|
|
77
|
+
};
|
|
78
|
+
const prefixDepth = (start) => {
|
|
79
|
+
let d = 0;
|
|
80
|
+
while (d < oldLines.length && start + d < sourceLines.length && lineEq(sourceLines[start + d], oldLines[d].line)) d += 1;
|
|
81
|
+
return d;
|
|
82
|
+
};
|
|
83
|
+
const declared = Math.max(0, (Number(hunk?.oldStart) || 1) - 1);
|
|
84
|
+
const candidates = [];
|
|
85
|
+
if (declared < sourceLines.length) candidates.push(declared);
|
|
86
|
+
for (let i = 0; i < sourceLines.length && candidates.length < 8; i++) {
|
|
87
|
+
if (i !== declared && lineEq(sourceLines[i], oldLines[0].line)) candidates.push(i);
|
|
88
|
+
}
|
|
89
|
+
let best = null;
|
|
90
|
+
for (const start of candidates) {
|
|
91
|
+
const depth = prefixDepth(start);
|
|
92
|
+
if (depth >= oldLines.length) continue;
|
|
93
|
+
if (!best || depth > best.depth) best = { start, depth };
|
|
94
|
+
}
|
|
95
|
+
if (!best || best.depth === 0) return null;
|
|
96
|
+
return {
|
|
97
|
+
line: oldLines[best.depth].line,
|
|
98
|
+
preferredLine: best.start + best.depth,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function firstMeaningfulUnifiedEntryLine(entry) {
|
|
103
|
+
const hunks = Array.isArray(entry?.hunks) ? entry.hunks : [];
|
|
104
|
+
for (const hunk of hunks) {
|
|
105
|
+
const expected = firstMeaningfulUnifiedHunkLine(hunk);
|
|
106
|
+
if (expected) return expected;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// --- Byte-level diagnostic-matcher substrate (diagnostics ONLY) ---------------
|
|
112
|
+
export function toLineBytes(v) {
|
|
113
|
+
return Buffer.isBuffer(v) ? v : Buffer.from(String(v ?? ''), 'utf8');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function byteTrimPatchWhitespace(buf) {
|
|
117
|
+
let start = 0;
|
|
118
|
+
let end = buf.length;
|
|
119
|
+
while (start < end && (buf[start] === 0x20 || buf[start] === 0x09)) start++;
|
|
120
|
+
while (end > start && (buf[end - 1] === 0x20 || buf[end - 1] === 0x09)) end--;
|
|
121
|
+
return buf.subarray(start, end);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const __utf8FatalDecoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
125
|
+
export function decodeValidUtf8OrNull(buf) {
|
|
126
|
+
try {
|
|
127
|
+
return __utf8FatalDecoder.decode(buf);
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function unifiedOldLinesMatchAt(sourceLines, oldLines, startIdx, fuzz, band) {
|
|
134
|
+
if (startIdx < 0 || startIdx + oldLines.length > sourceLines.length) return null;
|
|
135
|
+
let fuzzUsed = 0;
|
|
136
|
+
let normCount = 0;
|
|
137
|
+
const srcFinalNewline = sourceLines.hasFinalNewline !== false;
|
|
138
|
+
const lastSrcIdx = sourceLines.length - 1;
|
|
139
|
+
for (let offset = 0; offset < oldLines.length; offset++) {
|
|
140
|
+
const expected = oldLines[offset];
|
|
141
|
+
const actualIdx = startIdx + offset;
|
|
142
|
+
const actualBytes = toLineBytes(sourceLines[actualIdx]);
|
|
143
|
+
const expectedBytes = toLineBytes(expected.line);
|
|
144
|
+
const expectedNL = expected.hasNewline !== false;
|
|
145
|
+
const actualNL = !(actualIdx === lastSrcIdx && !srcFinalNewline);
|
|
146
|
+
const newlineOk = expectedNL === actualNL;
|
|
147
|
+
if (newlineOk && actualBytes.equals(expectedBytes)) continue;
|
|
148
|
+
if (newlineOk && fuzz > 0 && (expected.tag === ' ' || expected.tag === '-') && byteTrimPatchWhitespace(actualBytes).equals(byteTrimPatchWhitespace(expectedBytes))) continue;
|
|
149
|
+
if (
|
|
150
|
+
newlineOk
|
|
151
|
+
&& fuzz > 0
|
|
152
|
+
&& (expected.tag === ' ' || expected.tag === '-')
|
|
153
|
+
) {
|
|
154
|
+
const actualStr = decodeValidUtf8OrNull(actualBytes);
|
|
155
|
+
const expectedStr = actualStr === null ? null : decodeValidUtf8OrNull(expectedBytes);
|
|
156
|
+
if (
|
|
157
|
+
actualStr !== null
|
|
158
|
+
&& expectedStr !== null
|
|
159
|
+
&& normalizeTypographic(actualStr) === normalizeTypographic(expectedStr)
|
|
160
|
+
) {
|
|
161
|
+
normCount++;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (fuzz > 0 && expected.tag === ' ') {
|
|
166
|
+
const ctxPos = (offset + 1) * 2;
|
|
167
|
+
const isOuter = (!band || band.first === null || band.last === null)
|
|
168
|
+
? true
|
|
169
|
+
: (ctxPos < band.first || ctxPos > band.last);
|
|
170
|
+
if (!isOuter) return null;
|
|
171
|
+
fuzzUsed++;
|
|
172
|
+
if (fuzzUsed <= fuzz) continue;
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
return { fuzzUsed, normCount };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function findUnifiedHunkMatch(sourceLines, hunk, minStartIdx, fuzz) {
|
|
180
|
+
const oldLines = collectUnifiedOldLines(hunk);
|
|
181
|
+
const band = computeUnifiedChangeBand(collectUnifiedOps(hunk));
|
|
182
|
+
const oldStart = Math.max(0, (Number(hunk?.oldStart) || 1) - 1);
|
|
183
|
+
if (oldLines.length === 0) {
|
|
184
|
+
const insertIdx = Math.max(0, Number(hunk?.oldStart) || 0);
|
|
185
|
+
return insertIdx >= minStartIdx && insertIdx <= sourceLines.length ? { start: insertIdx, end: insertIdx } : null;
|
|
186
|
+
}
|
|
187
|
+
if (oldStart >= minStartIdx && unifiedOldLinesMatchAt(sourceLines, oldLines, oldStart, 0, band) !== null) {
|
|
188
|
+
return { start: oldStart, end: oldStart + oldLines.length };
|
|
189
|
+
}
|
|
190
|
+
if (fuzz <= 0) return null;
|
|
191
|
+
let best = null;
|
|
192
|
+
for (let start = minStartIdx; start <= sourceLines.length - oldLines.length; start++) {
|
|
193
|
+
const matched = unifiedOldLinesMatchAt(sourceLines, oldLines, start, fuzz, band);
|
|
194
|
+
if (matched === null) continue;
|
|
195
|
+
const { fuzzUsed, normCount } = matched;
|
|
196
|
+
const distance = Math.abs(start - oldStart);
|
|
197
|
+
if (
|
|
198
|
+
!best ||
|
|
199
|
+
fuzzUsed < best.fuzzUsed ||
|
|
200
|
+
(fuzzUsed === best.fuzzUsed && normCount < best.normCount) ||
|
|
201
|
+
(fuzzUsed === best.fuzzUsed && normCount === best.normCount && distance < best.distance)
|
|
202
|
+
) {
|
|
203
|
+
best = { start, distance, fuzzUsed, normCount };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return best ? { start: best.start, end: best.start + oldLines.length } : null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function findFirstFailingUnifiedHunk(entry, sourceLines, fuzz) {
|
|
210
|
+
const hunks = Array.isArray(entry?.hunks) ? entry.hunks : [];
|
|
211
|
+
let minStartIdx = 0;
|
|
212
|
+
for (const hunk of hunks) {
|
|
213
|
+
const match = findUnifiedHunkMatch(sourceLines, hunk, minStartIdx, fuzz);
|
|
214
|
+
if (!match) return hunk;
|
|
215
|
+
minStartIdx = Math.max(minStartIdx, match.end);
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function nativeFailurePathCandidates(parsed) {
|
|
221
|
+
const candidates = new Set();
|
|
222
|
+
for (const entry of Array.isArray(parsed) ? parsed : []) {
|
|
223
|
+
const kind = classifyEntry(entry);
|
|
224
|
+
const headerName = kind === 'create' ? entry.newFileName : entry.oldFileName;
|
|
225
|
+
if (!headerName) continue;
|
|
226
|
+
const stripped = stripDiffPrefix(headerName);
|
|
227
|
+
const display = normalizeOutputPath(stripped);
|
|
228
|
+
candidates.add(headerName);
|
|
229
|
+
candidates.add(stripped);
|
|
230
|
+
candidates.add(display);
|
|
231
|
+
if (display) {
|
|
232
|
+
candidates.add(`a/${display}`);
|
|
233
|
+
candidates.add(`b/${display}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return [...candidates].filter(Boolean).sort((a, b) => b.length - a.length);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function extractNativeFailurePath(message, parsed) {
|
|
240
|
+
const text = String(message || '').trim();
|
|
241
|
+
if (!text) return '';
|
|
242
|
+
const candidates = nativeFailurePathCandidates(parsed);
|
|
243
|
+
for (const candidate of candidates) {
|
|
244
|
+
if (text.startsWith(`${candidate}:`)) return candidate;
|
|
245
|
+
}
|
|
246
|
+
const hunkMatch = /(?:^|\b)hunk rejected in (.+?)(?: \(|$)/i.exec(text);
|
|
247
|
+
if (hunkMatch?.[1]) return hunkMatch[1].trim();
|
|
248
|
+
for (const candidate of candidates) {
|
|
249
|
+
if (text.includes(candidate)) return candidate;
|
|
250
|
+
}
|
|
251
|
+
return '';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function nativeFailureMatchesEntry(entry, failedPath) {
|
|
255
|
+
if (!failedPath) return true;
|
|
256
|
+
const kind = classifyEntry(entry);
|
|
257
|
+
const headerName = kind === 'create' ? entry.newFileName : entry.oldFileName;
|
|
258
|
+
if (!headerName) return false;
|
|
259
|
+
const failed = normalizeOutputPath(stripDiffPrefix(failedPath));
|
|
260
|
+
const display = normalizeOutputPath(stripDiffPrefix(headerName));
|
|
261
|
+
if (!failed || !display) return false;
|
|
262
|
+
if (failed === display) return true;
|
|
263
|
+
return display.endsWith(`/${failed}`) || failed.endsWith(`/${display}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// --- typographic normalization + line splitters ------------------------------
|
|
267
|
+
const RUST_WS = '\\u0009\\u000A\\u000B\\u000C\\u000D\\u0020\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000';
|
|
268
|
+
const RUST_TRIM_RE = new RegExp(`^[${RUST_WS}]+|[${RUST_WS}]+$`, 'g');
|
|
269
|
+
function rustTrim(s) {
|
|
270
|
+
return s.replace(RUST_TRIM_RE, '');
|
|
271
|
+
}
|
|
272
|
+
export function normalizeTypographic(s) {
|
|
273
|
+
return rustTrim(String(s ?? ''))
|
|
274
|
+
.replace(/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/g, '-')
|
|
275
|
+
.replace(/[\u2018\u2019\u201A\u201B]/g, "'")
|
|
276
|
+
.replace(/[\u201C\u201D\u201E\u201F]/g, '"')
|
|
277
|
+
.replace(/[\u00A0\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000]/g, ' ');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function splitTextLinesForPatch(text) {
|
|
281
|
+
const body = String(text ?? '').replace(/\r\n/g, '\n');
|
|
282
|
+
if (body.length === 0) {
|
|
283
|
+
const empty = [];
|
|
284
|
+
empty.hasFinalNewline = true;
|
|
285
|
+
return empty;
|
|
286
|
+
}
|
|
287
|
+
const lines = body.split('\n');
|
|
288
|
+
let hasFinalNewline = true;
|
|
289
|
+
if (lines[lines.length - 1] === '') lines.pop();
|
|
290
|
+
else hasFinalNewline = false;
|
|
291
|
+
lines.hasFinalNewline = hasFinalNewline;
|
|
292
|
+
return lines;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function splitBufferLinesForPatch(buf) {
|
|
296
|
+
const empty = [];
|
|
297
|
+
if (!buf || buf.length === 0) {
|
|
298
|
+
empty.hasFinalNewline = true;
|
|
299
|
+
return empty;
|
|
300
|
+
}
|
|
301
|
+
const lines = [];
|
|
302
|
+
let start = 0;
|
|
303
|
+
for (let i = 0; i < buf.length; i++) {
|
|
304
|
+
if (buf[i] === 0x0a) {
|
|
305
|
+
let end = i;
|
|
306
|
+
if (end > start && buf[end - 1] === 0x0d) end--;
|
|
307
|
+
lines.push(buf.subarray(start, end));
|
|
308
|
+
start = i + 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
let hasFinalNewline;
|
|
312
|
+
if (start === buf.length) {
|
|
313
|
+
hasFinalNewline = true;
|
|
314
|
+
} else {
|
|
315
|
+
lines.push(buf.subarray(start, buf.length));
|
|
316
|
+
hasFinalNewline = false;
|
|
317
|
+
}
|
|
318
|
+
lines.hasFinalNewline = hasFinalNewline;
|
|
319
|
+
return lines;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function longestCommonSubstringLen(a, b, cap = 4000) {
|
|
323
|
+
if (!a || !b) return 0;
|
|
324
|
+
const A = a.length > cap ? a.slice(0, cap) : a;
|
|
325
|
+
const B = b.length > cap ? b.slice(0, cap) : b;
|
|
326
|
+
const la = A.length;
|
|
327
|
+
const lb = B.length;
|
|
328
|
+
if (la === 0 || lb === 0) return 0;
|
|
329
|
+
let prev = new Int32Array(lb + 1);
|
|
330
|
+
let curr = new Int32Array(lb + 1);
|
|
331
|
+
let best = 0;
|
|
332
|
+
for (let i = 1; i <= la; i++) {
|
|
333
|
+
const ca = A.charCodeAt(i - 1);
|
|
334
|
+
for (let j = 1; j <= lb; j++) {
|
|
335
|
+
curr[j] = ca === B.charCodeAt(j - 1) ? prev[j - 1] + 1 : 0;
|
|
336
|
+
if (curr[j] > best) best = curr[j];
|
|
337
|
+
}
|
|
338
|
+
const tmp = prev; prev = curr; curr = tmp;
|
|
339
|
+
curr.fill(0);
|
|
340
|
+
}
|
|
341
|
+
return best;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function findLineSequence(lines, needle, fromLine, preferredLine = 0, options = {}) {
|
|
345
|
+
if (!Array.isArray(needle) || needle.length === 0) return Math.max(0, preferredLine || fromLine || 0);
|
|
346
|
+
const eof = options?.eof === true;
|
|
347
|
+
let minStart = Math.max(0, fromLine || 0);
|
|
348
|
+
if (eof && needle.length <= lines.length) {
|
|
349
|
+
minStart = Math.max(minStart, lines.length - needle.length);
|
|
350
|
+
}
|
|
351
|
+
const preferred = Math.max(0, preferredLine || 0);
|
|
352
|
+
const fuzzy = options && options.fuzzy === false ? false : true;
|
|
353
|
+
const tiers = fuzzy
|
|
354
|
+
? [
|
|
355
|
+
(a, b) => a === b,
|
|
356
|
+
(a, b) => a.replace(/\s+$/, '') === b.replace(/\s+$/, ''),
|
|
357
|
+
(a, b) => a.trim() === b.trim(),
|
|
358
|
+
(a, b) => a.replace(/\s+/g, ' ').trim() === b.replace(/\s+/g, ' ').trim(),
|
|
359
|
+
(a, b) => normalizeTypographic(a) === normalizeTypographic(b),
|
|
360
|
+
]
|
|
361
|
+
: [
|
|
362
|
+
(a, b) => a === b,
|
|
363
|
+
];
|
|
364
|
+
for (const eq of tiers) {
|
|
365
|
+
const starts = [];
|
|
366
|
+
for (let i = minStart; i <= lines.length - needle.length; i++) {
|
|
367
|
+
let ok = true;
|
|
368
|
+
for (let k = 0; k < needle.length; k++) {
|
|
369
|
+
if (!eq(lines[i + k], needle[k])) { ok = false; break; }
|
|
370
|
+
}
|
|
371
|
+
if (ok) starts.push(i);
|
|
372
|
+
}
|
|
373
|
+
if (starts.length) {
|
|
374
|
+
starts.sort((a, b) => Math.abs(a - preferred) - Math.abs(b - preferred) || a - b);
|
|
375
|
+
return starts[0];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (fuzzy && needle.length === 1) {
|
|
379
|
+
const want = String(needle[0] ?? '').replace(/\s+/g, ' ').trim();
|
|
380
|
+
if (want.length >= 40) {
|
|
381
|
+
const minLcs = Math.max(40, Math.floor(want.length / 2));
|
|
382
|
+
let bestIdx = -1;
|
|
383
|
+
let bestLcs = 0;
|
|
384
|
+
let bestTies = 0;
|
|
385
|
+
for (let i = minStart; i < lines.length; i++) {
|
|
386
|
+
const cand = String(lines[i] ?? '').replace(/\s+/g, ' ').trim();
|
|
387
|
+
if (cand.length === 0) continue;
|
|
388
|
+
const lcs = longestCommonSubstringLen(cand, want);
|
|
389
|
+
if (lcs < minLcs) continue;
|
|
390
|
+
if (lcs > bestLcs) { bestLcs = lcs; bestIdx = i; bestTies = 1; }
|
|
391
|
+
else if (lcs === bestLcs) { bestTies++; }
|
|
392
|
+
}
|
|
393
|
+
if (bestIdx >= 0 && bestTies === 1) return bestIdx;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return -1;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export function compactPatchPreviewLine(line, maxLen = 140) {
|
|
400
|
+
const text = String(line ?? '').replace(/\t/g, '\\t');
|
|
401
|
+
return text.length > maxLen ? `${text.slice(0, maxLen - 1)}…` : text;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function escapeNonAsciiForPatch(line) {
|
|
405
|
+
const s = String(line ?? '');
|
|
406
|
+
let out = '';
|
|
407
|
+
for (let i = 0; i < s.length; i++) {
|
|
408
|
+
const code = s.charCodeAt(i);
|
|
409
|
+
out += code > 0x7f
|
|
410
|
+
? String.fromCharCode(92) + 'u' + code.toString(16).padStart(4, '0')
|
|
411
|
+
: s[i];
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export function findLineSequenceEscapeEquiv(sourceLines, pattern, minStart, preferred) {
|
|
417
|
+
if (!pattern || pattern.length === 0) return -1;
|
|
418
|
+
const starts = [];
|
|
419
|
+
const from = Math.max(0, Number.isFinite(minStart) ? minStart : 0);
|
|
420
|
+
outer: for (let i = from; i + pattern.length <= sourceLines.length; i++) {
|
|
421
|
+
let usedEquiv = false;
|
|
422
|
+
for (let k = 0; k < pattern.length; k++) {
|
|
423
|
+
const pat = pattern[k];
|
|
424
|
+
const src = sourceLines[i + k];
|
|
425
|
+
if (src === pat) continue;
|
|
426
|
+
if (src === escapeNonAsciiForPatch(pat)) { usedEquiv = true; continue; }
|
|
427
|
+
continue outer;
|
|
428
|
+
}
|
|
429
|
+
if (usedEquiv) starts.push(i);
|
|
430
|
+
}
|
|
431
|
+
if (starts.length === 0) return -1;
|
|
432
|
+
const pref = Number.isFinite(preferred) && preferred >= 0 ? preferred : 0;
|
|
433
|
+
starts.sort((a, b) => Math.abs(a - pref) - Math.abs(b - pref) || a - b);
|
|
434
|
+
return starts[0];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function firstMeaningfulPatchLine(lines) {
|
|
438
|
+
return (lines || []).find((line) => String(line ?? '').trim().length > 0) || '';
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function scoreSimilarPatchLine(candidate, target) {
|
|
442
|
+
const cand = String(candidate ?? '').trim().replace(/\s+/g, ' ');
|
|
443
|
+
const want = String(target ?? '').trim().replace(/\s+/g, ' ');
|
|
444
|
+
if (!cand || !want) return 0;
|
|
445
|
+
if (cand === want) return 100000;
|
|
446
|
+
let score = 0;
|
|
447
|
+
const lcs = longestCommonSubstringLen(cand, want);
|
|
448
|
+
score += lcs * 20;
|
|
449
|
+
if (cand.includes(want) || want.includes(cand)) score += 5000 + Math.min(cand.length, want.length);
|
|
450
|
+
const words = new Set(want.split(/[^A-Za-z0-9_$]+/).filter((word) => word.length > 1));
|
|
451
|
+
for (const word of words) {
|
|
452
|
+
if (cand.includes(word)) score += Math.min(200, word.length * 12);
|
|
453
|
+
}
|
|
454
|
+
if (Math.max(cand.length, want.length) < 80) {
|
|
455
|
+
score -= Math.abs(cand.length - want.length);
|
|
456
|
+
}
|
|
457
|
+
return score;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export function nearestPatchLineHint(sourceLines, expectedLine, preferredLine) {
|
|
461
|
+
const expected = String(expectedLine || '');
|
|
462
|
+
if (!expected.trim()) return '';
|
|
463
|
+
let best = null;
|
|
464
|
+
const preferred = Number.isFinite(preferredLine) && preferredLine >= 0 ? preferredLine : 0;
|
|
465
|
+
for (let i = 0; i < sourceLines.length; i++) {
|
|
466
|
+
const score = scoreSimilarPatchLine(sourceLines[i], expected) - (Math.abs(i - preferred) * 0.01);
|
|
467
|
+
if (!best || score > best.score) best = { score, index: i, line: sourceLines[i] };
|
|
468
|
+
}
|
|
469
|
+
if (!best || best.score <= 0) return '';
|
|
470
|
+
return `nearest line ${best.index + 1}: ${JSON.stringify(compactPatchPreviewLine(best.line))}`;
|
|
471
|
+
}
|