@bastani/atomic 0.9.2 → 0.9.3-alpha.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/CHANGELOG.md +57 -0
- package/README.md +2 -2
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/direct-tools.ts +4 -2
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/proxy-call.ts +3 -1
- package/dist/builtin/mcp/utils.ts +18 -7
- package/dist/builtin/subagents/CHANGELOG.md +11 -0
- package/dist/builtin/subagents/README.md +6 -6
- package/dist/builtin/subagents/agents/code-simplifier.md +7 -6
- package/dist/builtin/subagents/agents/codebase-analyzer.md +5 -4
- package/dist/builtin/subagents/agents/codebase-locator.md +3 -3
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +10 -10
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +4 -4
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +3 -3
- package/dist/builtin/subagents/agents/codebase-research-locator.md +4 -4
- package/dist/builtin/subagents/agents/debugger.md +5 -5
- package/dist/builtin/subagents/agents/worker.md +56 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
- package/dist/builtin/subagents/src/agents/agent-loaders.ts +3 -5
- package/dist/builtin/subagents/src/agents/agent-management-helpers.ts +3 -3
- package/dist/builtin/subagents/src/extension/schemas.ts +2 -2
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -3
- package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +2 -2
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -1
- package/dist/builtin/subagents/src/shared/types-depth.ts +5 -5
- package/dist/builtin/subagents/src/shared/types-runtime.ts +2 -1
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +13 -0
- package/dist/builtin/workflows/README.md +2 -2
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +11 -6
- package/dist/builtin/workflows/builtin/goal-ledger.ts +33 -1
- package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -28
- package/dist/builtin/workflows/builtin/goal-reducer.ts +2 -2
- package/dist/builtin/workflows/builtin/goal-reports.ts +2 -5
- package/dist/builtin/workflows/builtin/goal-review.ts +1 -1
- package/dist/builtin/workflows/builtin/goal-runner.ts +10 -17
- package/dist/builtin/workflows/builtin/open-claude-design-feedback.ts +3 -3
- package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +1 -3
- package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +1 -1
- package/dist/builtin/workflows/builtin/ralph-core.ts +7 -17
- package/dist/builtin/workflows/builtin/ralph-runner.ts +11 -18
- package/dist/builtin/workflows/builtin/shared-prompts.ts +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/config-loader.ts +35 -15
- package/dist/builtin/workflows/src/extension/discovery.ts +20 -8
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +1 -2
- package/dist/builtin/workflows/src/extension/wiring.ts +1 -1
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +11 -10
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/config-self-update.d.ts.map +1 -1
- package/dist/config-self-update.js +3 -4
- package/dist/config-self-update.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -5
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-bash.d.ts +1 -0
- package/dist/core/agent-session-bash.d.ts.map +1 -1
- package/dist/core/agent-session-bash.js +1 -0
- package/dist/core/agent-session-bash.js.map +1 -1
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +23 -0
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -0
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +1 -0
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +29 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +36 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/context-compaction-metrics.d.ts +14 -2
- package/dist/core/compaction/context-compaction-metrics.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-metrics.js +50 -1
- package/dist/core/compaction/context-compaction-metrics.js.map +1 -1
- package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-prompt.js +2 -0
- package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
- package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-runner.js +1 -1
- package/dist/core/compaction/context-compaction-runner.js.map +1 -1
- package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-application.js +5 -5
- package/dist/core/compaction/context-deletion-application.js.map +1 -1
- package/dist/core/compaction/context-deletion-targets.d.ts +2 -0
- package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-targets.js +23 -3
- package/dist/core/compaction/context-deletion-targets.js.map +1 -1
- package/dist/core/compaction/context-deletion-tool-definitions.d.ts +6 -0
- package/dist/core/compaction/context-deletion-tool-definitions.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-tool-definitions.js.map +1 -1
- package/dist/core/compaction/context-deletion-tools.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-tools.js +18 -10
- package/dist/core/compaction/context-deletion-tools.js.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.js +2 -4
- package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
- package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
- package/dist/core/copilot-gemini-tool-arguments.js +2 -60
- package/dist/core/copilot-gemini-tool-arguments.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +2 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +11 -3
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +11 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/tool-events.d.ts +13 -13
- package/dist/core/extensions/tool-events.d.ts.map +1 -1
- package/dist/core/extensions/tool-events.js +3 -3
- package/dist/core/extensions/tool-events.js.map +1 -1
- package/dist/core/extensions/types.d.ts +1 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/flattened-tool-arguments.d.ts +18 -0
- package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
- package/dist/core/flattened-tool-arguments.js +104 -0
- package/dist/core/flattened-tool-arguments.js.map +1 -1
- package/dist/core/sdk-exports.d.ts +1 -1
- package/dist/core/sdk-exports.d.ts.map +1 -1
- package/dist/core/sdk-exports.js +1 -1
- package/dist/core/sdk-exports.js.map +1 -1
- package/dist/core/sdk-types.d.ts +2 -2
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +4 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +18 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager-resource-accessors.d.ts +4 -0
- package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-resource-accessors.js +15 -0
- package/dist/core/settings-manager-resource-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +11 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +3 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/artifact-protocol.d.ts +11 -0
- package/dist/core/tools/artifact-protocol.d.ts.map +1 -0
- package/dist/core/tools/artifact-protocol.js +76 -0
- package/dist/core/tools/artifact-protocol.js.map +1 -0
- package/dist/core/tools/artifacts.d.ts +18 -0
- package/dist/core/tools/artifacts.d.ts.map +1 -0
- package/dist/core/tools/artifacts.js +90 -0
- package/dist/core/tools/artifacts.js.map +1 -0
- package/dist/core/tools/bash-async-jobs.d.ts +20 -0
- package/dist/core/tools/bash-async-jobs.d.ts.map +1 -0
- package/dist/core/tools/bash-async-jobs.js +59 -0
- package/dist/core/tools/bash-async-jobs.js.map +1 -0
- package/dist/core/tools/bash-async-output.d.ts +10 -0
- package/dist/core/tools/bash-async-output.d.ts.map +1 -0
- package/dist/core/tools/bash-async-output.js +80 -0
- package/dist/core/tools/bash-async-output.js.map +1 -0
- package/dist/core/tools/bash-interceptor.d.ts +10 -0
- package/dist/core/tools/bash-interceptor.d.ts.map +1 -0
- package/dist/core/tools/bash-interceptor.js +39 -0
- package/dist/core/tools/bash-interceptor.js.map +1 -0
- package/dist/core/tools/bash-leading-cd.d.ts +7 -0
- package/dist/core/tools/bash-leading-cd.d.ts.map +1 -0
- package/dist/core/tools/bash-leading-cd.js +59 -0
- package/dist/core/tools/bash-leading-cd.js.map +1 -0
- package/dist/core/tools/bash-pty-native.d.ts +14 -0
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -0
- package/dist/core/tools/bash-pty-native.js +71 -0
- package/dist/core/tools/bash-pty-native.js.map +1 -0
- package/dist/core/tools/bash.d.ts +28 -17
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +152 -35
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/block-resolver.d.ts +16 -0
- package/dist/core/tools/block-resolver.d.ts.map +1 -0
- package/dist/core/tools/block-resolver.js +74 -0
- package/dist/core/tools/block-resolver.js.map +1 -0
- package/dist/core/tools/conflict-registry.d.ts +16 -0
- package/dist/core/tools/conflict-registry.d.ts.map +1 -0
- package/dist/core/tools/conflict-registry.js +44 -0
- package/dist/core/tools/conflict-registry.js.map +1 -0
- package/dist/core/tools/directory-tree.d.ts +13 -0
- package/dist/core/tools/directory-tree.d.ts.map +1 -0
- package/dist/core/tools/directory-tree.js +81 -0
- package/dist/core/tools/directory-tree.js.map +1 -0
- package/dist/core/tools/edit.d.ts +4 -29
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +136 -228
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/fetch-url.d.ts +74 -0
- package/dist/core/tools/fetch-url.d.ts.map +1 -0
- package/dist/core/tools/fetch-url.js +518 -0
- package/dist/core/tools/fetch-url.js.map +1 -0
- package/dist/core/tools/find.d.ts +27 -9
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +400 -176
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/glob-path-utils.d.ts +8 -0
- package/dist/core/tools/glob-path-utils.d.ts.map +1 -0
- package/dist/core/tools/glob-path-utils.js +26 -0
- package/dist/core/tools/glob-path-utils.js.map +1 -0
- package/dist/core/tools/grep.d.ts +12 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +141 -17
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/hashline-engine/apply.d.ts +11 -0
- package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/apply.js +752 -0
- package/dist/core/tools/hashline-engine/apply.js.map +1 -0
- package/dist/core/tools/hashline-engine/block.d.ts +40 -0
- package/dist/core/tools/hashline-engine/block.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/block.js +117 -0
- package/dist/core/tools/hashline-engine/block.js.map +1 -0
- package/dist/core/tools/hashline-engine/diff-preview.d.ts +15 -0
- package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/diff-preview.js +98 -0
- package/dist/core/tools/hashline-engine/diff-preview.js.map +1 -0
- package/dist/core/tools/hashline-engine/format.d.ts +71 -0
- package/dist/core/tools/hashline-engine/format.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/format.js +178 -0
- package/dist/core/tools/hashline-engine/format.js.map +1 -0
- package/dist/core/tools/hashline-engine/fs.d.ts +81 -0
- package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/fs.js +143 -0
- package/dist/core/tools/hashline-engine/fs.js.map +1 -0
- package/dist/core/tools/hashline-engine/index.d.ts +18 -0
- package/dist/core/tools/hashline-engine/index.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/index.js +20 -0
- package/dist/core/tools/hashline-engine/index.js.map +1 -0
- package/dist/core/tools/hashline-engine/input.d.ts +101 -0
- package/dist/core/tools/hashline-engine/input.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/input.js +398 -0
- package/dist/core/tools/hashline-engine/input.js.map +1 -0
- package/dist/core/tools/hashline-engine/messages.d.ts +99 -0
- package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/messages.js +144 -0
- package/dist/core/tools/hashline-engine/messages.js.map +1 -0
- package/dist/core/tools/hashline-engine/mismatch.d.ts +45 -0
- package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/mismatch.js +90 -0
- package/dist/core/tools/hashline-engine/mismatch.js.map +1 -0
- package/dist/core/tools/hashline-engine/normalize.d.ts +21 -0
- package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/normalize.js +33 -0
- package/dist/core/tools/hashline-engine/normalize.js.map +1 -0
- package/dist/core/tools/hashline-engine/parser.d.ts +24 -0
- package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/parser.js +381 -0
- package/dist/core/tools/hashline-engine/parser.js.map +1 -0
- package/dist/core/tools/hashline-engine/patcher.d.ts +118 -0
- package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/patcher.js +341 -0
- package/dist/core/tools/hashline-engine/patcher.js.map +1 -0
- package/dist/core/tools/hashline-engine/prefixes.d.ts +43 -0
- package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/prefixes.js +135 -0
- package/dist/core/tools/hashline-engine/prefixes.js.map +1 -0
- package/dist/core/tools/hashline-engine/recovery.d.ts +41 -0
- package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/recovery.js +168 -0
- package/dist/core/tools/hashline-engine/recovery.js.map +1 -0
- package/dist/core/tools/hashline-engine/snapshots.d.ts +65 -0
- package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/snapshots.js +108 -0
- package/dist/core/tools/hashline-engine/snapshots.js.map +1 -0
- package/dist/core/tools/hashline-engine/stream.d.ts +3 -0
- package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/stream.js +111 -0
- package/dist/core/tools/hashline-engine/stream.js.map +1 -0
- package/dist/core/tools/hashline-engine/tokenizer.d.ts +69 -0
- package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/tokenizer.js +430 -0
- package/dist/core/tools/hashline-engine/tokenizer.js.map +1 -0
- package/dist/core/tools/hashline-engine/types.d.ts +166 -0
- package/dist/core/tools/hashline-engine/types.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/types.js +9 -0
- package/dist/core/tools/hashline-engine/types.js.map +1 -0
- package/dist/core/tools/hashline.d.ts +29 -0
- package/dist/core/tools/hashline.d.ts.map +1 -0
- package/dist/core/tools/hashline.js +110 -0
- package/dist/core/tools/hashline.js.map +1 -0
- package/dist/core/tools/index.d.ts +6 -4
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +52 -35
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/notebook.d.ts +38 -0
- package/dist/core/tools/notebook.d.ts.map +1 -0
- package/dist/core/tools/notebook.js +125 -0
- package/dist/core/tools/notebook.js.map +1 -0
- package/dist/core/tools/read-document-extract.d.ts +9 -0
- package/dist/core/tools/read-document-extract.d.ts.map +1 -0
- package/dist/core/tools/read-document-extract.js +212 -0
- package/dist/core/tools/read-document-extract.js.map +1 -0
- package/dist/core/tools/read-selectors.d.ts +24 -0
- package/dist/core/tools/read-selectors.d.ts.map +1 -0
- package/dist/core/tools/read-selectors.js +277 -0
- package/dist/core/tools/read-selectors.js.map +1 -0
- package/dist/core/tools/read-url.d.ts +37 -0
- package/dist/core/tools/read-url.d.ts.map +1 -0
- package/dist/core/tools/read-url.js +39 -0
- package/dist/core/tools/read-url.js.map +1 -0
- package/dist/core/tools/read.d.ts +11 -11
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +224 -94
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/resource-selectors.d.ts +44 -0
- package/dist/core/tools/resource-selectors.d.ts.map +1 -0
- package/dist/core/tools/resource-selectors.js +808 -0
- package/dist/core/tools/resource-selectors.js.map +1 -0
- package/dist/core/tools/search-details.d.ts +26 -0
- package/dist/core/tools/search-details.d.ts.map +1 -0
- package/dist/core/tools/search-details.js +24 -0
- package/dist/core/tools/search-details.js.map +1 -0
- package/dist/core/tools/search-line-ranges.d.ts +11 -0
- package/dist/core/tools/search-line-ranges.d.ts.map +1 -0
- package/dist/core/tools/search-line-ranges.js +65 -0
- package/dist/core/tools/search-line-ranges.js.map +1 -0
- package/dist/core/tools/search-native.d.ts +97 -0
- package/dist/core/tools/search-native.d.ts.map +1 -0
- package/dist/core/tools/search-native.js +27 -0
- package/dist/core/tools/search-native.js.map +1 -0
- package/dist/core/tools/search.d.ts +24 -0
- package/dist/core/tools/search.d.ts.map +1 -0
- package/dist/core/tools/search.js +573 -0
- package/dist/core/tools/search.js.map +1 -0
- package/dist/core/tools/truncate.d.ts +4 -4
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js +3 -3
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/url-ip-guards.d.ts +4 -0
- package/dist/core/tools/url-ip-guards.d.ts.map +1 -0
- package/dist/core/tools/url-ip-guards.js +126 -0
- package/dist/core/tools/url-ip-guards.js.map +1 -0
- package/dist/core/tools/write.d.ts +12 -2
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +166 -14
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -3
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/index-extensions.d.ts +2 -2
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +9 -2
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.js +3 -0
- package/dist/modes/interactive/components/settings-selector-handlers.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-items.js +7 -0
- package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.js +0 -5
- package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
- package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-auth-login.js +1 -0
- package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +80 -2
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.js +3 -0
- package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +51 -0
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +5 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +12 -0
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +4 -1
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-onboarding.d.ts +11 -0
- package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-onboarding.js +220 -0
- package/dist/modes/interactive/interactive-onboarding.js.map +1 -0
- package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-selectors.js +4 -0
- package/dist/modes/interactive/interactive-selectors.js.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.js +6 -0
- package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.js +9 -4
- package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +28 -0
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +21 -1
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/markit.d.ts +8 -0
- package/dist/utils/markit.d.ts.map +1 -0
- package/dist/utils/markit.js +53 -0
- package/dist/utils/markit.js.map +1 -0
- package/dist/utils/paths.d.ts +2 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -1
- package/dist/utils/paths.js.map +1 -1
- package/docs/compaction.md +16 -1
- package/docs/containerization.md +1 -1
- package/docs/docs.json +1 -0
- package/docs/extensions.md +25 -36
- package/docs/quickstart.md +11 -6
- package/docs/sdk.md +5 -5
- package/docs/settings.md +7 -0
- package/docs/subagents.md +3 -2
- package/docs/tools.md +49 -0
- package/docs/usage.md +3 -3
- package/docs/workflows.md +7 -5
- package/examples/extensions/subagent/README.md +5 -5
- package/examples/extensions/subagent/agents/planner.md +1 -1
- package/examples/extensions/subagent/agents/reviewer.md +1 -1
- package/examples/extensions/subagent/agents/scout.md +2 -2
- package/examples/extensions/subagent/display.ts +3 -3
- package/examples/sdk/05-tools.ts +3 -3
- package/examples/sdk/README.md +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.
|
|
2
|
+
// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).
|
|
3
|
+
/**
|
|
4
|
+
* Token-driven state machine that turns a stream of {@link Token}s into a
|
|
5
|
+
* flat list of {@link Edit}s. Sits between the {@link Tokenizer} and the
|
|
6
|
+
* applier.
|
|
7
|
+
*/
|
|
8
|
+
import { HL_PAYLOAD_REPLACE } from "./format.js";
|
|
9
|
+
import { BARE_BODY_AUTO_PIPED_WARNING, DELETE_BLOCK_TAKES_NO_BODY, DELETE_TAKES_NO_BODY, EMPTY_BLOCK, EMPTY_INSERT, MINUS_ROW_REJECTED, } from "./messages.js";
|
|
10
|
+
import { stripOneLeadingHashlinePrefix } from "./prefixes.js";
|
|
11
|
+
import { cloneCursor, Tokenizer } from "./tokenizer.js";
|
|
12
|
+
function validateRangeOrder(range, lineNum) {
|
|
13
|
+
if (range.end.line < range.start.line) {
|
|
14
|
+
throw new Error(`line ${lineNum}: range ${range.start.line}..${range.end.line} ends before it starts.`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function expandRange(range) {
|
|
18
|
+
const anchors = [];
|
|
19
|
+
for (let line = range.start.line; line <= range.end.line; line++)
|
|
20
|
+
anchors.push({ line });
|
|
21
|
+
return anchors;
|
|
22
|
+
}
|
|
23
|
+
function isSkippableCommentLine(line) {
|
|
24
|
+
return line.trimStart().startsWith("#");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Stripped remainder of a bare `N: <value>` row that is a lone quoted or
|
|
28
|
+
* numeric literal (optionally comma-terminated) — the shape of a numeric-keyed
|
|
29
|
+
* dict/YAML body rather than read-output paste.
|
|
30
|
+
*/
|
|
31
|
+
const BARE_LITERAL_VALUE_RE = /^\s*(?:"[^"]*"|'[^']*'|[-+]?\d+(?:\.\d+)?)\s*,?\s*$/;
|
|
32
|
+
function detectApplyPatchContamination(text, _hasPending) {
|
|
33
|
+
const trimmed = text.trimStart();
|
|
34
|
+
if (trimmed.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
if (trimmed.startsWith("*** Update File:") ||
|
|
37
|
+
trimmed.startsWith("*** Add File:") ||
|
|
38
|
+
trimmed.startsWith("*** Delete File:") ||
|
|
39
|
+
trimmed.startsWith("*** Move to:")) {
|
|
40
|
+
const preview = trimmed.length > 48 ? `${trimmed.slice(0, 48)}…` : trimmed;
|
|
41
|
+
return (`apply_patch sentinel ${JSON.stringify(preview)} is not valid in hashline. ` +
|
|
42
|
+
"File sections start with `[path#HASH]` (no `Update File:` / `Add File:` keyword). " +
|
|
43
|
+
"Use `replace N..M:`, `delete N..M`, or `insert before|after|head|tail:` ops.");
|
|
44
|
+
}
|
|
45
|
+
if (/^@@\s+[-+]?\d+,\d+\s+[-+]?\d+,\d+\s+@@/.test(trimmed)) {
|
|
46
|
+
return ("unified-diff hunk header (`@@ -N,M +N,M @@`) is not valid in hashline. " +
|
|
47
|
+
"Use `replace N..M:`, `delete N..M`, or `insert before|after|head|tail:` ops.");
|
|
48
|
+
}
|
|
49
|
+
if (trimmed.startsWith("@@")) {
|
|
50
|
+
const preview = trimmed.length > 48 ? `${trimmed.slice(0, 48)}…` : trimmed;
|
|
51
|
+
return (`\`@@\`-bracketed hunk header ${JSON.stringify(preview)} is not valid in hashline. ` +
|
|
52
|
+
"Drop the `@@ ... @@` brackets and write a verb header such as `replace N..M:`.");
|
|
53
|
+
}
|
|
54
|
+
if (/^delete\s+[1-9]\d*(?:\s*(?:\.\.|-|…|\s)\s*[1-9]\d*)?\s*:/.test(trimmed)) {
|
|
55
|
+
return "`delete N..M` has no colon and no body. Remove the colon and body rows.";
|
|
56
|
+
}
|
|
57
|
+
if (/^[1-9]\d*\s*$/.test(trimmed)) {
|
|
58
|
+
return `hunk headers need a verb. Use \`replace ${trimmed}..${trimmed}:\` to replace, or \`delete ${trimmed}\` to delete.`;
|
|
59
|
+
}
|
|
60
|
+
const bareRange = /^([1-9]\d*)\s*[-. …]+\s*([1-9]\d*)\s*:?$/.exec(trimmed);
|
|
61
|
+
if (bareRange !== null) {
|
|
62
|
+
return (`bare range hunk header ${JSON.stringify(trimmed)} is not valid. ` +
|
|
63
|
+
`Hunk headers need a verb: write \`replace ${bareRange[1]}..${bareRange[2]}:\` or \`delete ${bareRange[1]}..${bareRange[2]}\`.`);
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
export class Executor {
|
|
68
|
+
#edits = [];
|
|
69
|
+
#warnings = [];
|
|
70
|
+
#editIndex = 0;
|
|
71
|
+
#pending;
|
|
72
|
+
#terminated = false;
|
|
73
|
+
#skippableComments = [];
|
|
74
|
+
#discardPendingSkippableComments() {
|
|
75
|
+
this.#skippableComments = [];
|
|
76
|
+
}
|
|
77
|
+
#consumePendingSkippableComments() {
|
|
78
|
+
if (this.#skippableComments.length === 0)
|
|
79
|
+
return;
|
|
80
|
+
for (const comment of this.#skippableComments)
|
|
81
|
+
this.#handleRaw(comment.text, comment.lineNum);
|
|
82
|
+
this.#skippableComments = [];
|
|
83
|
+
}
|
|
84
|
+
feed(token) {
|
|
85
|
+
if (this.#terminated)
|
|
86
|
+
return;
|
|
87
|
+
switch (token.kind) {
|
|
88
|
+
case "envelope-begin":
|
|
89
|
+
this.#consumePendingSkippableComments();
|
|
90
|
+
return;
|
|
91
|
+
case "envelope-end":
|
|
92
|
+
this.#consumePendingSkippableComments();
|
|
93
|
+
this.#terminated = true;
|
|
94
|
+
return;
|
|
95
|
+
case "abort":
|
|
96
|
+
this.#terminated = true;
|
|
97
|
+
return;
|
|
98
|
+
case "header":
|
|
99
|
+
this.#consumePendingSkippableComments();
|
|
100
|
+
this.#flushPending();
|
|
101
|
+
return;
|
|
102
|
+
case "blank":
|
|
103
|
+
this.#consumePendingSkippableComments();
|
|
104
|
+
this.#handleBlank("", token.lineNum);
|
|
105
|
+
return;
|
|
106
|
+
case "payload-literal":
|
|
107
|
+
this.#consumePendingSkippableComments();
|
|
108
|
+
this.#handleLiteralPayload(token.text, token.lineNum);
|
|
109
|
+
return;
|
|
110
|
+
case "raw":
|
|
111
|
+
if (this.#pending === undefined && isSkippableCommentLine(token.text)) {
|
|
112
|
+
this.#skippableComments.push({ text: token.text, lineNum: token.lineNum });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.#consumePendingSkippableComments();
|
|
116
|
+
this.#handleRaw(token.text, token.lineNum);
|
|
117
|
+
return;
|
|
118
|
+
case "op-block":
|
|
119
|
+
this.#discardPendingSkippableComments();
|
|
120
|
+
if (token.target.kind === "replace" || token.target.kind === "delete") {
|
|
121
|
+
validateRangeOrder(token.target.range, token.lineNum);
|
|
122
|
+
}
|
|
123
|
+
this.#flushPending();
|
|
124
|
+
this.#pending = { target: token.target, lineNum: token.lineNum, payloads: [], deferredBlanks: [] };
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
end() {
|
|
129
|
+
this.#consumePendingSkippableComments();
|
|
130
|
+
this.#flushPending();
|
|
131
|
+
this.#validateNoOverlappingDeletes();
|
|
132
|
+
return { edits: this.#edits, warnings: this.#warnings };
|
|
133
|
+
}
|
|
134
|
+
endStreaming() {
|
|
135
|
+
this.#consumePendingSkippableComments();
|
|
136
|
+
if (this.#pending && this.#pending.payloads.length > 0)
|
|
137
|
+
this.#flushPending();
|
|
138
|
+
else if (this.#pending?.target.kind === "delete" || this.#pending?.target.kind === "delete_block")
|
|
139
|
+
this.#flushPending();
|
|
140
|
+
else
|
|
141
|
+
this.#pending = undefined;
|
|
142
|
+
this.#validateNoOverlappingDeletes();
|
|
143
|
+
return { edits: this.#edits, warnings: this.#warnings };
|
|
144
|
+
}
|
|
145
|
+
reset() {
|
|
146
|
+
this.#edits = [];
|
|
147
|
+
this.#warnings = [];
|
|
148
|
+
this.#editIndex = 0;
|
|
149
|
+
this.#pending = undefined;
|
|
150
|
+
this.#skippableComments = [];
|
|
151
|
+
this.#terminated = false;
|
|
152
|
+
}
|
|
153
|
+
#validateNoOverlappingDeletes() {
|
|
154
|
+
const sourceLinesByAnchor = new Map();
|
|
155
|
+
for (const edit of this.#edits) {
|
|
156
|
+
if (edit.kind !== "delete")
|
|
157
|
+
continue;
|
|
158
|
+
let sourceLines = sourceLinesByAnchor.get(edit.anchor.line);
|
|
159
|
+
if (sourceLines === undefined) {
|
|
160
|
+
sourceLines = [];
|
|
161
|
+
sourceLinesByAnchor.set(edit.anchor.line, sourceLines);
|
|
162
|
+
}
|
|
163
|
+
if (!sourceLines.includes(edit.lineNum))
|
|
164
|
+
sourceLines.push(edit.lineNum);
|
|
165
|
+
}
|
|
166
|
+
for (const [anchorLine, sourceLines] of sourceLinesByAnchor) {
|
|
167
|
+
if (sourceLines.length < 2)
|
|
168
|
+
continue;
|
|
169
|
+
const [firstBlock, secondBlock] = [...sourceLines].sort((a, b) => a - b);
|
|
170
|
+
throw new Error(`line ${secondBlock}: anchor line ${anchorLine} is already targeted by another hunk on line ${firstBlock}. ` +
|
|
171
|
+
"Issue ONE hunk per range; payload is only the final desired content, never a before/after pair.");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
#handleLiteralPayload(text, lineNum) {
|
|
175
|
+
const pending = this.#pending;
|
|
176
|
+
if (!pending) {
|
|
177
|
+
throw new Error(`line ${lineNum}: payload line has no preceding hunk header. ` +
|
|
178
|
+
`Got ${JSON.stringify(`${HL_PAYLOAD_REPLACE}${text}`)}.`);
|
|
179
|
+
}
|
|
180
|
+
if (pending.target.kind === "delete")
|
|
181
|
+
throw new Error(`line ${lineNum}: ${DELETE_TAKES_NO_BODY}`);
|
|
182
|
+
if (pending.target.kind === "delete_block")
|
|
183
|
+
throw new Error(`line ${lineNum}: ${DELETE_BLOCK_TAKES_NO_BODY}`);
|
|
184
|
+
this.#commitDeferredBlanks(pending);
|
|
185
|
+
pending.payloads.push({ kind: "literal", text, lineNum });
|
|
186
|
+
}
|
|
187
|
+
#handleRaw(text, lineNum) {
|
|
188
|
+
const contamination = detectApplyPatchContamination(text, this.#pending !== undefined);
|
|
189
|
+
if (contamination !== null)
|
|
190
|
+
throw new Error(`line ${lineNum}: ${contamination}`);
|
|
191
|
+
if (this.#pending) {
|
|
192
|
+
if (text.trim().length === 0) {
|
|
193
|
+
this.#handleBlank(text, lineNum);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (this.#pending.target.kind === "delete")
|
|
197
|
+
throw new Error(`line ${lineNum}: ${DELETE_TAKES_NO_BODY}`);
|
|
198
|
+
if (this.#pending.target.kind === "delete_block")
|
|
199
|
+
throw new Error(`line ${lineNum}: ${DELETE_BLOCK_TAKES_NO_BODY}`);
|
|
200
|
+
if (text.trimStart().charCodeAt(0) === 45 /* - */)
|
|
201
|
+
throw new Error(`line ${lineNum}: ${MINUS_ROW_REJECTED}`);
|
|
202
|
+
if (!this.#warnings.includes(BARE_BODY_AUTO_PIPED_WARNING))
|
|
203
|
+
this.#warnings.push(BARE_BODY_AUTO_PIPED_WARNING);
|
|
204
|
+
this.#commitDeferredBlanks(this.#pending);
|
|
205
|
+
// Defer read-output line-number stripping to #flushPending: a bare
|
|
206
|
+
// "N:text" row is only a copy-paste artifact from snapshot output
|
|
207
|
+
// when *every* bare row in the hunk carries that prefix. Stripping a
|
|
208
|
+
// row in isolation would corrupt a genuine body that merely starts
|
|
209
|
+
// with "digits:" (YAML ports "42:hello", timestamps "12:30") when it
|
|
210
|
+
// sits next to an unprefixed sibling. Rows with an explicit "+" go
|
|
211
|
+
// through #handleLiteralPayload and are never bare, never stripped.
|
|
212
|
+
this.#pending.payloads.push({ kind: "literal", text, lineNum, bare: true });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (text.trim().length === 0)
|
|
216
|
+
return;
|
|
217
|
+
throw new Error(`line ${lineNum}: payload line has no preceding hunk header. ` +
|
|
218
|
+
`Use \`replace N..M:\`, \`delete N..M\`, or \`insert before|after|head|tail:\` above the body. Got ${JSON.stringify(text)}.`);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* A blank row inside a hunk body is ambiguous: interior blanks are body
|
|
222
|
+
* content (a bare-pasted body legitimately contains empty lines), while
|
|
223
|
+
* blanks before the body starts or trailing into the next op are layout.
|
|
224
|
+
* Defer them; {@link #commitDeferredBlanks} folds them in only when a later
|
|
225
|
+
* non-blank row proves they were interior.
|
|
226
|
+
*/
|
|
227
|
+
#handleBlank(text, lineNum) {
|
|
228
|
+
const pending = this.#pending;
|
|
229
|
+
if (!pending)
|
|
230
|
+
return;
|
|
231
|
+
if (pending.target.kind === "delete" || pending.target.kind === "delete_block")
|
|
232
|
+
return;
|
|
233
|
+
if (pending.payloads.length === 0)
|
|
234
|
+
return;
|
|
235
|
+
pending.deferredBlanks.push({ kind: "literal", text, lineNum, bare: true });
|
|
236
|
+
}
|
|
237
|
+
#commitDeferredBlanks(pending) {
|
|
238
|
+
if (pending.deferredBlanks.length === 0)
|
|
239
|
+
return;
|
|
240
|
+
if (!this.#warnings.includes(BARE_BODY_AUTO_PIPED_WARNING))
|
|
241
|
+
this.#warnings.push(BARE_BODY_AUTO_PIPED_WARNING);
|
|
242
|
+
pending.payloads.push(...pending.deferredBlanks);
|
|
243
|
+
pending.deferredBlanks = [];
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Strip a single read-output line-number prefix (`N:`) from every bare body
|
|
247
|
+
* row, but only when *all* bare rows carry one. A uniform set of prefixes is
|
|
248
|
+
* the signature of content pasted straight from `read`/`search` output; a
|
|
249
|
+
* mixed set means the `N:` is genuine payload content and must stay. Rows
|
|
250
|
+
* authored with an explicit `+` are not bare and are never touched.
|
|
251
|
+
*/
|
|
252
|
+
#stripBarePrefixesIfUniform(payloads) {
|
|
253
|
+
let sawBare = false;
|
|
254
|
+
let allLiteralValues = true;
|
|
255
|
+
for (const row of payloads) {
|
|
256
|
+
if (!row.bare || row.text.trim().length === 0)
|
|
257
|
+
continue;
|
|
258
|
+
sawBare = true;
|
|
259
|
+
const stripped = stripOneLeadingHashlinePrefix(row.text);
|
|
260
|
+
if (stripped === row.text)
|
|
261
|
+
return;
|
|
262
|
+
allLiteralValues &&= BARE_LITERAL_VALUE_RE.test(stripped);
|
|
263
|
+
}
|
|
264
|
+
if (!sawBare)
|
|
265
|
+
return;
|
|
266
|
+
// A body where every stripped remainder is a lone quoted/numeric literal
|
|
267
|
+
// (optionally comma-terminated) is the shape of a numeric-keyed dict or
|
|
268
|
+
// YAML mapping (`1: "one",`), not read-output paste; stripping the "N:"
|
|
269
|
+
// keys would mangle every line. Leave such bodies untouched.
|
|
270
|
+
if (allLiteralValues)
|
|
271
|
+
return;
|
|
272
|
+
for (const row of payloads) {
|
|
273
|
+
if (row.bare && row.text.trim().length > 0)
|
|
274
|
+
row.text = stripOneLeadingHashlinePrefix(row.text);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
#pushInsert(cursor, text, lineNum, mode) {
|
|
278
|
+
this.#edits.push({
|
|
279
|
+
kind: "insert",
|
|
280
|
+
cursor: cloneCursor(cursor),
|
|
281
|
+
text,
|
|
282
|
+
lineNum,
|
|
283
|
+
index: this.#editIndex++,
|
|
284
|
+
...(mode === undefined ? {} : { mode }),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
#pushDelete(anchor, lineNum) {
|
|
288
|
+
this.#edits.push({ kind: "delete", anchor: { ...anchor }, lineNum, index: this.#editIndex++ });
|
|
289
|
+
}
|
|
290
|
+
#pushBlock(anchor, payloads, lineNum, mode) {
|
|
291
|
+
this.#edits.push({
|
|
292
|
+
kind: "block",
|
|
293
|
+
anchor: { ...anchor },
|
|
294
|
+
payloads: payloads.map(payload => payload.text),
|
|
295
|
+
...(mode === undefined ? {} : { mode }),
|
|
296
|
+
lineNum,
|
|
297
|
+
index: this.#editIndex++,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
#emitPayloadRows(cursor, payloads, lineNum, mode) {
|
|
301
|
+
for (const payload of payloads)
|
|
302
|
+
this.#pushInsert(cursor, payload.text, lineNum, mode);
|
|
303
|
+
}
|
|
304
|
+
#flushPending() {
|
|
305
|
+
const pending = this.#pending;
|
|
306
|
+
if (!pending)
|
|
307
|
+
return;
|
|
308
|
+
const { target, lineNum, payloads } = pending;
|
|
309
|
+
this.#stripBarePrefixesIfUniform(payloads);
|
|
310
|
+
this.#pending = undefined;
|
|
311
|
+
if (target.kind === "delete") {
|
|
312
|
+
for (const anchor of expandRange(target.range))
|
|
313
|
+
this.#pushDelete(anchor, lineNum);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (target.kind === "delete_block") {
|
|
317
|
+
// A block edit with no payloads resolves to a pure block deletion.
|
|
318
|
+
this.#pushBlock(target.anchor, [], lineNum);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (target.kind === "block") {
|
|
322
|
+
if (payloads.length === 0)
|
|
323
|
+
throw new Error(`line ${lineNum}: ${EMPTY_BLOCK}`);
|
|
324
|
+
this.#pushBlock(target.anchor, payloads, lineNum);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (target.kind === "insert_after_block") {
|
|
328
|
+
if (payloads.length === 0)
|
|
329
|
+
throw new Error(`line ${lineNum}: ${EMPTY_INSERT}`);
|
|
330
|
+
this.#pushBlock(target.anchor, payloads, lineNum, "insert_after");
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (payloads.length === 0) {
|
|
334
|
+
if (target.kind === "replace") {
|
|
335
|
+
for (const anchor of expandRange(target.range))
|
|
336
|
+
this.#pushDelete(anchor, lineNum);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
throw new Error(`line ${lineNum}: ${EMPTY_INSERT}`);
|
|
340
|
+
}
|
|
341
|
+
if (target.kind === "replace") {
|
|
342
|
+
const cursor = { kind: "before_anchor", anchor: { ...target.range.start } };
|
|
343
|
+
this.#emitPayloadRows(cursor, payloads, lineNum, "replacement");
|
|
344
|
+
for (const anchor of expandRange(target.range))
|
|
345
|
+
this.#pushDelete(anchor, lineNum);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (target.kind === "insert_before") {
|
|
349
|
+
this.#emitPayloadRows({ kind: "before_anchor", anchor: { ...target.anchor } }, payloads, lineNum);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (target.kind === "insert_after") {
|
|
353
|
+
this.#emitPayloadRows({ kind: "after_anchor", anchor: { ...target.anchor } }, payloads, lineNum);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const cursor = target.kind === "bof" ? { kind: "bof" } : { kind: "eof" };
|
|
357
|
+
this.#emitPayloadRows(cursor, payloads, lineNum);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function drain(executor, tokenizer) {
|
|
361
|
+
for (const token of tokenizer.end())
|
|
362
|
+
executor.feed(token);
|
|
363
|
+
return executor.end();
|
|
364
|
+
}
|
|
365
|
+
export function parsePatch(diff) {
|
|
366
|
+
const tokenizer = new Tokenizer();
|
|
367
|
+
const executor = new Executor();
|
|
368
|
+
for (const token of tokenizer.feed(diff))
|
|
369
|
+
executor.feed(token);
|
|
370
|
+
return drain(executor, tokenizer);
|
|
371
|
+
}
|
|
372
|
+
export function parsePatchStreaming(diff) {
|
|
373
|
+
const tokenizer = new Tokenizer();
|
|
374
|
+
const executor = new Executor();
|
|
375
|
+
for (const token of tokenizer.feed(diff))
|
|
376
|
+
executor.feed(token);
|
|
377
|
+
for (const token of tokenizer.end())
|
|
378
|
+
executor.feed(token);
|
|
379
|
+
return executor.endStreaming();
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/parser.ts"],"names":[],"mappings":"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","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Token-driven state machine that turns a stream of {@link Token}s into a\n * flat list of {@link Edit}s. Sits between the {@link Tokenizer} and the\n * applier.\n */\nimport { HL_PAYLOAD_REPLACE } from \"./format.js\";\nimport {\n\tBARE_BODY_AUTO_PIPED_WARNING,\n\tDELETE_BLOCK_TAKES_NO_BODY,\n\tDELETE_TAKES_NO_BODY,\n\tEMPTY_BLOCK,\n\tEMPTY_INSERT,\n\tMINUS_ROW_REJECTED,\n} from \"./messages.js\";\nimport { stripOneLeadingHashlinePrefix } from \"./prefixes.js\";\nimport { type BlockTarget, cloneCursor, type ParsedRange, type Token, Tokenizer } from \"./tokenizer.js\";\nimport type { Anchor, Cursor, Edit } from \"./types.js\";\n\nfunction validateRangeOrder(range: ParsedRange, lineNum: number): void {\n\tif (range.end.line < range.start.line) {\n\t\tthrow new Error(`line ${lineNum}: range ${range.start.line}..${range.end.line} ends before it starts.`);\n\t}\n}\n\nfunction expandRange(range: ParsedRange): Anchor[] {\n\tconst anchors: Anchor[] = [];\n\tfor (let line = range.start.line; line <= range.end.line; line++) anchors.push({ line });\n\treturn anchors;\n}\n\nfunction isSkippableCommentLine(line: string): boolean {\n\treturn line.trimStart().startsWith(\"#\");\n}\n\n/**\n * Stripped remainder of a bare `N: <value>` row that is a lone quoted or\n * numeric literal (optionally comma-terminated) — the shape of a numeric-keyed\n * dict/YAML body rather than read-output paste.\n */\nconst BARE_LITERAL_VALUE_RE = /^\\s*(?:\"[^\"]*\"|'[^']*'|[-+]?\\d+(?:\\.\\d+)?)\\s*,?\\s*$/;\n\nfunction detectApplyPatchContamination(text: string, _hasPending: boolean): string | null {\n\tconst trimmed = text.trimStart();\n\tif (trimmed.length === 0) return null;\n\tif (\n\t\ttrimmed.startsWith(\"*** Update File:\") ||\n\t\ttrimmed.startsWith(\"*** Add File:\") ||\n\t\ttrimmed.startsWith(\"*** Delete File:\") ||\n\t\ttrimmed.startsWith(\"*** Move to:\")\n\t) {\n\t\tconst preview = trimmed.length > 48 ? `${trimmed.slice(0, 48)}…` : trimmed;\n\t\treturn (\n\t\t\t`apply_patch sentinel ${JSON.stringify(preview)} is not valid in hashline. ` +\n\t\t\t\"File sections start with `[path#HASH]` (no `Update File:` / `Add File:` keyword). \" +\n\t\t\t\"Use `replace N..M:`, `delete N..M`, or `insert before|after|head|tail:` ops.\"\n\t\t);\n\t}\n\tif (/^@@\\s+[-+]?\\d+,\\d+\\s+[-+]?\\d+,\\d+\\s+@@/.test(trimmed)) {\n\t\treturn (\n\t\t\t\"unified-diff hunk header (`@@ -N,M +N,M @@`) is not valid in hashline. \" +\n\t\t\t\"Use `replace N..M:`, `delete N..M`, or `insert before|after|head|tail:` ops.\"\n\t\t);\n\t}\n\tif (trimmed.startsWith(\"@@\")) {\n\t\tconst preview = trimmed.length > 48 ? `${trimmed.slice(0, 48)}…` : trimmed;\n\t\treturn (\n\t\t\t`\\`@@\\`-bracketed hunk header ${JSON.stringify(preview)} is not valid in hashline. ` +\n\t\t\t\"Drop the `@@ ... @@` brackets and write a verb header such as `replace N..M:`.\"\n\t\t);\n\t}\n\tif (/^delete\\s+[1-9]\\d*(?:\\s*(?:\\.\\.|-|…|\\s)\\s*[1-9]\\d*)?\\s*:/.test(trimmed)) {\n\t\treturn \"`delete N..M` has no colon and no body. Remove the colon and body rows.\";\n\t}\n\tif (/^[1-9]\\d*\\s*$/.test(trimmed)) {\n\t\treturn `hunk headers need a verb. Use \\`replace ${trimmed}..${trimmed}:\\` to replace, or \\`delete ${trimmed}\\` to delete.`;\n\t}\n\tconst bareRange = /^([1-9]\\d*)\\s*[-. …]+\\s*([1-9]\\d*)\\s*:?$/.exec(trimmed);\n\tif (bareRange !== null) {\n\t\treturn (\n\t\t\t`bare range hunk header ${JSON.stringify(trimmed)} is not valid. ` +\n\t\t\t`Hunk headers need a verb: write \\`replace ${bareRange[1]}..${bareRange[2]}:\\` or \\`delete ${bareRange[1]}..${bareRange[2]}\\`.`\n\t\t);\n\t}\n\treturn null;\n}\n\ninterface PendingComment {\n\tlineNum: number;\n\ttext: string;\n}\n\ntype PayloadRow = { kind: \"literal\"; text: string; lineNum: number; bare?: boolean };\n\ninterface Pending {\n\ttarget: BlockTarget;\n\tlineNum: number;\n\tpayloads: PayloadRow[];\n\t/**\n\t * Blank rows seen after the body started. Interior blanks are committed to\n\t * the payload when the next non-blank row arrives; trailing blanks before\n\t * the next header/op are layout separators and are discarded on flush.\n\t */\n\tdeferredBlanks: PayloadRow[];\n}\n\nexport class Executor {\n\t#edits: Edit[] = [];\n\t#warnings: string[] = [];\n\t#editIndex = 0;\n\t#pending: Pending | undefined;\n\t#terminated = false;\n\t#skippableComments: PendingComment[] = [];\n\n\t#discardPendingSkippableComments(): void {\n\t\tthis.#skippableComments = [];\n\t}\n\n\t#consumePendingSkippableComments(): void {\n\t\tif (this.#skippableComments.length === 0) return;\n\t\tfor (const comment of this.#skippableComments) this.#handleRaw(comment.text, comment.lineNum);\n\t\tthis.#skippableComments = [];\n\t}\n\n\tfeed(token: Token): void {\n\t\tif (this.#terminated) return;\n\t\tswitch (token.kind) {\n\t\t\tcase \"envelope-begin\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\treturn;\n\t\t\tcase \"envelope-end\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#terminated = true;\n\t\t\t\treturn;\n\t\t\tcase \"abort\":\n\t\t\t\tthis.#terminated = true;\n\t\t\t\treturn;\n\t\t\tcase \"header\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#flushPending();\n\t\t\t\treturn;\n\t\t\tcase \"blank\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#handleBlank(\"\", token.lineNum);\n\t\t\t\treturn;\n\t\t\tcase \"payload-literal\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#handleLiteralPayload(token.text, token.lineNum);\n\t\t\t\treturn;\n\t\t\tcase \"raw\":\n\t\t\t\tif (this.#pending === undefined && isSkippableCommentLine(token.text)) {\n\t\t\t\t\tthis.#skippableComments.push({ text: token.text, lineNum: token.lineNum });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#handleRaw(token.text, token.lineNum);\n\t\t\t\treturn;\n\t\t\tcase \"op-block\":\n\t\t\t\tthis.#discardPendingSkippableComments();\n\t\t\t\tif (token.target.kind === \"replace\" || token.target.kind === \"delete\") {\n\t\t\t\t\tvalidateRangeOrder(token.target.range, token.lineNum);\n\t\t\t\t}\n\t\t\t\tthis.#flushPending();\n\t\t\t\tthis.#pending = { target: token.target, lineNum: token.lineNum, payloads: [], deferredBlanks: [] };\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tend(): { edits: Edit[]; warnings: string[] } {\n\t\tthis.#consumePendingSkippableComments();\n\t\tthis.#flushPending();\n\t\tthis.#validateNoOverlappingDeletes();\n\t\treturn { edits: this.#edits, warnings: this.#warnings };\n\t}\n\n\tendStreaming(): { edits: Edit[]; warnings: string[] } {\n\t\tthis.#consumePendingSkippableComments();\n\t\tif (this.#pending && this.#pending.payloads.length > 0) this.#flushPending();\n\t\telse if (this.#pending?.target.kind === \"delete\" || this.#pending?.target.kind === \"delete_block\")\n\t\t\tthis.#flushPending();\n\t\telse this.#pending = undefined;\n\t\tthis.#validateNoOverlappingDeletes();\n\t\treturn { edits: this.#edits, warnings: this.#warnings };\n\t}\n\n\treset(): void {\n\t\tthis.#edits = [];\n\t\tthis.#warnings = [];\n\t\tthis.#editIndex = 0;\n\t\tthis.#pending = undefined;\n\t\tthis.#skippableComments = [];\n\t\tthis.#terminated = false;\n\t}\n\n\t#validateNoOverlappingDeletes(): void {\n\t\tconst sourceLinesByAnchor = new Map<number, number[]>();\n\t\tfor (const edit of this.#edits) {\n\t\t\tif (edit.kind !== \"delete\") continue;\n\t\t\tlet sourceLines = sourceLinesByAnchor.get(edit.anchor.line);\n\t\t\tif (sourceLines === undefined) {\n\t\t\t\tsourceLines = [];\n\t\t\t\tsourceLinesByAnchor.set(edit.anchor.line, sourceLines);\n\t\t\t}\n\t\t\tif (!sourceLines.includes(edit.lineNum)) sourceLines.push(edit.lineNum);\n\t\t}\n\t\tfor (const [anchorLine, sourceLines] of sourceLinesByAnchor) {\n\t\t\tif (sourceLines.length < 2) continue;\n\t\t\tconst [firstBlock, secondBlock] = [...sourceLines].sort((a, b) => a - b);\n\t\t\tthrow new Error(\n\t\t\t\t`line ${secondBlock}: anchor line ${anchorLine} is already targeted by another hunk on line ${firstBlock}. ` +\n\t\t\t\t\t\"Issue ONE hunk per range; payload is only the final desired content, never a before/after pair.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t#handleLiteralPayload(text: string, lineNum: number): void {\n\t\tconst pending = this.#pending;\n\t\tif (!pending) {\n\t\t\tthrow new Error(\n\t\t\t\t`line ${lineNum}: payload line has no preceding hunk header. ` +\n\t\t\t\t\t`Got ${JSON.stringify(`${HL_PAYLOAD_REPLACE}${text}`)}.`,\n\t\t\t);\n\t\t}\n\t\tif (pending.target.kind === \"delete\") throw new Error(`line ${lineNum}: ${DELETE_TAKES_NO_BODY}`);\n\t\tif (pending.target.kind === \"delete_block\") throw new Error(`line ${lineNum}: ${DELETE_BLOCK_TAKES_NO_BODY}`);\n\t\tthis.#commitDeferredBlanks(pending);\n\t\tpending.payloads.push({ kind: \"literal\", text, lineNum });\n\t}\n\n\t#handleRaw(text: string, lineNum: number): void {\n\t\tconst contamination = detectApplyPatchContamination(text, this.#pending !== undefined);\n\t\tif (contamination !== null) throw new Error(`line ${lineNum}: ${contamination}`);\n\t\tif (this.#pending) {\n\t\t\tif (text.trim().length === 0) {\n\t\t\t\tthis.#handleBlank(text, lineNum);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (this.#pending.target.kind === \"delete\") throw new Error(`line ${lineNum}: ${DELETE_TAKES_NO_BODY}`);\n\t\t\tif (this.#pending.target.kind === \"delete_block\")\n\t\t\t\tthrow new Error(`line ${lineNum}: ${DELETE_BLOCK_TAKES_NO_BODY}`);\n\t\t\tif (text.trimStart().charCodeAt(0) === 45 /* - */) throw new Error(`line ${lineNum}: ${MINUS_ROW_REJECTED}`);\n\t\t\tif (!this.#warnings.includes(BARE_BODY_AUTO_PIPED_WARNING)) this.#warnings.push(BARE_BODY_AUTO_PIPED_WARNING);\n\t\t\tthis.#commitDeferredBlanks(this.#pending);\n\t\t\t// Defer read-output line-number stripping to #flushPending: a bare\n\t\t\t// \"N:text\" row is only a copy-paste artifact from snapshot output\n\t\t\t// when *every* bare row in the hunk carries that prefix. Stripping a\n\t\t\t// row in isolation would corrupt a genuine body that merely starts\n\t\t\t// with \"digits:\" (YAML ports \"42:hello\", timestamps \"12:30\") when it\n\t\t\t// sits next to an unprefixed sibling. Rows with an explicit \"+\" go\n\t\t\t// through #handleLiteralPayload and are never bare, never stripped.\n\t\t\tthis.#pending.payloads.push({ kind: \"literal\", text, lineNum, bare: true });\n\t\t\treturn;\n\t\t}\n\t\tif (text.trim().length === 0) return;\n\t\tthrow new Error(\n\t\t\t`line ${lineNum}: payload line has no preceding hunk header. ` +\n\t\t\t\t`Use \\`replace N..M:\\`, \\`delete N..M\\`, or \\`insert before|after|head|tail:\\` above the body. Got ${JSON.stringify(text)}.`,\n\t\t);\n\t}\n\n\t/**\n\t * A blank row inside a hunk body is ambiguous: interior blanks are body\n\t * content (a bare-pasted body legitimately contains empty lines), while\n\t * blanks before the body starts or trailing into the next op are layout.\n\t * Defer them; {@link #commitDeferredBlanks} folds them in only when a later\n\t * non-blank row proves they were interior.\n\t */\n\t#handleBlank(text: string, lineNum: number): void {\n\t\tconst pending = this.#pending;\n\t\tif (!pending) return;\n\t\tif (pending.target.kind === \"delete\" || pending.target.kind === \"delete_block\") return;\n\t\tif (pending.payloads.length === 0) return;\n\t\tpending.deferredBlanks.push({ kind: \"literal\", text, lineNum, bare: true });\n\t}\n\n\t#commitDeferredBlanks(pending: Pending): void {\n\t\tif (pending.deferredBlanks.length === 0) return;\n\t\tif (!this.#warnings.includes(BARE_BODY_AUTO_PIPED_WARNING)) this.#warnings.push(BARE_BODY_AUTO_PIPED_WARNING);\n\t\tpending.payloads.push(...pending.deferredBlanks);\n\t\tpending.deferredBlanks = [];\n\t}\n\n\t/**\n\t * Strip a single read-output line-number prefix (`N:`) from every bare body\n\t * row, but only when *all* bare rows carry one. A uniform set of prefixes is\n\t * the signature of content pasted straight from `read`/`search` output; a\n\t * mixed set means the `N:` is genuine payload content and must stay. Rows\n\t * authored with an explicit `+` are not bare and are never touched.\n\t */\n\t#stripBarePrefixesIfUniform(payloads: PayloadRow[]): void {\n\t\tlet sawBare = false;\n\t\tlet allLiteralValues = true;\n\t\tfor (const row of payloads) {\n\t\t\tif (!row.bare || row.text.trim().length === 0) continue;\n\t\t\tsawBare = true;\n\t\t\tconst stripped = stripOneLeadingHashlinePrefix(row.text);\n\t\t\tif (stripped === row.text) return;\n\t\t\tallLiteralValues &&= BARE_LITERAL_VALUE_RE.test(stripped);\n\t\t}\n\t\tif (!sawBare) return;\n\t\t// A body where every stripped remainder is a lone quoted/numeric literal\n\t\t// (optionally comma-terminated) is the shape of a numeric-keyed dict or\n\t\t// YAML mapping (`1: \"one\",`), not read-output paste; stripping the \"N:\"\n\t\t// keys would mangle every line. Leave such bodies untouched.\n\t\tif (allLiteralValues) return;\n\t\tfor (const row of payloads) {\n\t\t\tif (row.bare && row.text.trim().length > 0) row.text = stripOneLeadingHashlinePrefix(row.text);\n\t\t}\n\t}\n\n\t#pushInsert(cursor: Cursor, text: string, lineNum: number, mode?: \"replacement\"): void {\n\t\tthis.#edits.push({\n\t\t\tkind: \"insert\",\n\t\t\tcursor: cloneCursor(cursor),\n\t\t\ttext,\n\t\t\tlineNum,\n\t\t\tindex: this.#editIndex++,\n\t\t\t...(mode === undefined ? {} : { mode }),\n\t\t});\n\t}\n\n\t#pushDelete(anchor: Anchor, lineNum: number): void {\n\t\tthis.#edits.push({ kind: \"delete\", anchor: { ...anchor }, lineNum, index: this.#editIndex++ });\n\t}\n\n\t#pushBlock(anchor: Anchor, payloads: readonly PayloadRow[], lineNum: number, mode?: \"insert_after\"): void {\n\t\tthis.#edits.push({\n\t\t\tkind: \"block\",\n\t\t\tanchor: { ...anchor },\n\t\t\tpayloads: payloads.map(payload => payload.text),\n\t\t\t...(mode === undefined ? {} : { mode }),\n\t\t\tlineNum,\n\t\t\tindex: this.#editIndex++,\n\t\t});\n\t}\n\n\t#emitPayloadRows(cursor: Cursor, payloads: readonly PayloadRow[], lineNum: number, mode?: \"replacement\"): void {\n\t\tfor (const payload of payloads) this.#pushInsert(cursor, payload.text, lineNum, mode);\n\t}\n\n\t#flushPending(): void {\n\t\tconst pending = this.#pending;\n\t\tif (!pending) return;\n\t\tconst { target, lineNum, payloads } = pending;\n\t\tthis.#stripBarePrefixesIfUniform(payloads);\n\t\tthis.#pending = undefined;\n\t\tif (target.kind === \"delete\") {\n\t\t\tfor (const anchor of expandRange(target.range)) this.#pushDelete(anchor, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"delete_block\") {\n\t\t\t// A block edit with no payloads resolves to a pure block deletion.\n\t\t\tthis.#pushBlock(target.anchor, [], lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"block\") {\n\t\t\tif (payloads.length === 0) throw new Error(`line ${lineNum}: ${EMPTY_BLOCK}`);\n\t\t\tthis.#pushBlock(target.anchor, payloads, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"insert_after_block\") {\n\t\t\tif (payloads.length === 0) throw new Error(`line ${lineNum}: ${EMPTY_INSERT}`);\n\t\t\tthis.#pushBlock(target.anchor, payloads, lineNum, \"insert_after\");\n\t\t\treturn;\n\t\t}\n\t\tif (payloads.length === 0) {\n\t\t\tif (target.kind === \"replace\") {\n\t\t\t\tfor (const anchor of expandRange(target.range)) this.#pushDelete(anchor, lineNum);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new Error(`line ${lineNum}: ${EMPTY_INSERT}`);\n\t\t}\n\t\tif (target.kind === \"replace\") {\n\t\t\tconst cursor: Cursor = { kind: \"before_anchor\", anchor: { ...target.range.start } };\n\t\t\tthis.#emitPayloadRows(cursor, payloads, lineNum, \"replacement\");\n\t\t\tfor (const anchor of expandRange(target.range)) this.#pushDelete(anchor, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"insert_before\") {\n\t\t\tthis.#emitPayloadRows({ kind: \"before_anchor\", anchor: { ...target.anchor } }, payloads, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"insert_after\") {\n\t\t\tthis.#emitPayloadRows({ kind: \"after_anchor\", anchor: { ...target.anchor } }, payloads, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tconst cursor: Cursor = target.kind === \"bof\" ? { kind: \"bof\" } : { kind: \"eof\" };\n\t\tthis.#emitPayloadRows(cursor, payloads, lineNum);\n\t}\n}\n\nfunction drain(executor: Executor, tokenizer: Tokenizer): { edits: Edit[]; warnings: string[] } {\n\tfor (const token of tokenizer.end()) executor.feed(token);\n\treturn executor.end();\n}\n\nexport function parsePatch(diff: string): { edits: Edit[]; warnings: string[] } {\n\tconst tokenizer = new Tokenizer();\n\tconst executor = new Executor();\n\tfor (const token of tokenizer.feed(diff)) executor.feed(token);\n\treturn drain(executor, tokenizer);\n}\n\nexport function parsePatchStreaming(diff: string): { edits: Edit[]; warnings: string[] } {\n\tconst tokenizer = new Tokenizer();\n\tconst executor = new Executor();\n\tfor (const token of tokenizer.feed(diff)) executor.feed(token);\n\tfor (const token of tokenizer.end()) executor.feed(token);\n\treturn executor.endStreaming();\n}\n"]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { Filesystem } from "./fs.js";
|
|
2
|
+
import type { Patch, PatchSection } from "./input.js";
|
|
3
|
+
import { type LineEnding } from "./normalize.js";
|
|
4
|
+
import { Recovery } from "./recovery.js";
|
|
5
|
+
import type { SnapshotStore } from "./snapshots.js";
|
|
6
|
+
import type { ApplyResult, BlockResolution, BlockResolver } from "./types.js";
|
|
7
|
+
export interface PatcherOptions {
|
|
8
|
+
/** Storage backend used for all reads and writes. */
|
|
9
|
+
fs: Filesystem;
|
|
10
|
+
/** Snapshot store that minted and resolves hashline section tags. Required. */
|
|
11
|
+
snapshots: SnapshotStore;
|
|
12
|
+
/**
|
|
13
|
+
* Resolves `replace block N:` anchors to concrete line spans via tree-sitter.
|
|
14
|
+
* Optional: when omitted, any `replace block N:` edit throws on apply (the
|
|
15
|
+
* host did not wire a resolver). Plain line-range ops never need it.
|
|
16
|
+
*/
|
|
17
|
+
blockResolver?: BlockResolver;
|
|
18
|
+
}
|
|
19
|
+
/** Per-section result returned by {@link Patcher.apply} / {@link Patcher.commit}. */
|
|
20
|
+
export interface PatchSectionResult {
|
|
21
|
+
/** Section path (as authored, after cwd-resolution at parse time). */
|
|
22
|
+
path: string;
|
|
23
|
+
/** Filesystem-canonical key for this section (e.g. absolute path). */
|
|
24
|
+
canonicalPath: string;
|
|
25
|
+
/** `"noop"` when the apply produced no change; otherwise `"create"` / `"update"`. */
|
|
26
|
+
op: "create" | "update" | "noop";
|
|
27
|
+
/** Pre-edit text (LF-normalized, BOM-stripped). */
|
|
28
|
+
before: string;
|
|
29
|
+
/** Post-edit text (LF-normalized, BOM-stripped). For `"noop"` equals `before`. */
|
|
30
|
+
after: string;
|
|
31
|
+
/** Same text as `after` but with the original BOM and line ending restored. */
|
|
32
|
+
persisted: string;
|
|
33
|
+
/** Final text that the {@link Filesystem} actually wrote (may differ if the FS transformed it). */
|
|
34
|
+
written: string;
|
|
35
|
+
/** 4-hex content-hash tag for `after`. Use to anchor follow-up edits. */
|
|
36
|
+
fileHash: string;
|
|
37
|
+
/** Hashline section header (`[path#tag]`) of the post-edit content. */
|
|
38
|
+
header: string;
|
|
39
|
+
/** 1-indexed first changed line in `after`, or `undefined` for noops. */
|
|
40
|
+
firstChangedLine?: number;
|
|
41
|
+
/** Warnings collected by the parser, applier, and (optionally) recovery. */
|
|
42
|
+
warnings: string[];
|
|
43
|
+
/**
|
|
44
|
+
* Resolved spans for any `replace block`/`delete block` ops, present when the
|
|
45
|
+
* apply matched the tagged content. Undefined for patches with no block ops
|
|
46
|
+
* (and for resolutions routed through drift recovery, where numbers shift).
|
|
47
|
+
*/
|
|
48
|
+
blockResolutions?: BlockResolution[];
|
|
49
|
+
}
|
|
50
|
+
export interface PatcherApplyResult {
|
|
51
|
+
sections: PatchSectionResult[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Opaque token returned by {@link Patcher.prepare}. Carries the section, the
|
|
55
|
+
* raw file content read off disk, and the in-memory apply result.
|
|
56
|
+
* {@link Patcher.commit} just writes the {@link PreparedSection.applyResult}.
|
|
57
|
+
*/
|
|
58
|
+
export declare class PreparedSection {
|
|
59
|
+
readonly section: PatchSection;
|
|
60
|
+
readonly canonicalPath: string;
|
|
61
|
+
readonly exists: boolean;
|
|
62
|
+
readonly rawContent: string;
|
|
63
|
+
readonly bom: string;
|
|
64
|
+
readonly lineEnding: LineEnding;
|
|
65
|
+
readonly normalized: string;
|
|
66
|
+
readonly applyResult: ApplyResult;
|
|
67
|
+
readonly parseWarnings: readonly string[];
|
|
68
|
+
/** @internal */
|
|
69
|
+
constructor(section: PatchSection, canonicalPath: string, exists: boolean, rawContent: string, bom: string, lineEnding: LineEnding, normalized: string, applyResult: ApplyResult, parseWarnings: readonly string[]);
|
|
70
|
+
/** Convenience: returns true when the apply produced no change. */
|
|
71
|
+
get isNoop(): boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* High-level patcher. Wires a {@link Filesystem} and a required
|
|
75
|
+
* {@link SnapshotStore} together with the parsing + applying core.
|
|
76
|
+
*
|
|
77
|
+
* Construct once per FS configuration; reuse across patches.
|
|
78
|
+
*/
|
|
79
|
+
export declare class Patcher {
|
|
80
|
+
#private;
|
|
81
|
+
readonly fs: Filesystem;
|
|
82
|
+
readonly snapshots: SnapshotStore;
|
|
83
|
+
readonly recovery: Recovery;
|
|
84
|
+
readonly blockResolver: BlockResolver | undefined;
|
|
85
|
+
constructor(options: PatcherOptions);
|
|
86
|
+
/**
|
|
87
|
+
* Apply every section in `patch`. `prepare` runs the full apply for each
|
|
88
|
+
* section in memory before any write hits the filesystem, so a
|
|
89
|
+
* multi-section batch fails before writing on parse/validation/apply errors.
|
|
90
|
+
* Write commits are sequential rather than transactional; if a write fails,
|
|
91
|
+
* earlier sections may already be on disk and are listed in the thrown error.
|
|
92
|
+
* Returns one {@link PatchSectionResult} per section in the original patch order.
|
|
93
|
+
*/
|
|
94
|
+
apply(patch: Patch): Promise<PatcherApplyResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Run the preflight pass only: read, parse, validate, apply-in-memory.
|
|
97
|
+
* No writes hit the filesystem. Use for CI checks and dry runs.
|
|
98
|
+
*/
|
|
99
|
+
preflight(patch: Patch): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Read a section's target file, parse the section, validate the snapshot
|
|
102
|
+
* tag (with recovery), and apply the edits in memory. Returns a
|
|
103
|
+
* {@link PreparedSection} which can be fed to {@link commit} to land
|
|
104
|
+
* the result on the filesystem.
|
|
105
|
+
*
|
|
106
|
+
* Throws on parse error, missing-file-for-anchored-edit, or unrecovered
|
|
107
|
+
* tag mismatch ({@link MismatchError}).
|
|
108
|
+
*/
|
|
109
|
+
prepare(section: PatchSection): Promise<PreparedSection>;
|
|
110
|
+
/**
|
|
111
|
+
* Commit a previously {@link prepare}d section to the filesystem.
|
|
112
|
+
* Restores line endings and BOM, writes via the {@link Filesystem}, and
|
|
113
|
+
* records a fresh snapshot in the {@link SnapshotStore} keyed by the
|
|
114
|
+
* filesystem-canonical path.
|
|
115
|
+
*/
|
|
116
|
+
commit(prepared: PreparedSection): Promise<PatchSectionResult>;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=patcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patcher.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/patcher.ts"],"names":[],"mappings":"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","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * High-level patch orchestrator. Reads each section's target file via the\n * configured {@link Filesystem}, strips BOM and normalizes line endings,\n * validates the section snapshot tag (with {@link Recovery}), applies the\n * result back through the same {@link Filesystem}.\n *\n * Two layers:\n * - {@link Patcher.apply} — high-level, preflight-atomic. It validates and\n * applies every section in memory before any write hits disk, then commits\n * in order. It is not write-transactional: a mid-batch filesystem failure\n * can leave earlier sections already written, and the thrown error reports\n * which sections landed.\n * - {@link Patcher.prepare} / {@link Patcher.commit} — granular primitives\n * for callers that need per-section control (e.g. batched LSP flush,\n * custom interleaving). `prepare` performs all the read-side work,\n * validates the section snapshot tag (with recovery), and applies the\n * edits in memory. `commit` writes the prepared result and records a\n * fresh snapshot.\n *\n * Because `prepare` already runs the full apply, a multi-section batch fails\n * before any write when parsing, stale-tag validation, or in-memory apply fails.\n *\n * The patcher itself is stateless across calls; reuse one instance per\n * filesystem configuration.\n */\nimport { applyEdits } from \"./apply.js\";\nimport { hasBlockEdit, resolveBlockEdits } from \"./block.js\";\nimport { computeFileHash, formatHashlineHeader } from \"./format.js\";\nimport type { Filesystem, WriteResult } from \"./fs.js\";\nimport { isNotFound } from \"./fs.js\";\nimport type { Patch, PatchSection } from \"./input.js\";\nimport { HEADTAIL_DRIFT_WARNING, missingSnapshotTagMessage } from \"./messages.js\";\nimport { MismatchError } from \"./mismatch.js\";\nimport { detectLineEnding, type LineEnding, normalizeToLF, restoreLineEndings, stripBom } from \"./normalize.js\";\nimport { Recovery, type RecoveryResult } from \"./recovery.js\";\nimport type { SnapshotStore } from \"./snapshots.js\";\nimport type { ApplyResult, BlockResolution, BlockResolver, Edit } from \"./types.js\";\n\nexport interface PatcherOptions {\n\t/** Storage backend used for all reads and writes. */\n\tfs: Filesystem;\n\t/** Snapshot store that minted and resolves hashline section tags. Required. */\n\tsnapshots: SnapshotStore;\n\t/**\n\t * Resolves `replace block N:` anchors to concrete line spans via tree-sitter.\n\t * Optional: when omitted, any `replace block N:` edit throws on apply (the\n\t * host did not wire a resolver). Plain line-range ops never need it.\n\t */\n\tblockResolver?: BlockResolver;\n}\n\n/** Per-section result returned by {@link Patcher.apply} / {@link Patcher.commit}. */\nexport interface PatchSectionResult {\n\t/** Section path (as authored, after cwd-resolution at parse time). */\n\tpath: string;\n\t/** Filesystem-canonical key for this section (e.g. absolute path). */\n\tcanonicalPath: string;\n\t/** `\"noop\"` when the apply produced no change; otherwise `\"create\"` / `\"update\"`. */\n\top: \"create\" | \"update\" | \"noop\";\n\t/** Pre-edit text (LF-normalized, BOM-stripped). */\n\tbefore: string;\n\t/** Post-edit text (LF-normalized, BOM-stripped). For `\"noop\"` equals `before`. */\n\tafter: string;\n\t/** Same text as `after` but with the original BOM and line ending restored. */\n\tpersisted: string;\n\t/** Final text that the {@link Filesystem} actually wrote (may differ if the FS transformed it). */\n\twritten: string;\n\t/** 4-hex content-hash tag for `after`. Use to anchor follow-up edits. */\n\tfileHash: string;\n\t/** Hashline section header (`[path#tag]`) of the post-edit content. */\n\theader: string;\n\t/** 1-indexed first changed line in `after`, or `undefined` for noops. */\n\tfirstChangedLine?: number;\n\t/** Warnings collected by the parser, applier, and (optionally) recovery. */\n\twarnings: string[];\n\t/**\n\t * Resolved spans for any `replace block`/`delete block` ops, present when the\n\t * apply matched the tagged content. Undefined for patches with no block ops\n\t * (and for resolutions routed through drift recovery, where numbers shift).\n\t */\n\tblockResolutions?: BlockResolution[];\n}\n\nexport interface PatcherApplyResult {\n\tsections: PatchSectionResult[];\n}\n\n/**\n * Opaque token returned by {@link Patcher.prepare}. Carries the section, the\n * raw file content read off disk, and the in-memory apply result.\n * {@link Patcher.commit} just writes the {@link PreparedSection.applyResult}.\n */\nexport class PreparedSection {\n\treadonly section: PatchSection;\n\treadonly canonicalPath: string;\n\treadonly exists: boolean;\n\treadonly rawContent: string;\n\treadonly bom: string;\n\treadonly lineEnding: LineEnding;\n\treadonly normalized: string;\n\treadonly applyResult: ApplyResult;\n\treadonly parseWarnings: readonly string[];\n\t/** @internal */\n\tconstructor(\n\t\tsection: PatchSection,\n\t\tcanonicalPath: string,\n\t\texists: boolean,\n\t\trawContent: string,\n\t\tbom: string,\n\t\tlineEnding: LineEnding,\n\t\tnormalized: string,\n\t\tapplyResult: ApplyResult,\n\t\tparseWarnings: readonly string[],\n\t) {\n\t\tthis.section = section;\n\t\tthis.canonicalPath = canonicalPath;\n\t\tthis.exists = exists;\n\t\tthis.rawContent = rawContent;\n\t\tthis.bom = bom;\n\t\tthis.lineEnding = lineEnding;\n\t\tthis.normalized = normalized;\n\t\tthis.applyResult = applyResult;\n\t\tthis.parseWarnings = parseWarnings;\n\t}\n\n\t/** Convenience: returns true when the apply produced no change. */\n\tget isNoop(): boolean {\n\t\treturn this.applyResult.text === this.normalized;\n\t}\n}\n\nfunction hasAnchorScopedEdit(edits: readonly Edit[]): boolean {\n\treturn edits.some(edit => {\n\t\tif (edit.kind === \"delete\") return true;\n\t\t// A `replace block N:` edit anchors to concrete content on line N.\n\t\tif (edit.kind === \"block\") return true;\n\t\treturn edit.cursor.kind === \"before_anchor\" || edit.cursor.kind === \"after_anchor\";\n\t});\n}\n\nfunction assertSectionHashPresent(sectionPath: string, fileHash: string | undefined): void {\n\tif (fileHash !== undefined) return;\n\tthrow new Error(missingSnapshotTagMessage(sectionPath));\n}\n\nfunction recoveryToApplyResult(result: RecoveryResult): ApplyResult {\n\treturn {\n\t\ttext: result.text,\n\t\tfirstChangedLine: result.firstChangedLine,\n\t\twarnings: result.warnings,\n\t};\n}\nfunction mergeWarnings(...sources: ReadonlyArray<readonly string[] | undefined>): string[] {\n\tconst out: string[] = [];\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tfor (const warning of source) out.push(warning);\n\t}\n\treturn out;\n}\n\nfunction assertUniqueCanonicalPaths(prepared: readonly PreparedSection[]): void {\n\tconst seen = new Map<string, string>();\n\tfor (const entry of prepared) {\n\t\tconst previous = seen.get(entry.canonicalPath);\n\t\tif (previous !== undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Multiple hashline sections resolve to the same file (${previous} and ${entry.section.path}). Merge their ops under one header before applying.`,\n\t\t\t);\n\t\t}\n\t\tseen.set(entry.canonicalPath, entry.section.path);\n\t}\n}\n\n/**\n * High-level patcher. Wires a {@link Filesystem} and a required\n * {@link SnapshotStore} together with the parsing + applying core.\n *\n * Construct once per FS configuration; reuse across patches.\n */\nexport class Patcher {\n\treadonly fs: Filesystem;\n\treadonly snapshots: SnapshotStore;\n\treadonly recovery: Recovery;\n\treadonly blockResolver: BlockResolver | undefined;\n\n\tconstructor(options: PatcherOptions) {\n\t\tif (!options.snapshots) {\n\t\t\tthrow new Error(\"Hashline Patcher requires a SnapshotStore; section tags are opaque store pointers.\");\n\t\t}\n\t\tthis.fs = options.fs;\n\t\tthis.snapshots = options.snapshots;\n\t\tthis.recovery = new Recovery(options.snapshots);\n\t\tthis.blockResolver = options.blockResolver;\n\t}\n\n\t/**\n\t * Apply every section in `patch`. `prepare` runs the full apply for each\n\t * section in memory before any write hits the filesystem, so a\n\t * multi-section batch fails before writing on parse/validation/apply errors.\n\t * Write commits are sequential rather than transactional; if a write fails,\n\t * earlier sections may already be on disk and are listed in the thrown error.\n\t * Returns one {@link PatchSectionResult} per section in the original patch order.\n\t */\n\tasync apply(patch: Patch): Promise<PatcherApplyResult> {\n\t\t// Single-section fast path.\n\t\tif (patch.sections.length === 1) {\n\t\t\tconst prepared = await this.prepare(patch.sections[0]);\n\t\t\treturn { sections: [await this.commit(prepared)] };\n\t\t}\n\n\t\t// Prepare every section first so any failure (stale hash, missing\n\t\t// file, parse error, in-memory no-op) surfaces before any write.\n\t\tconst prepared: PreparedSection[] = [];\n\t\tfor (const section of patch.sections) prepared.push(await this.prepare(section));\n\t\tassertUniqueCanonicalPaths(prepared);\n\t\tfor (const entry of prepared) {\n\t\t\tif (entry.isNoop) {\n\t\t\t\tthrow new Error(`Edits to ${entry.section.path} resulted in no changes being made.`);\n\t\t\t}\n\t\t}\n\n\t\tconst results: PatchSectionResult[] = [];\n\t\tfor (let index = 0; index < prepared.length; index++) {\n\t\t\ttry {\n\t\t\t\tresults.push(await this.commit(prepared[index]));\n\t\t\t} catch (error) {\n\t\t\t\t// A mid-batch write failure leaves earlier sections on disk with no\n\t\t\t\t// rollback; report exactly which sections landed so the caller can\n\t\t\t\t// re-issue only the missing ones instead of double-applying.\n\t\t\t\tconst written = prepared.slice(0, index).map(entry => entry.section.path);\n\t\t\t\tconst notWritten = prepared.slice(index + 1).map(entry => entry.section.path);\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to write ${prepared[index].section.path}: ${message}` +\n\t\t\t\t\t\t(written.length > 0 ? ` Sections already written: ${written.join(\", \")}.` : \"\") +\n\t\t\t\t\t\t(notWritten.length > 0 ? ` Sections not written: ${notWritten.join(\", \")}.` : \"\"),\n\t\t\t\t\t{ cause: error },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn { sections: results };\n\t}\n\n\t/**\n\t * Run the preflight pass only: read, parse, validate, apply-in-memory.\n\t * No writes hit the filesystem. Use for CI checks and dry runs.\n\t */\n\tasync preflight(patch: Patch): Promise<void> {\n\t\tconst prepared: PreparedSection[] = [];\n\t\tfor (const section of patch.sections) prepared.push(await this.prepare(section));\n\t\tassertUniqueCanonicalPaths(prepared);\n\t\tfor (const entry of prepared) {\n\t\t\tif (entry.isNoop) {\n\t\t\t\tthrow new Error(`Edits to ${entry.section.path} resulted in no changes being made.`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Read a section's target file, parse the section, validate the snapshot\n\t * tag (with recovery), and apply the edits in memory. Returns a\n\t * {@link PreparedSection} which can be fed to {@link commit} to land\n\t * the result on the filesystem.\n\t *\n\t * Throws on parse error, missing-file-for-anchored-edit, or unrecovered\n\t * tag mismatch ({@link MismatchError}).\n\t */\n\tasync prepare(section: PatchSection): Promise<PreparedSection> {\n\t\tconst { edits, warnings: parseWarnings } = section.parse();\n\t\tassertSectionHashPresent(section.path, section.fileHash);\n\n\t\tconst canonicalPath = this.fs.canonicalPath(section.path);\n\t\tawait this.fs.preflightWrite(section.path);\n\t\tconst { exists, rawContent } = await this.#tryRead(section.path);\n\t\tif (!exists) {\n\t\t\tthrow new Error(`File not found: ${section.path}. Use the write tool to create new files.`);\n\t\t}\n\n\t\tconst { bom, text } = stripBom(rawContent);\n\t\tconst lineEnding = detectLineEnding(text);\n\t\tconst normalized = normalizeToLF(text);\n\n\t\tconst applyResult = this.#applyWithRecovery({\n\t\t\tsection,\n\t\t\tcanonicalPath,\n\t\t\texists,\n\t\t\tnormalized,\n\t\t\tedits,\n\t\t});\n\n\t\treturn new PreparedSection(\n\t\t\tsection,\n\t\t\tcanonicalPath,\n\t\t\texists,\n\t\t\trawContent,\n\t\t\tbom,\n\t\t\tlineEnding,\n\t\t\tnormalized,\n\t\t\tapplyResult,\n\t\t\tparseWarnings,\n\t\t);\n\t}\n\n\t/**\n\t * Commit a previously {@link prepare}d section to the filesystem.\n\t * Restores line endings and BOM, writes via the {@link Filesystem}, and\n\t * records a fresh snapshot in the {@link SnapshotStore} keyed by the\n\t * filesystem-canonical path.\n\t */\n\tasync commit(prepared: PreparedSection): Promise<PatchSectionResult> {\n\t\tconst { section, normalized, bom, lineEnding, parseWarnings, exists, applyResult, canonicalPath } = prepared;\n\t\tconst after = applyResult.text;\n\t\tconst warnings = mergeWarnings(parseWarnings, applyResult.warnings);\n\n\t\tif (after === normalized) {\n\t\t\tconst hash = this.#recordFullSnapshot(canonicalPath, normalized);\n\t\t\treturn {\n\t\t\t\tpath: section.path,\n\t\t\t\tcanonicalPath,\n\t\t\t\top: \"noop\",\n\t\t\t\tbefore: normalized,\n\t\t\t\tafter: normalized,\n\t\t\t\tpersisted: prepared.rawContent,\n\t\t\t\twritten: prepared.rawContent,\n\t\t\t\tfileHash: hash,\n\t\t\t\theader: formatHashlineHeader(section.path, hash),\n\t\t\t\twarnings,\n\t\t\t};\n\t\t}\n\n\t\tconst persisted = bom + restoreLineEndings(after, lineEnding);\n\t\tconst write: WriteResult = await this.fs.writeText(section.path, persisted);\n\t\tconst fileHash = this.#recordFullSnapshot(canonicalPath, after);\n\t\tconst op = exists ? \"update\" : \"create\";\n\n\t\treturn {\n\t\t\tpath: section.path,\n\t\t\tcanonicalPath,\n\t\t\top,\n\t\t\tbefore: normalized,\n\t\t\tafter,\n\t\t\tpersisted,\n\t\t\twritten: write.text,\n\t\t\tfileHash,\n\t\t\theader: formatHashlineHeader(section.path, fileHash),\n\t\t\tfirstChangedLine: applyResult.firstChangedLine,\n\t\t\tblockResolutions: applyResult.blockResolutions,\n\t\t\twarnings,\n\t\t};\n\t}\n\n\tasync #tryRead(path: string): Promise<{ exists: boolean; rawContent: string }> {\n\t\ttry {\n\t\t\tconst content = await this.fs.readText(path);\n\t\t\treturn { exists: true, rawContent: content };\n\t\t} catch (error) {\n\t\t\tif (isNotFound(error)) return { exists: false, rawContent: \"\" };\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t#recordFullSnapshot(canonicalPath: string, normalized: string): string {\n\t\treturn this.snapshots.record(canonicalPath, normalized);\n\t}\n\t#mismatchError(\n\t\tsection: PatchSection,\n\t\tcanonicalPath: string,\n\t\tnormalized: string,\n\t\texpected: string,\n\t\thashRecognized: boolean,\n\t): MismatchError {\n\t\tconst actualFileHash = this.#recordFullSnapshot(canonicalPath, normalized);\n\t\treturn new MismatchError({\n\t\t\tpath: section.path,\n\t\t\texpectedFileHash: expected,\n\t\t\tactualFileHash,\n\t\t\tfileLines: normalized.split(\"\\n\"),\n\t\t\tanchorLines: section.collectAnchorLines(),\n\t\t\thashRecognized,\n\t\t});\n\t}\n\n\t#applyWithRecovery(args: {\n\t\tsection: PatchSection;\n\t\tcanonicalPath: string;\n\t\texists: boolean;\n\t\tnormalized: string;\n\t\tedits: readonly Edit[];\n\t}): ApplyResult {\n\t\tconst { section, canonicalPath, exists, normalized, edits } = args;\n\t\tconst expected = exists ? section.fileHash : undefined;\n\t\tconst expectedSnapshot = expected === undefined ? null : this.snapshots.byHash(canonicalPath, expected);\n\t\tconst liveHashMatches = expected !== undefined && computeFileHash(normalized) === expected;\n\t\tconst liveSnapshot = expected === undefined ? null : this.snapshots.byHashAndText(canonicalPath, expected, normalized);\n\t\tconst liveMatches = liveHashMatches && liveSnapshot !== null;\n\t\tif (liveHashMatches && !liveMatches) {\n\t\t\tthrow this.#mismatchError(section, canonicalPath, normalized, expected, expectedSnapshot !== null);\n\t\t}\n\n\t\t// Resolve `replace block N:` edits to concrete ranges before recovery\n\t\t// runs. Block anchors are expressed against the snapshot the section tag\n\t\t// names, so resolve against that exact text:\n\t\t// - live content matches the tag (or there is no tag) → resolve against\n\t\t// the live, normalized content;\n\t\t// - the file drifted → resolve against the tagged snapshot's text so the\n\t\t// resulting ranges flow through the 3-way-merge recovery below.\n\t\t// When a block edit needs the tagged snapshot but it is unavailable, the\n\t\t// range cannot be placed safely — reject with a MismatchError (re-read).\n\t\tconst blockResolutions: BlockResolution[] = [];\n\t\tconst resolveWarnings: string[] = [];\n\t\tlet resolved: readonly Edit[] = edits;\n\t\tif (hasBlockEdit(edits)) {\n\t\t\tconst baseText =\n\t\t\t\texpected === undefined || liveMatches ? normalized : expectedSnapshot?.text;\n\t\t\tif (baseText === undefined) {\n\t\t\t\tthrow this.#mismatchError(section, canonicalPath, normalized, expected ?? \"\", false);\n\t\t\t}\n\t\t\tresolved = resolveBlockEdits(edits, baseText, section.path, this.blockResolver, {\n\t\t\t\tonUnresolved: \"throw\",\n\t\t\t\tonResolved: resolution => blockResolutions.push(resolution),\n\t\t\t\tonWarning: warning => resolveWarnings.push(warning),\n\t\t\t});\n\t\t}\n\t\tconst withResolveWarnings = (result: ApplyResult): ApplyResult =>\n\t\t\tresolveWarnings.length === 0\n\t\t\t\t? result\n\t\t\t\t: { ...result, warnings: [...resolveWarnings, ...(result.warnings ?? [])] };\n\n\t\t// No tag, or the tag still names the live content: an edit anchored at any\n\t\t// line is safe to apply, and the resolved block spans line up with what\n\t\t// the caller read, so echo them back. (A drifted file falls through to\n\t\t// recovery below, where line numbers shift, so resolutions are dropped.)\n\t\tif (expected === undefined || liveMatches) {\n\t\t\tconst result = applyEdits(normalized, resolved);\n\t\t\treturn withResolveWarnings(blockResolutions.length > 0 ? { ...result, blockResolutions } : result);\n\t\t}\n\t\t// Head/tail-only inserts are position-stable: \"start\"/\"end\" cannot move\n\t\t// with content drift, so a stale tag is non-fatal. Apply onto the live\n\t\t// content and warn instead of hard-failing — unlike an anchored\n\t\t// mismatch, which cannot be safely relocated and must reject.\n\t\tif (!hasAnchorScopedEdit(resolved)) {\n\t\t\tconst result = applyEdits(normalized, resolved);\n\t\t\treturn withResolveWarnings({ ...result, warnings: [HEADTAIL_DRIFT_WARNING, ...(result.warnings ?? [])] });\n\t\t}\n\t\t// File drifted: try to replay the edit against the version the tag\n\t\t// names and 3-way-merge it onto the live content.\n\t\tconst recovered = this.recovery.tryRecover({\n\t\t\tpath: canonicalPath,\n\t\t\tcurrentText: normalized,\n\t\t\tfileHash: expected,\n\t\t\tedits: resolved,\n\t\t});\n\t\tif (recovered) return withResolveWarnings(recoveryToApplyResult(recovered));\n\t\tconst hashRecognized = expectedSnapshot !== null;\n\t\tthrow this.#mismatchError(section, canonicalPath, normalized, expected, hashRecognized);\n\t}\n}\n"]}
|