mixdog 0.7.1
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/.claude-plugin/marketplace.json +31 -0
- package/.claude-plugin/plugin.json +20 -0
- package/.gitattributes +34 -0
- package/.mcp.json +14 -0
- package/ARCHITECTURE.md +77 -0
- package/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +45 -0
- package/DATA-FLOW.md +79 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/SECURITY.md +138 -0
- package/UNINSTALL.md +112 -0
- package/agents/maintenance.md +5 -0
- package/agents/memory-classification.md +30 -0
- package/agents/scheduler-task.md +18 -0
- package/agents/webhook-handler.md +27 -0
- package/agents/worker.md +24 -0
- package/bin/bridge +133 -0
- package/bin/statusline-launcher.mjs +78 -0
- package/bin/statusline-lib.mjs +550 -0
- package/bin/statusline.mjs +607 -0
- package/bun.lock +802 -0
- package/commands/config.md +16 -0
- package/commands/doctor.md +13 -0
- package/commands/setup.md +17 -0
- package/defaults/cycle3-review-prompt.md +90 -0
- package/defaults/hidden-roles.json +65 -0
- package/defaults/memory-chunk-prompt.md +63 -0
- package/defaults/memory-promote-prompt.md +135 -0
- package/defaults/mixdog-config.template.json +27 -0
- package/defaults/user-workflow.json +8 -0
- package/defaults/user-workflow.md +12 -0
- package/hooks/hooks.json +73 -0
- package/hooks/lib/active-instance.cjs +77 -0
- package/hooks/lib/permission-evaluator.cjs +411 -0
- package/hooks/lib/permission-route.cjs +63 -0
- package/hooks/lib/permission-rules.cjs +170 -0
- package/hooks/lib/settings-loader.cjs +116 -0
- package/hooks/post-tool-use.cjs +84 -0
- package/hooks/pre-mcp-sandbox.cjs +158 -0
- package/hooks/pre-tool-subagent.cjs +253 -0
- package/hooks/session-start.cjs +1372 -0
- package/hooks/turn-timer.cjs +82 -0
- package/lib/claude-md-writer.cjs +386 -0
- package/lib/config-cjs.cjs +61 -0
- package/lib/hook-pipe-path.cjs +10 -0
- package/lib/keychain-cjs.cjs +263 -0
- package/lib/plugin-paths.cjs +61 -0
- package/lib/rules-builder.cjs +241 -0
- package/lib/text-utils.cjs +61 -0
- package/native/README.md +117 -0
- package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
- package/package.json +107 -0
- package/prompts/code-review.txt +16 -0
- package/prompts/security-audit.txt +17 -0
- package/rules/bridge/00-common.md +39 -0
- package/rules/bridge/20-skip-protocol.md +18 -0
- package/rules/bridge/30-explorer.md +33 -0
- package/rules/bridge/40-cycle1-agent.md +52 -0
- package/rules/bridge/41-cycle2-agent.md +62 -0
- package/rules/bridge/42-cycle3-agent.md +44 -0
- package/rules/lead/00-tool-lead.md +61 -0
- package/rules/lead/01-general.md +23 -0
- package/rules/lead/02-channels.md +49 -0
- package/rules/lead/03-team.md +27 -0
- package/rules/lead/04-workflow.md +20 -0
- package/rules/shared/00-language.md +14 -0
- package/rules/shared/01-tool.md +138 -0
- package/scripts/bootstrap.mjs +184 -0
- package/scripts/bridge-unify-smoke.mjs +308 -0
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/builtin-utils-smoke.mjs +392 -0
- package/scripts/check-json.mjs +45 -0
- package/scripts/check-syntax-changed.mjs +102 -0
- package/scripts/check-syntax.mjs +58 -0
- package/scripts/code-graph-batch.test.mjs +33 -0
- package/scripts/config-preserve-smoke.mjs +180 -0
- package/scripts/doctor.mjs +484 -0
- package/scripts/edit-normalize-fuzz.mjs +130 -0
- package/scripts/edit-normalize-smoke.mjs +401 -0
- package/scripts/edit-operation-smoke.mjs +369 -0
- package/scripts/edit2-smoke.mjs +63 -0
- package/scripts/fuzzy-e2e.mjs +28 -0
- package/scripts/fuzzy-smoke.mjs +26 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/guard-smoke.mjs +66 -0
- package/scripts/hidden-role-schema-smoke.mjs +162 -0
- package/scripts/hook-routing-smoke.mjs +29 -0
- package/scripts/inject-input.ps1 +204 -0
- package/scripts/io-complex-smoke.mjs +667 -0
- package/scripts/io-explore-bench.mjs +424 -0
- package/scripts/io-guardrails-smoke.mjs +205 -0
- package/scripts/io-mini-bench-baseline.json +11 -0
- package/scripts/io-mini-bench.mjs +216 -0
- package/scripts/io-route-harness.mjs +933 -0
- package/scripts/io-telemetry-report.mjs +691 -0
- package/scripts/mutation-bench.mjs +564 -0
- package/scripts/mutation-io-smoke.mjs +1081 -0
- package/scripts/native-patch-bridge-smoke.mjs +288 -0
- package/scripts/native-patch-smoke.mjs +304 -0
- package/scripts/patch-interior-context-smoke.mjs +49 -0
- package/scripts/patch-newline-utf8-smoke.mjs +157 -0
- package/scripts/perf-hook-smoke.mjs +71 -0
- package/scripts/permission-eval-smoke.mjs +426 -0
- package/scripts/prep-patch.mjs +53 -0
- package/scripts/prep-shim.mjs +96 -0
- package/scripts/provider-cache-smoke.mjs +687 -0
- package/scripts/report-runtime-health.mjs +132 -0
- package/scripts/run-mcp.mjs +1547 -0
- package/scripts/salvage-v4a-shatter.test.mjs +58 -0
- package/scripts/scoped-cache-io-smoke.mjs +103 -0
- package/scripts/shell-policy-round3-smoke.mjs +46 -0
- package/scripts/smoke-runtime-negative.ps1 +100 -0
- package/scripts/smoke-runtime-negative.sh +95 -0
- package/scripts/stall-policy-smoke.mjs +50 -0
- package/scripts/start-memory-worker.mjs +23 -0
- package/scripts/statusline-launcher-smoke.mjs +82 -0
- package/scripts/stress-atomic-write.mjs +1028 -0
- package/scripts/test-config-rmw-restore.mjs +122 -0
- package/scripts/test-fault-inject.mjs +164 -0
- package/scripts/test-large-file.mjs +174 -0
- package/scripts/tool-edge-smoke.mjs +209 -0
- package/scripts/uninstall.mjs +201 -0
- package/scripts/webhook-selfheal-smoke.mjs +29 -0
- package/scripts/write-overwrite-guard-smoke.mjs +56 -0
- package/server-main.mjs +3055 -0
- package/server.mjs +468 -0
- package/setup/config-merge.mjs +254 -0
- package/setup/install.mjs +120 -0
- package/setup/launch-core.mjs +507 -0
- package/setup/launch.mjs +101 -0
- package/setup/setup-server.mjs +3206 -0
- package/setup/setup.html +3693 -0
- package/skills/retro-skill-proposer/SKILL.md +92 -0
- package/skills/schedule-add/SKILL.md +77 -0
- package/skills/setup/SKILL.md +346 -0
- package/skills/webhook-add/SKILL.md +81 -0
- package/src/agent/bridge-stall-watchdog.mjs +337 -0
- package/src/agent/index.mjs +2138 -0
- package/src/agent/orchestrator/activity-bus.mjs +38 -0
- package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
- package/src/agent/orchestrator/bridge-retry.mjs +220 -0
- package/src/agent/orchestrator/bridge-trace.mjs +583 -0
- package/src/agent/orchestrator/cache-mtime.mjs +58 -0
- package/src/agent/orchestrator/config.mjs +358 -0
- package/src/agent/orchestrator/context/collect.mjs +651 -0
- package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
- package/src/agent/orchestrator/drain-registry.mjs +50 -0
- package/src/agent/orchestrator/explore-validator.mjs +8 -0
- package/src/agent/orchestrator/internal-roles.mjs +118 -0
- package/src/agent/orchestrator/internal-tools.mjs +88 -0
- package/src/agent/orchestrator/jobs.mjs +116 -0
- package/src/agent/orchestrator/mcp/client.mjs +364 -0
- package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
- package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
- package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
- package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
- package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
- package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
- package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
- package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
- package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
- package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
- package/src/agent/orchestrator/providers/registry.mjs +192 -0
- package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
- package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
- package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
- package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
- package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
- package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
- package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
- package/src/agent/orchestrator/session/cache/util.mjs +49 -0
- package/src/agent/orchestrator/session/loop.mjs +1478 -0
- package/src/agent/orchestrator/session/manager.mjs +1975 -0
- package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
- package/src/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
- package/src/agent/orchestrator/session/store.mjs +624 -0
- package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
- package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
- package/src/agent/orchestrator/session/trim.mjs +491 -0
- package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
- package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
- package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
- package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
- package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
- package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
- package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
- package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
- package/src/agent/orchestrator/stall-policy.mjs +195 -0
- package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
- package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
- package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
- package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
- package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
- package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
- package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
- package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
- package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
- package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
- package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
- package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
- package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
- package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
- package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
- package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
- package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
- package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
- package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
- package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
- package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
- package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
- package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
- package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
- package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
- package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
- package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
- package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
- package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
- package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
- package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
- package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
- package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
- package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
- package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
- package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
- package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
- package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
- package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
- package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
- package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
- package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
- package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
- package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
- package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
- package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
- package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
- package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
- package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
- package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
- package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
- package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
- package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
- package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
- package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
- package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
- package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
- package/src/agent/orchestrator/tools/builtin.mjs +500 -0
- package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
- package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
- package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
- package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
- package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
- package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
- package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
- package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
- package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
- package/src/agent/orchestrator/tools/host-input.mjs +204 -0
- package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
- package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
- package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
- package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
- package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
- package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
- package/src/agent/orchestrator/tools/patch.mjs +2754 -0
- package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
- package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
- package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
- package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
- package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
- package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
- package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
- package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
- package/src/agent/orchestrator/workflow-store.mjs +93 -0
- package/src/agent/tool-defs.mjs +103 -0
- package/src/channels/backends/discord.mjs +784 -0
- package/src/channels/data/voice-runtime-manifest.json +138 -0
- package/src/channels/index.mjs +3229 -0
- package/src/channels/lib/cli-worker-host.mjs +12 -0
- package/src/channels/lib/config-lock.mjs +13 -0
- package/src/channels/lib/config.mjs +292 -0
- package/src/channels/lib/drop-trace.mjs +71 -0
- package/src/channels/lib/event-pipeline.mjs +81 -0
- package/src/channels/lib/event-queue.mjs +345 -0
- package/src/channels/lib/executor.mjs +168 -0
- package/src/channels/lib/format.mjs +188 -0
- package/src/channels/lib/holidays.mjs +138 -0
- package/src/channels/lib/hook-pipe-server.mjs +802 -0
- package/src/channels/lib/interaction-workflows.mjs +184 -0
- package/src/channels/lib/memory-client.mjs +149 -0
- package/src/channels/lib/output-forwarder.mjs +765 -0
- package/src/channels/lib/runtime-paths.mjs +479 -0
- package/src/channels/lib/scheduler.mjs +723 -0
- package/src/channels/lib/session-control.mjs +36 -0
- package/src/channels/lib/session-discovery.mjs +103 -0
- package/src/channels/lib/settings.mjs +11 -0
- package/src/channels/lib/state-file.mjs +68 -0
- package/src/channels/lib/status-snapshot.mjs +219 -0
- package/src/channels/lib/tool-format.mjs +140 -0
- package/src/channels/lib/transcript-discovery.mjs +195 -0
- package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
- package/src/channels/lib/webhook.mjs +1179 -0
- package/src/channels/lib/whisper-server.mjs +477 -0
- package/src/channels/tool-defs.mjs +170 -0
- package/src/daemon/host.mjs +118 -0
- package/src/daemon/mcp-transport.mjs +47 -0
- package/src/daemon/session.mjs +100 -0
- package/src/daemon/thin-client.mjs +71 -0
- package/src/daemon/transport.mjs +163 -0
- package/src/memory/data/runtime-manifest.json +40 -0
- package/src/memory/index.mjs +3305 -0
- package/src/memory/lib/agent-ipc.mjs +93 -0
- package/src/memory/lib/bridge-trace-queries.mjs +120 -0
- package/src/memory/lib/core-memory-store.mjs +330 -0
- package/src/memory/lib/embedding-provider.mjs +269 -0
- package/src/memory/lib/embedding-worker.mjs +323 -0
- package/src/memory/lib/llm-worker-host.mjs +17 -0
- package/src/memory/lib/memory-cycle.mjs +11 -0
- package/src/memory/lib/memory-cycle1.mjs +641 -0
- package/src/memory/lib/memory-cycle2.mjs +1284 -0
- package/src/memory/lib/memory-cycle3.mjs +540 -0
- package/src/memory/lib/memory-embed.mjs +299 -0
- package/src/memory/lib/memory-extraction.mjs +5 -0
- package/src/memory/lib/memory-maintenance-store.mjs +32 -0
- package/src/memory/lib/memory-ops-policy.mjs +190 -0
- package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
- package/src/memory/lib/memory-recall-read-query.mjs +7 -0
- package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
- package/src/memory/lib/memory-recall-store.mjs +621 -0
- package/src/memory/lib/memory-retrievers.mjs +112 -0
- package/src/memory/lib/memory-score.mjs +71 -0
- package/src/memory/lib/memory-text-utils.mjs +58 -0
- package/src/memory/lib/memory.mjs +412 -0
- package/src/memory/lib/model-profile.mjs +85 -0
- package/src/memory/lib/pg/adapter.mjs +308 -0
- package/src/memory/lib/pg/process.mjs +360 -0
- package/src/memory/lib/pg/supervisor.mjs +396 -0
- package/src/memory/lib/project-id-resolver.mjs +86 -0
- package/src/memory/lib/runtime-fetcher.mjs +442 -0
- package/src/memory/lib/trace-store.mjs +728 -0
- package/src/memory/tool-defs.mjs +79 -0
- package/src/search/index.mjs +1173 -0
- package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
- package/src/search/lib/backends/exa.mjs +50 -0
- package/src/search/lib/backends/firecrawl.mjs +61 -0
- package/src/search/lib/backends/gemini-api.mjs +83 -0
- package/src/search/lib/backends/grok-oauth.mjs +86 -0
- package/src/search/lib/backends/index.mjs +150 -0
- package/src/search/lib/backends/openai-api.mjs +144 -0
- package/src/search/lib/backends/openai-oauth.mjs +98 -0
- package/src/search/lib/backends/openai-web-search.mjs +76 -0
- package/src/search/lib/backends/tavily.mjs +55 -0
- package/src/search/lib/backends/xai-api.mjs +113 -0
- package/src/search/lib/cache.mjs +131 -0
- package/src/search/lib/config.mjs +192 -0
- package/src/search/lib/formatter.mjs +115 -0
- package/src/search/lib/provider-usage.mjs +67 -0
- package/src/search/lib/providers.mjs +47 -0
- package/src/search/lib/search-intent.mjs +109 -0
- package/src/search/lib/setup-handler.mjs +261 -0
- package/src/search/lib/state.mjs +201 -0
- package/src/search/lib/web-tools.mjs +1207 -0
- package/src/search/tool-defs.mjs +83 -0
- package/src/setup/defender-exclusion.mjs +183 -0
- package/src/shared/abort-controller.mjs +15 -0
- package/src/shared/atomic-file.mjs +420 -0
- package/src/shared/config.mjs +350 -0
- package/src/shared/daemon-recycle.mjs +108 -0
- package/src/shared/disable-claude-builtins.mjs +88 -0
- package/src/shared/err-text.mjs +12 -0
- package/src/shared/llm/cost.mjs +66 -0
- package/src/shared/llm/http-agent.mjs +123 -0
- package/src/shared/llm/index.mjs +41 -0
- package/src/shared/llm/pid-cleanup.mjs +27 -0
- package/src/shared/llm/usage-log.mjs +47 -0
- package/src/shared/plugin-paths.mjs +58 -0
- package/src/shared/schedules-store.mjs +70 -0
- package/src/shared/seed.mjs +119 -0
- package/src/shared/user-cwd.mjs +213 -0
- package/src/shared/user-data-guard.mjs +238 -0
- package/src/status/aggregator.mjs +584 -0
- package/src/status/server.mjs +413 -0
- package/tools.json +1653 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Fuzz test: _findActualString invariants under random drift.
|
|
2
|
+
//
|
|
3
|
+
// Approach: pick a random substring of randomly-generated content, apply
|
|
4
|
+
// one of several deterministic drift transforms (NFC/NFD swap, codepoint
|
|
5
|
+
// fold variants, trailing-whitespace injection, indent shift, line-end
|
|
6
|
+
// drop) to the substring, and feed it through _findActualString.
|
|
7
|
+
//
|
|
8
|
+
// Invariants checked when the matcher returns a non-null slice:
|
|
9
|
+
// • returned string IS a substring of `content` (content.includes(slice))
|
|
10
|
+
// • content.indexOf(slice) finds a position (no synthesised bytes)
|
|
11
|
+
// • slice.normalize('NFD').indexOf(searchAfterDriftNfd) === 0 or close
|
|
12
|
+
// — the matched slice's NFD form contains the search's NFD form at
|
|
13
|
+
// the start, confirming the matcher did not pick something unrelated
|
|
14
|
+
//
|
|
15
|
+
// Seed: a deterministic LCG so the same DEV_FUZZ_SEED env reproduces
|
|
16
|
+
// failures bit-for-bit.
|
|
17
|
+
|
|
18
|
+
import { findActualString as _findActualString } from '../src/agent/orchestrator/tools/edit-normalize.mjs';
|
|
19
|
+
|
|
20
|
+
const ITERATIONS = Number(process.env.DEV_FUZZ_ITER) || 1000;
|
|
21
|
+
const SEED = Number(process.env.DEV_FUZZ_SEED) || 0xC0DE1234;
|
|
22
|
+
|
|
23
|
+
// LCG PRNG for reproducible fuzz.
|
|
24
|
+
let _state = SEED >>> 0;
|
|
25
|
+
function rand() {
|
|
26
|
+
_state = (_state * 1664525 + 1013904223) >>> 0;
|
|
27
|
+
return _state / 0x100000000;
|
|
28
|
+
}
|
|
29
|
+
function randInt(min, max) {
|
|
30
|
+
return min + Math.floor(rand() * (max - min + 1));
|
|
31
|
+
}
|
|
32
|
+
function pick(arr) {
|
|
33
|
+
return arr[Math.floor(rand() * arr.length)];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ASCII = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_(){}[],.;:!? \t\n';
|
|
37
|
+
const HANGUL = '가나다라마바사아자차카타파하각간갈감갑값나라말사람';
|
|
38
|
+
const FOLD_VARIANTS = ' ‐–—―−-‘’“”"'';
|
|
39
|
+
|
|
40
|
+
function randomChar() {
|
|
41
|
+
const r = rand();
|
|
42
|
+
if (r < 0.70) return ASCII[Math.floor(rand() * ASCII.length)];
|
|
43
|
+
if (r < 0.85) return HANGUL[Math.floor(rand() * HANGUL.length)];
|
|
44
|
+
return FOLD_VARIANTS[Math.floor(rand() * FOLD_VARIANTS.length)];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function randomContent() {
|
|
48
|
+
const len = randInt(80, 300);
|
|
49
|
+
let out = '';
|
|
50
|
+
for (let i = 0; i < len; i++) out += randomChar();
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Drift transforms — each takes a slice and produces a possibly different
|
|
55
|
+
// representation. The matcher must still locate the original slice.
|
|
56
|
+
const drifts = [
|
|
57
|
+
(s) => s, // identity
|
|
58
|
+
(s) => s.normalize('NFC'), // NFC
|
|
59
|
+
(s) => s.normalize('NFD'), // NFD
|
|
60
|
+
(s) => s.replace(/-/g, '—'), // hyphen → em dash
|
|
61
|
+
(s) => s.replace(/'/g, '‘'), // straight → curly single
|
|
62
|
+
(s) => s.replace(/"/g, '“'), // straight → curly double
|
|
63
|
+
(s) => s.replace(/ /g, ' '), // space → NBSP
|
|
64
|
+
(s) => s.replace(/—/g, '-'), // em dash → hyphen
|
|
65
|
+
(s) => s.replace(/‘/g, "'"), // curly → straight
|
|
66
|
+
(s) => s.replace(/“/g, '"'), // curly → straight
|
|
67
|
+
(s) => s.replace(/ /g, ' '), // NBSP → space
|
|
68
|
+
(s) => s.split('\n').map((l) => l + ' ').join('\n'), // add trailing spaces
|
|
69
|
+
(s) => s.split('\n').map((l) => l.replace(/ +$/, '')).join('\n'), // strip trailing
|
|
70
|
+
(s) => s.split('\n').map((l) => ' ' + l).join('\n'), // add leading indent
|
|
71
|
+
(s) => s.split('\n').map((l) => l.replace(/^ +/, '')).join('\n'), // strip leading
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
let matched = 0;
|
|
75
|
+
let unmatched = 0;
|
|
76
|
+
let invariantFails = 0;
|
|
77
|
+
const failSamples = [];
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < ITERATIONS; i++) {
|
|
80
|
+
const content = randomContent();
|
|
81
|
+
const start = randInt(0, Math.max(0, content.length - 10));
|
|
82
|
+
const len = randInt(3, Math.min(60, content.length - start));
|
|
83
|
+
const original = content.substring(start, start + len);
|
|
84
|
+
if (original.length < 2) continue;
|
|
85
|
+
const drift = pick(drifts);
|
|
86
|
+
const search = drift(original);
|
|
87
|
+
const info = {};
|
|
88
|
+
let result;
|
|
89
|
+
try {
|
|
90
|
+
result = _findActualString(content, search, info);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
invariantFails++;
|
|
93
|
+
failSamples.push({ reason: 'throw', err: String(err), content, search });
|
|
94
|
+
if (failSamples.length > 5) break;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (result === null) {
|
|
98
|
+
unmatched++;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
matched++;
|
|
102
|
+
if (typeof result !== 'string') {
|
|
103
|
+
invariantFails++;
|
|
104
|
+
failSamples.push({ reason: 'non-string', result, content, search });
|
|
105
|
+
if (failSamples.length > 5) break;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (!content.includes(result)) {
|
|
109
|
+
invariantFails++;
|
|
110
|
+
failSamples.push({ reason: 'not-substring', result, content, search });
|
|
111
|
+
if (failSamples.length > 5) break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`edit-normalize-fuzz: ${ITERATIONS} iterations (seed=0x${SEED.toString(16).toUpperCase()})`);
|
|
116
|
+
console.log(` matched: ${matched}`);
|
|
117
|
+
console.log(` unmatched: ${unmatched}`);
|
|
118
|
+
console.log(` invariant fails: ${invariantFails}`);
|
|
119
|
+
|
|
120
|
+
if (invariantFails > 0) {
|
|
121
|
+
console.error('\nFAIL samples:');
|
|
122
|
+
for (const s of failSamples) {
|
|
123
|
+
console.error(` reason: ${s.reason}`);
|
|
124
|
+
console.error(` content: ${JSON.stringify(s.content).slice(0, 200)}`);
|
|
125
|
+
console.error(` search: ${JSON.stringify(s.search).slice(0, 200)}`);
|
|
126
|
+
if (s.result !== undefined) console.error(` result: ${JSON.stringify(s.result).slice(0, 200)}`);
|
|
127
|
+
if (s.err) console.error(` err: ${s.err}`);
|
|
128
|
+
}
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
// Smoke test: edit-normalize.mjs edit-input normalization helpers.
|
|
2
|
+
//
|
|
3
|
+
// Validates the invariant-safe matching helpers extracted live from the
|
|
4
|
+
// implementation so the smoke stays in sync:
|
|
5
|
+
// • _normalizeForMatch — curly-quote → straight fold (length-preserving).
|
|
6
|
+
// • _nfcFoldMatch — NFC/NFD canonical matcher (byte-exact slice).
|
|
7
|
+
// • _stripTrailingWhitespacePerLine — insert-side trailing space/tab strip
|
|
8
|
+
// with line-terminator preservation.
|
|
9
|
+
// • _preserveQuoteTypography — heuristic insert-side typography helper.
|
|
10
|
+
//
|
|
11
|
+
// Last-resort rstrip-fold / indent-fold / eol-fold tiers are enabled with
|
|
12
|
+
// per-tier ambiguity rejection (diagnoseFoldTierAmbiguity). Case, dash,
|
|
13
|
+
// fullwidth and Unicode-space folding remain removed.
|
|
14
|
+
//
|
|
15
|
+
// Invariants:
|
|
16
|
+
// 1. Every folded codepoint is a single UTF-16 unit (BMP only).
|
|
17
|
+
// 2. Fold preserves input.length so indexOf hits re-index original bytes.
|
|
18
|
+
// 3. nfc-fold returns content.substring(start, end) — never synthesised.
|
|
19
|
+
// 4. Trailing-whitespace strip preserves LF / CRLF / lone CR byte-exact.
|
|
20
|
+
// 5. Non-curly codepoints (dash, fullwidth, space, ZWSP, BOM, ASCII)
|
|
21
|
+
// never match the fold.
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
normalizeForMatch as _normalizeForMatch,
|
|
25
|
+
nfcFoldMatch as _nfcFoldMatch,
|
|
26
|
+
findActualString as _findActualString,
|
|
27
|
+
diagnoseFoldTierAmbiguity as _diagnoseFoldTierAmbiguity,
|
|
28
|
+
diagnoseEolFoldAmbiguity as _diagnoseEolFoldAmbiguity,
|
|
29
|
+
stripTrailingWhitespacePerLine as _stripTrailingWhitespacePerLine,
|
|
30
|
+
preserveQuoteTypography as _preserveQuoteTypography,
|
|
31
|
+
} from '../src/agent/orchestrator/tools/edit-normalize.mjs';
|
|
32
|
+
|
|
33
|
+
let failures = 0;
|
|
34
|
+
function fail(name, msg) { console.error(`FAIL ${name}: ${msg}`); failures++; }
|
|
35
|
+
|
|
36
|
+
// ── Section 1: fold codepoint matrix (curly quotes ONLY) ───────────────────
|
|
37
|
+
const matchCases = [
|
|
38
|
+
['U+2018 left single quote', '\u2018', "'"],
|
|
39
|
+
['U+2019 right single quote', '\u2019', "'"],
|
|
40
|
+
['U+201A single low-9', '\u201A', "'"],
|
|
41
|
+
['U+201B single high-rev-9', '\u201B', "'"],
|
|
42
|
+
['U+201C left double quote', '\u201C', '"'],
|
|
43
|
+
['U+201D right double quote', '\u201D', '"'],
|
|
44
|
+
['U+201E double low-9', '\u201E', '"'],
|
|
45
|
+
['U+201F double high-rev-9', '\u201F', '"'],
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// Heuristic-risky folds intentionally removed: these must pass through
|
|
49
|
+
// UNCHANGED now (each could map genuinely-different text to the same view).
|
|
50
|
+
const skipCases = [
|
|
51
|
+
['U+2010 hyphen', '\u2010'],
|
|
52
|
+
['U+2013 en dash', '\u2013'],
|
|
53
|
+
['U+2014 em dash', '\u2014'],
|
|
54
|
+
['U+2015 horizontal bar', '\u2015'],
|
|
55
|
+
['U+2212 minus', '\u2212'],
|
|
56
|
+
['U+FF0D fullwidth hyphen-minus', '\uFF0D'],
|
|
57
|
+
['U+FF07 fullwidth apostrophe', '\uFF07'],
|
|
58
|
+
['U+FF02 fullwidth quote', '\uFF02'],
|
|
59
|
+
['U+00A0 NBSP', '\u00A0'],
|
|
60
|
+
['U+2003 em space', '\u2003'],
|
|
61
|
+
['U+202F narrow NBSP', '\u202F'],
|
|
62
|
+
['U+205F medium math space', '\u205F'],
|
|
63
|
+
['U+3000 ideographic space', '\u3000'],
|
|
64
|
+
['U+200B ZWSP', '\u200B'],
|
|
65
|
+
['U+FEFF BOM', '\uFEFF'],
|
|
66
|
+
['U+0020 ASCII space', ' '],
|
|
67
|
+
['U+002D ASCII hyphen', '-'],
|
|
68
|
+
['U+0027 ASCII apos', "'"],
|
|
69
|
+
['U+0022 ASCII quote', '"'],
|
|
70
|
+
['U+0041 ASCII A', 'A'],
|
|
71
|
+
['U+AC00 Hangul GA', '\uAC00'],
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const [name, input, expected] of matchCases) {
|
|
75
|
+
if (input.length !== 1) { fail(name, `single UTF-16 unit expected, got length ${input.length}`); continue; }
|
|
76
|
+
const folded = _normalizeForMatch(input);
|
|
77
|
+
if (folded !== expected) { fail(name, `folded to ${JSON.stringify(folded)}, expected ${JSON.stringify(expected)}`); continue; }
|
|
78
|
+
if (folded.length !== input.length) { fail(name, `length drift (${input.length} -> ${folded.length})`); continue; }
|
|
79
|
+
}
|
|
80
|
+
for (const [name, input] of skipCases) {
|
|
81
|
+
const folded = _normalizeForMatch(input);
|
|
82
|
+
if (folded !== input) { fail(name, `should not be folded, got ${JSON.stringify(folded)}`); }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Mixed string: curly quotes fold, dash + ideographic space stay verbatim.
|
|
86
|
+
const mixed = 'foo\u2014bar baz\u2018qux\u2019\u3000end';
|
|
87
|
+
const mixedFolded = _normalizeForMatch(mixed);
|
|
88
|
+
if (mixedFolded !== 'foo\u2014bar baz\'qux\'\u3000end') fail('mixed-fold', `got ${JSON.stringify(mixedFolded)}`);
|
|
89
|
+
if (mixedFolded.length !== mixed.length) fail('mixed-fold-length', `${mixed.length} -> ${mixedFolded.length}`);
|
|
90
|
+
|
|
91
|
+
// ── Section 2: findActualString pipeline (exact -> fold -> nfc) ─────────────
|
|
92
|
+
// Curly-quote fold tier still recovers a unique straight-vs-curly mismatch.
|
|
93
|
+
{
|
|
94
|
+
const content = 'the value is \u201Chello\u201D today\n';
|
|
95
|
+
const search = 'the value is "hello" today';
|
|
96
|
+
const info = {};
|
|
97
|
+
const slice = _findActualString(content, search, info);
|
|
98
|
+
if (slice === null) fail('pipeline:curly-fold', 'expected a fold match, got null');
|
|
99
|
+
else if (info.stage !== 'fold') fail('pipeline:curly-fold', `expected stage fold, got ${info.stage}`);
|
|
100
|
+
else if (!content.includes(slice)) fail('pipeline:curly-fold', 'returned slice is NOT a substring of content');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fold-tier uniqueness gate: two curly variants of the same straight search
|
|
104
|
+
// must collapse to null rather than silently picking the first.
|
|
105
|
+
{
|
|
106
|
+
const content = 'say \u2018hi\u2019 / say \u201Bhi\u2019\n';
|
|
107
|
+
const search = "say 'hi'";
|
|
108
|
+
const info = {};
|
|
109
|
+
const slice = _findActualString(content, search, info);
|
|
110
|
+
if (slice !== null) fail('pipeline:fold-multi-match-rejected', `expected null, got ${JSON.stringify(slice)} (stage=${info.stage})`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// rstrip-fold: unique trailing-whitespace drift on a line body.
|
|
114
|
+
{
|
|
115
|
+
const content = 'header\nfoo \nbar\nfooter\n';
|
|
116
|
+
const search = 'foo\nbar';
|
|
117
|
+
const info = {};
|
|
118
|
+
const slice = _findActualString(content, search, info);
|
|
119
|
+
if (slice === null) fail('pipeline:rstrip-fold', 'expected unique match');
|
|
120
|
+
else if (info.stage !== 'rstrip-fold') fail('pipeline:rstrip-fold', `stage ${info.stage}`);
|
|
121
|
+
else if (slice !== 'foo \nbar') fail('pipeline:rstrip-fold', `slice ${JSON.stringify(slice)}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// indent-fold: leading tabs in file vs spaces in search (CC tab width 2).
|
|
125
|
+
{
|
|
126
|
+
const content = 'header\n\tif (x) {\n\t\ty;\n\t}\nfooter\n';
|
|
127
|
+
const search = ' if (x) {\n y;\n }';
|
|
128
|
+
const info = {};
|
|
129
|
+
const slice = _findActualString(content, search, info);
|
|
130
|
+
if (slice === null) fail('pipeline:indent-fold', 'expected unique match');
|
|
131
|
+
else if (info.stage !== 'indent-fold') fail('pipeline:indent-fold', `stage ${info.stage}`);
|
|
132
|
+
else if (!content.includes(slice)) fail('pipeline:indent-fold', 'slice not substring');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// eol-fold: search has trailing newline; file slice does not.
|
|
136
|
+
{
|
|
137
|
+
const content = 'alpha';
|
|
138
|
+
const search = 'alpha\n';
|
|
139
|
+
const info = {};
|
|
140
|
+
const slice = _findActualString(content, search, info);
|
|
141
|
+
if (slice === null) fail('pipeline:eol-fold', 'expected match');
|
|
142
|
+
else if (info.stage !== 'eol-fold') fail('pipeline:eol-fold', `stage ${info.stage}`);
|
|
143
|
+
else if (slice !== 'alpha') fail('pipeline:eol-fold', `got ${JSON.stringify(slice)}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// eol-fold must not match bare text mid-line.
|
|
147
|
+
{
|
|
148
|
+
const content = 'prefix alpha suffix\n';
|
|
149
|
+
const search = 'alpha\n';
|
|
150
|
+
const slice = _findActualString(content, search, {});
|
|
151
|
+
if (slice !== null) fail('pipeline:eol-fold-midline-reject', JSON.stringify(slice));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// crlf-fold wins over rstrip-fold when both sites exist.
|
|
155
|
+
{
|
|
156
|
+
const content = 'foo \nbar\nzzz\nfoo\r\nbar\n';
|
|
157
|
+
const search = 'foo\nbar';
|
|
158
|
+
const info = {};
|
|
159
|
+
const slice = _findActualString(content, search, info);
|
|
160
|
+
if (slice === null) fail('pipeline:crlf-beats-rstrip', 'expected crlf site');
|
|
161
|
+
else if (info.stage !== 'crlf-fold') fail('pipeline:crlf-beats-rstrip', `stage ${info.stage}`);
|
|
162
|
+
else if (slice !== 'foo\r\nbar') fail('pipeline:crlf-beats-rstrip', `slice ${JSON.stringify(slice)}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Ambiguity: two rstrip-fold sites → diagnose code 9 payload.
|
|
166
|
+
{
|
|
167
|
+
const content = 'foo \nbar\nzzz\nfoo \nbar\n';
|
|
168
|
+
const search = 'foo\nbar';
|
|
169
|
+
const amb = _diagnoseFoldTierAmbiguity(content, search);
|
|
170
|
+
if (!amb || amb.stage !== 'rstrip-fold' || amb.count !== 2) {
|
|
171
|
+
fail('pipeline:rstrip-ambiguity', JSON.stringify(amb));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Ambiguity: two indent-fold sites.
|
|
176
|
+
{
|
|
177
|
+
const content = 'header\n\tif (a) {}\nmid\n\tif (a) {}\nfooter\n';
|
|
178
|
+
const search = ' if (a) {}';
|
|
179
|
+
const slice = _findActualString(content, search, {});
|
|
180
|
+
const amb = _diagnoseFoldTierAmbiguity(content, search);
|
|
181
|
+
if (slice !== null) fail('pipeline:indent-ambiguity-find', JSON.stringify(slice));
|
|
182
|
+
if (!amb || amb.stage !== 'indent-fold' || amb.count !== 2) {
|
|
183
|
+
fail('pipeline:indent-ambiguity', JSON.stringify(amb));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Ambiguity: two eol-fold boundary sites.
|
|
188
|
+
{
|
|
189
|
+
const content = 'alpha\nbeta\nalpha';
|
|
190
|
+
const search = 'alpha\n';
|
|
191
|
+
const amb = _diagnoseEolFoldAmbiguity(content, search);
|
|
192
|
+
if (!amb || amb.stage !== 'eol-fold' || amb.count !== 2) {
|
|
193
|
+
fail('pipeline:eol-ambiguity', JSON.stringify(amb));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Whole-block re-indent still does not match (indent-shift not enabled).
|
|
198
|
+
{
|
|
199
|
+
const content = 'header\nif (x) {\n y;\n}\nfooter\n';
|
|
200
|
+
const search = ' if (x) {\n y;\n }';
|
|
201
|
+
const slice = _findActualString(content, search, {});
|
|
202
|
+
if (slice !== null) fail('pipeline:indent-shift-still-removed', JSON.stringify(slice));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ── Section 3: _stripTrailingWhitespacePerLine (insert-side helper) ─────────
|
|
206
|
+
const stripCases = [
|
|
207
|
+
{ name: 'LF-trailing-spaces', input: 'foo \nbar\t\nbaz\n', output: 'foo\nbar\nbaz\n' },
|
|
208
|
+
{ name: 'CRLF-preserved', input: 'foo \r\nbar\t\r\nbaz\r\n', output: 'foo\r\nbar\r\nbaz\r\n' },
|
|
209
|
+
{ name: 'lone-CR-preserved', input: 'foo \rbar\t\rbaz\r', output: 'foo\rbar\rbaz\r' },
|
|
210
|
+
{ name: 'no-trailing-no-change', input: 'foo\nbar\nbaz\n', output: 'foo\nbar\nbaz\n' },
|
|
211
|
+
{ name: 'mixed-LF-CRLF', input: 'foo \nbar \r\nbaz \n', output: 'foo\nbar\r\nbaz\n' },
|
|
212
|
+
{ name: 'final-line-no-terminator', input: 'foo \nbar ', output: 'foo\nbar' },
|
|
213
|
+
{ name: 'blank-lines-untouched', input: 'foo\n\n \nbar\n', output: 'foo\n\n\nbar\n' },
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
for (const { name, input, output } of stripCases) {
|
|
217
|
+
const got = _stripTrailingWhitespacePerLine(input);
|
|
218
|
+
if (got !== output) { fail(`strip:${name}`, `got ${JSON.stringify(got)}, expected ${JSON.stringify(output)}`); }
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── Section 4: _nfcFoldMatch ────────────────────────────────────────────────
|
|
222
|
+
// 가 NFC = U+AC00 (1 char); NFD = U+1100 U+1161 (2 chars).
|
|
223
|
+
// 각 = U+AC01 (1 NFC) vs U+1100 U+1161 U+11A8 (3 NFD).
|
|
224
|
+
const NFC_GA = '\uAC00';
|
|
225
|
+
const NFD_GA = '\u1100\u1161';
|
|
226
|
+
const NFC_GAK = '\uAC01';
|
|
227
|
+
const NFD_GAK = '\u1100\u1161\u11A8';
|
|
228
|
+
|
|
229
|
+
const nfcCases = [
|
|
230
|
+
{
|
|
231
|
+
name: 'NFD-content-NFC-search-hangul',
|
|
232
|
+
content: 'prefix-' + NFD_GAK + '-suffix\n',
|
|
233
|
+
search: 'prefix-' + NFC_GAK + '-suffix',
|
|
234
|
+
expected: 'prefix-' + NFD_GAK + '-suffix',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'NFC-content-NFD-search-hangul',
|
|
238
|
+
content: 'prefix-' + NFC_GAK + '-suffix\n',
|
|
239
|
+
search: 'prefix-' + NFD_GAK + '-suffix',
|
|
240
|
+
expected: 'prefix-' + NFC_GAK + '-suffix',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'NFC-no-drift-skip',
|
|
244
|
+
content: 'plain text',
|
|
245
|
+
search: 'plain text',
|
|
246
|
+
expected: null,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'NFC-multi-match-rejected',
|
|
250
|
+
content: NFD_GAK + ' / ' + NFD_GAK + '\n',
|
|
251
|
+
search: NFC_GAK,
|
|
252
|
+
expected: null,
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'NFC-not-found',
|
|
256
|
+
content: 'completely unrelated\n',
|
|
257
|
+
search: NFC_GAK,
|
|
258
|
+
expected: null,
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'NFC-empty-search',
|
|
262
|
+
content: NFD_GA + '\n',
|
|
263
|
+
search: '',
|
|
264
|
+
expected: null,
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'NFC-cafe-latin',
|
|
268
|
+
content: 'cafe\u0301 menu\n',
|
|
269
|
+
search: 'caf\u00E9 menu',
|
|
270
|
+
expected: 'cafe\u0301 menu',
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: 'NFC-partial-line-byte-slice',
|
|
274
|
+
content: 'header\nthe ' + NFD_GA + ' is here\nfooter\n',
|
|
275
|
+
search: NFC_GA,
|
|
276
|
+
expected: NFD_GA,
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
for (const { name, content, search, expected } of nfcCases) {
|
|
281
|
+
const got = _nfcFoldMatch(content, search);
|
|
282
|
+
if (got !== expected) { fail(`nfc:${name}`, `got ${JSON.stringify(got)}, expected ${JSON.stringify(expected)}`); continue; }
|
|
283
|
+
if (got !== null && !content.includes(got)) { fail(`nfc:${name}`, `returned slice is NOT a substring of content`); }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ── Section 5: edge cases ───────────────────────────────────────────────────
|
|
287
|
+
const edgeCases = [
|
|
288
|
+
{
|
|
289
|
+
name: 'fold-empty-string',
|
|
290
|
+
run: () => _normalizeForMatch(''),
|
|
291
|
+
expected: '',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: 'fold-pure-ascii-no-change',
|
|
295
|
+
run: () => _normalizeForMatch('plain ASCII line 123'),
|
|
296
|
+
expected: 'plain ASCII line 123',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'strip-empty-input',
|
|
300
|
+
run: () => _stripTrailingWhitespacePerLine(''),
|
|
301
|
+
expected: '',
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: 'strip-only-newlines',
|
|
305
|
+
run: () => _stripTrailingWhitespacePerLine('\n\n\n'),
|
|
306
|
+
expected: '\n\n\n',
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: 'strip-tab-only-line',
|
|
310
|
+
run: () => _stripTrailingWhitespacePerLine('foo\n\t\nbar\n'),
|
|
311
|
+
expected: 'foo\n\nbar\n',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: 'fold-BOM-not-folded',
|
|
315
|
+
run: () => _normalizeForMatch('\uFEFFfoo'),
|
|
316
|
+
expected: '\uFEFFfoo',
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: 'fold-ZWSP-not-folded',
|
|
320
|
+
run: () => _normalizeForMatch('foo\u200Bbar'),
|
|
321
|
+
expected: 'foo\u200Bbar',
|
|
322
|
+
},
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
for (const { name, run, expected } of edgeCases) {
|
|
326
|
+
const got = run();
|
|
327
|
+
if (got !== expected) { fail(`edge:${name}`, `got ${JSON.stringify(got)}, expected ${JSON.stringify(expected)}`); }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ── Section 6: preserveQuoteTypography (heuristic exception, CC parity) ──
|
|
331
|
+
// L/R curly quote codepoints — explicit escape to avoid encoding ambiguity.
|
|
332
|
+
const LSQ = '\u2018', RSQ = '\u2019', LDQ = '\u201C', RDQ = '\u201D';
|
|
333
|
+
const typoCases = [
|
|
334
|
+
{
|
|
335
|
+
name: 'double-quote-preserved',
|
|
336
|
+
old: 'foo "bar" baz',
|
|
337
|
+
matched: `foo ${LDQ}bar${RDQ} baz`,
|
|
338
|
+
new_: 'updated "content" here',
|
|
339
|
+
expected: `updated ${LDQ}content${RDQ} here`,
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'oldstring-equals-matched-no-change',
|
|
343
|
+
old: 'plain text',
|
|
344
|
+
matched: 'plain text',
|
|
345
|
+
new_: 'still plain "ascii"',
|
|
346
|
+
expected: 'still plain "ascii"',
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: 'no-curly-in-matched-no-change',
|
|
350
|
+
old: 'foo "bar"',
|
|
351
|
+
matched: 'foo "bar"',
|
|
352
|
+
new_: 'new "stuff"',
|
|
353
|
+
expected: 'new "stuff"',
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: 'contraction-right-single',
|
|
357
|
+
old: "it's a test",
|
|
358
|
+
matched: `it${RSQ}s a test`,
|
|
359
|
+
new_: "they're here",
|
|
360
|
+
expected: `they${RSQ}re here`,
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: 'opening-single-quote',
|
|
364
|
+
old: "say 'hi'",
|
|
365
|
+
matched: `say ${LSQ}hi${RSQ}`,
|
|
366
|
+
new_: "say 'bye'",
|
|
367
|
+
expected: `say ${LSQ}bye${RSQ}`,
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: 'opening-at-string-start',
|
|
371
|
+
old: '"hello"',
|
|
372
|
+
matched: `${LDQ}hello${RDQ}`,
|
|
373
|
+
new_: '"world"',
|
|
374
|
+
expected: `${LDQ}world${RDQ}`,
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: 'opening-after-em-dash',
|
|
378
|
+
old: 'said \u2014 "yes"',
|
|
379
|
+
matched: `said \u2014 ${LDQ}yes${RDQ}`,
|
|
380
|
+
new_: 'said \u2014 "no"',
|
|
381
|
+
expected: `said \u2014 ${LDQ}no${RDQ}`,
|
|
382
|
+
},
|
|
383
|
+
];
|
|
384
|
+
|
|
385
|
+
for (const { name, old, matched, new_, expected } of typoCases) {
|
|
386
|
+
const got = _preserveQuoteTypography(old, matched, new_);
|
|
387
|
+
if (got !== expected) {
|
|
388
|
+
fail(`typo:${name}`, `got ${JSON.stringify(got)}, expected ${JSON.stringify(expected)}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const totalChecks =
|
|
393
|
+
matchCases.length + skipCases.length + 2
|
|
394
|
+
+ 7 + stripCases.length + nfcCases.length + edgeCases.length
|
|
395
|
+
+ typoCases.length;
|
|
396
|
+
|
|
397
|
+
if (failures > 0) {
|
|
398
|
+
console.error(`\n${failures} smoke check(s) failed.`);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
console.log(`edit-normalize-smoke: ${totalChecks} checks passed.`);
|