dsh-hashline-edit 0.1.1 → 0.2.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/README.md +70 -69
- package/cordis.patch.yml +3 -3
- package/lib/index.js +4151 -4516
- package/lib/types/dsh/contracts.d.ts +69 -6
- package/lib/types/dsh/fs.d.ts +38 -3
- package/lib/types/dsh/guidance.d.ts +5 -5
- package/lib/types/dsh/hide.d.ts +79 -0
- package/lib/types/dsh/host-compat.d.ts +7 -78
- package/lib/types/dsh/index.d.ts +21 -9
- package/lib/types/dsh/metrics/events.d.ts +8 -1
- package/lib/types/dsh/metrics/instrument.d.ts +20 -14
- package/lib/types/dsh/metrics/observer.d.ts +28 -0
- package/lib/types/dsh/sandbox.d.ts +2 -2
- package/lib/types/dsh/shell-hook.d.ts +17 -8
- package/lib/types/dsh/shell-view.d.ts +52 -0
- package/lib/types/dsh/tools/apply-patch.d.ts +38 -0
- package/lib/types/dsh/tools/context.d.ts +19 -1
- package/lib/types/dsh/tools/output.d.ts +32 -4
- package/lib/types/dsh/tools/read.d.ts +12 -15
- package/lib/types/dsh/tools/undo.d.ts +5 -0
- package/lib/types/dsh/write-hook.d.ts +8 -22
- package/lib/types/src/apply-patch/commit.d.ts +49 -0
- package/lib/types/src/apply-patch/grammar.d.ts +29 -0
- package/lib/types/src/apply-patch/lifecycle.d.ts +32 -0
- package/lib/types/src/apply-patch/plan.d.ts +50 -0
- package/lib/types/src/apply-patch/render.d.ts +51 -0
- package/lib/types/src/apply-patch/resolve.d.ts +129 -0
- package/lib/types/src/apply-patch/tool.d.ts +54 -0
- package/lib/types/src/apply-patch/types.d.ts +362 -0
- package/lib/types/src/domain-errors.d.ts +16 -2
- package/lib/types/src/edit-undo.d.ts +11 -2
- package/lib/types/src/file-content/detection.d.ts +17 -0
- package/lib/types/src/file-content/index.d.ts +2 -2
- package/lib/types/src/file-content/preview.d.ts +1 -1
- package/lib/types/src/fs-lifecycle.d.ts +44 -0
- package/lib/types/src/hashline/resolve.d.ts +6 -0
- package/lib/types/src/host-utils.d.ts +63 -0
- package/lib/types/src/mutation-engine/batch-span-gate.d.ts +6 -0
- package/lib/types/src/mutation-engine/edit-request.d.ts +34 -0
- package/lib/types/src/mutation-engine/pipeline.d.ts +18 -3
- package/lib/types/src/mutation-engine/types.d.ts +50 -58
- package/lib/types/src/read-expansion/grammar-manifest.d.ts +4 -2
- package/lib/types/src/read-expansion/java-decoration.d.ts +21 -0
- package/lib/types/src/read-expansion/span-expansion.d.ts +6 -2
- package/lib/types/src/read-expansion/span-kind.d.ts +3 -2
- package/lib/types/src/read-expansion/vue-decoration.d.ts +14 -0
- package/lib/types/src/served-session/serve-render.d.ts +9 -3
- package/lib/types/src/undo-store.d.ts +17 -1
- package/package.json +18 -22
- package/prompts/apply-patch-guidelines.md +10 -0
- package/prompts/apply-patch.md +1 -0
- package/prompts/read-guidelines.md +5 -5
- package/prompts/read.md +1 -1
- package/prompts/undo-last-edit-guidelines.md +2 -2
- package/prompts/undo-last-edit-snippet.md +1 -1
- package/prompts/undo-last-edit.md +1 -1
- package/src/apply-patch/commit.ts +190 -0
- package/src/apply-patch/grammar.ts +620 -0
- package/src/apply-patch/index.ts +17 -0
- package/src/apply-patch/lifecycle.ts +195 -0
- package/src/apply-patch/plan.ts +800 -0
- package/src/apply-patch/render.ts +137 -0
- package/src/apply-patch/resolve.ts +470 -0
- package/src/apply-patch/tool.ts +257 -0
- package/src/apply-patch/types.ts +387 -0
- package/src/cut-repair.ts +77 -11
- package/src/domain-errors.ts +58 -10
- package/src/edit-undo.ts +379 -25
- package/src/file-content/detection.ts +34 -0
- package/src/file-content/index.ts +2 -2
- package/src/file-content/preview.ts +1 -1
- package/src/file-content/served-header.ts +1 -1
- package/src/fs-lifecycle.ts +201 -0
- package/src/hash-store.ts +18 -1
- package/src/hashline/lease-resolve.ts +14 -8
- package/src/hashline/resolve.ts +30 -27
- package/src/host-utils.ts +224 -0
- package/src/mutation-engine/batch-span-gate.ts +17 -4
- package/src/mutation-engine/edit-request.ts +39 -0
- package/src/mutation-engine/pipeline.ts +535 -63
- package/src/mutation-engine/types.ts +49 -65
- package/src/mutation-queue.ts +1 -1
- package/src/prompts.ts +0 -28
- package/src/read-expansion/ast-provider.ts +4 -0
- package/src/read-expansion/grammar-manifest.ts +21 -2
- package/src/read-expansion/java-decoration.ts +34 -0
- package/src/read-expansion/span-expansion.ts +6 -2
- package/src/read-expansion/span-kind.ts +3 -1
- package/src/read-expansion/vue-decoration.ts +43 -0
- package/src/served-session/serve-render.ts +5 -5
- package/src/undo-store.ts +74 -5
- package/lib/types/dsh/encoding-bridge.d.ts +0 -25
- package/lib/types/dsh/encoding.d.ts +0 -144
- package/lib/types/dsh/tools/edit.d.ts +0 -10
- package/lib/types/src/edit-tool.d.ts +0 -41
- package/lib/types/src/mutation-engine/engine.d.ts +0 -31
- package/lib/types/src/payload-contract.d.ts +0 -124
- package/prompts/edit-guidelines.md +0 -10
- package/prompts/edit-snippet.md +0 -1
- package/prompts/edit.md +0 -1
- package/src/bash-classifier.ts +0 -1626
- package/src/bash-conversion/decide.ts +0 -812
- package/src/bash-display-transform.ts +0 -50
- package/src/bash-multifile-config.ts +0 -239
- package/src/bash-view-config.ts +0 -158
- package/src/edit-normalize.ts +0 -8
- package/src/edit-render.ts +0 -161
- package/src/edit-tool.ts +0 -116
- package/src/edit.ts +0 -136
- package/src/integrations/pi-lens/command.ts +0 -712
- package/src/integrations/pi-lens/config.ts +0 -148
- package/src/integrations/pi-lens/io-bridge-adapter.ts +0 -234
- package/src/integrations/pi-lens/io-bridge.ts +0 -112
- package/src/integrations/pi-lens/settings-overlay.ts +0 -272
- package/src/lifecycle-hooks/index.ts +0 -1992
- package/src/lifecycle-hooks/types.ts +0 -64
- package/src/mutated-files.ts +0 -75
- package/src/mutation-engine/engine.ts +0 -173
- package/src/mutation-engine/index.ts +0 -17
- package/src/payload-contract.ts +0 -637
- package/src/preview-controller.ts +0 -84
- package/src/read.ts +0 -865
- package/src/search-hook.ts +0 -41
- package/src/store-config.ts +0 -73
- package/src/store-janitor-warning.ts +0 -169
- package/src/store-janitor.ts +0 -539
- package/src/store-kernel/settings.ts +0 -233
- package/src/store.ts +0 -154
- package/src/tui-presenter.ts +0 -134
- package/src/write-hook.ts +0 -130
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Hash-anchored file editing for [DeepSeek Harness](https://github.com/deepseek-ai) (dsh) on Windows.
|
|
4
4
|
|
|
5
|
-
`dsh-hashline-edit` is a dsh bundle that shadows the host's built-in `read`
|
|
5
|
+
`dsh-hashline-edit` is a dsh bundle that shadows the host's built-in `read` with hash-anchored output, registers `apply_patch` as the only file-mutation entry point, and adds `undo_last_edit`. The native `write`/`edit`/`str_replace_editor` surface is hidden; there is no pi host support, because this package targets DeepSeek Harness only. Every line a served read shows carries a stable 4-character base62 anchor (`A–Z`, `a–z`, `0–9`) derived from the file's canonical path and the line's content. An `apply_patch` hunk either names served anchors or matches file content; the model never re-types existing code. Anchors for unchanged lines stay stable across edits, and every successful `apply_patch` call returns fresh anchors for the changed lines, so chained calls need no re-read.
|
|
6
6
|
|
|
7
|
-
The semantic core is the [pi-better-edit](https://github.com/Rianico/pi-better-edit) v2.16.0 hashline engine (MIT). The `dsh/` tree is this repository's DeepSeek Harness host layer: tool registration, filesystem bridge,
|
|
7
|
+
The semantic core is the [pi-better-edit](https://github.com/Rianico/pi-better-edit) v2.16.0 hashline engine (MIT), carried as an internal engine; the pi host integration has been removed. The `dsh/` tree is this repository's DeepSeek Harness host layer: tool registration, filesystem bridge, UTF-8-only admission, prompt guidance, shell integration, write integration and telemetry. See [`dsh/README.md`](dsh/README.md) for the host-layer map.
|
|
8
8
|
|
|
9
9
|
## Requirements
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ The semantic core is the [pi-better-edit](https://github.com/Rianico/pi-better-e
|
|
|
13
13
|
| OS | Windows — this branch targets Windows only (see [`dsh/UPSTREAM.md`](dsh/UPSTREAM.md)) |
|
|
14
14
|
| Node.js | ≥ 24.0.0 |
|
|
15
15
|
| pnpm | 12.x |
|
|
16
|
-
| DeepSeek Harness | plugin-capable build; the repository is developed against `@deepseek-ai/dsh-*@0.2.
|
|
16
|
+
| DeepSeek Harness | plugin-capable build; the repository is developed against `@deepseek-ai/dsh-*@0.2.2-rc.1` (host packages are declared as peer dependencies) |
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
@@ -36,7 +36,7 @@ For a tarball, pack first and add the archive:
|
|
|
36
36
|
|
|
37
37
|
```sh
|
|
38
38
|
pnpm pack
|
|
39
|
-
dsh plugin add ./dsh-hashline-edit-0.1.
|
|
39
|
+
dsh plugin add ./dsh-hashline-edit-0.2.1.tgz
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
The bundle ships `lib/index.js` plus `lib/types/**`; `prompts/` must sit next to `lib/` in the installed package (it is in `files`). Guidance override files are materialized under `<DSH_HOME>/plugins/dsh-hashline-edit/guidance/` and are shared between profiles.
|
|
@@ -55,7 +55,7 @@ p9rX│ return total;
|
|
|
55
55
|
a1bX│}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
Copy only the 4 characters before `│` into `anchor_from` / `anchor_to`. An
|
|
58
|
+
Copy only the 4 characters before `│` into `anchor_from` / `anchor_to`. An `apply_patch` identity hunk targets an inclusive served range; a successful call returns a diff whose modified and context rows carry fresh anchors:
|
|
59
59
|
|
|
60
60
|
```text
|
|
61
61
|
- szJx│ let total = 0;
|
|
@@ -81,51 +81,61 @@ read({
|
|
|
81
81
|
| { around_anchor: string; radius?: number } // radius defaults to 10
|
|
82
82
|
>,
|
|
83
83
|
mode?: "served" | "verbatim", // default "served"
|
|
84
|
-
encoding?: string, // reopen a non-UTF-8 file, e.g. "gbk", "big5", "shift_jis", "utf16le"
|
|
85
84
|
})
|
|
86
85
|
```
|
|
87
86
|
|
|
88
|
-
- **Served mode** returns one `HASH│content` row per line and records every shown row as a lease for this session. A windowed read also includes a `[Lines A-B of N total]` page header. Every row shown is usable by one subsequent
|
|
89
|
-
- **Verbatim mode** returns the same page as plain text with no anchors and serves nothing; an
|
|
90
|
-
- A small requested window may widen to its enclosing function, method, class, section or paragraph; a `[widened …]` note names the block that was served. Expansion applies in both modes.
|
|
91
|
-
- `offset`/`limit` and `windows` are page requests, not anchors: only `served` output grants
|
|
87
|
+
- **Served mode** returns one `HASH│content` row per line and records every shown row as a lease for this session. A windowed read also includes a `[Lines A-B of N total]` page header. Every row shown is usable by one subsequent `apply_patch`, even when the windows are far apart.
|
|
88
|
+
- **Verbatim mode** returns the same page as plain text with no anchors and serves nothing; an `apply_patch` identity hunk cannot target those lines.
|
|
89
|
+
- A small requested window — up to 61 lines after folding — may widen to its enclosing function, method, class, section or paragraph; a `[widened …]` note names the block that was served. Expansion applies in both modes. Languages: TypeScript/TSX, Python, Go, Rust, Java and Vue through pinned tree-sitter wasm grammars (fetched lazily and verified against a committed SHA-256), plus Bash through the existing `unbash` parser (no wasm). Vue widens a top-level `template`/`script`/`style` block without parsing embedded JS/TS. SQL is not supported: real migrations largely fail to parse and `.tql` is a Java template, not SQL (see [`docs/spec/sql-vue-feasibility-gate.md`](docs/spec/sql-vue-feasibility-gate.md)).
|
|
90
|
+
- `offset`/`limit` and `windows` are page requests, not anchors: only `served` output grants mutation leases.
|
|
92
91
|
|
|
93
|
-
### `
|
|
92
|
+
### `apply_patch`
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
edits: [{
|
|
99
|
-
anchor_from: string, // bare 4-char anchor of the first range line (inclusive)
|
|
100
|
-
anchor_to: string, // bare 4-char anchor of the last range line (inclusive)
|
|
101
|
-
text?: string, // by-value content; "" deletes an in-place range
|
|
102
|
-
text_ref?: { // by-reference content from a served span
|
|
103
|
-
anchor_from: string,
|
|
104
|
-
anchor_to: string,
|
|
105
|
-
file?: string, // another served file (foreign-source copy/cut)
|
|
106
|
-
mode: "copy" | "cut", // required: copy keeps the source, cut retires the span
|
|
107
|
-
},
|
|
108
|
-
at?: "in-place" | "before" | "after", // default "in-place"
|
|
109
|
-
}],
|
|
110
|
-
mode?: "general" | "literal", // "general" refuses served-row echoes; "literal" declares them intended
|
|
111
|
-
})
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Each item carries exactly one of `text` or `text_ref`; supplying both or neither is refused. `before`/`after` insert at the range boundary and require a single-line resolved target. A foreign-source `cut` writes both files as one correlated transaction; `undo_last_edit` reverts both members. An edit never silently rewrites submitted text — a refused edit writes nothing.
|
|
94
|
+
The only file-mutation entry point. One call carries one Codex patch document and commits the whole
|
|
95
|
+
batch as one correlated transaction (pre-mirror, undo rows, deterministic member order, all-or-nothing
|
|
96
|
+
rollback, then per-file store commits).
|
|
115
97
|
|
|
116
|
-
|
|
98
|
+
```text
|
|
99
|
+
*** Begin Patch
|
|
100
|
+
*** Update File: src/config.ts
|
|
101
|
+
@@
|
|
102
|
+
- retries: 3,
|
|
103
|
+
+ retries: 5,
|
|
104
|
+
*** Add File: src/new.ts
|
|
105
|
+
+export const x = 1;
|
|
106
|
+
*** End Patch
|
|
107
|
+
```
|
|
117
108
|
|
|
109
|
+
Addressing works in two modes, and one patch may mix them:
|
|
110
|
+
|
|
111
|
+
- **Fragment hunks** use the official Codex syntax. `@@` starts the search window at the file start;
|
|
112
|
+
`@@ <one context line>` moves the window after that line's unique match. Body lines are
|
|
113
|
+
` ` (context), `-` (remove) and `+` (add). Matching is byte-exact and must be unique: zero or
|
|
114
|
+
multiple matches reject the whole call and serve the candidate rows back.
|
|
115
|
+
- **Identity hunks** use the `@@<a>` / `@@<a>-<b>` extension: two inclusive 4-char anchors from rows
|
|
116
|
+
this session served. The body is `+` lines only; an empty body deletes the span. A stale, unknown
|
|
117
|
+
or foreign anchor is rejected with the file's current rows served.
|
|
118
|
+
- **Reference hunks** `*** Copy From: <path> <a>-<b>` / `*** Cut From: <path> <a>-<b>` take the
|
|
119
|
+
bytes of another served span; a cut also retires the source span in the same transaction.
|
|
120
|
+
- **Lifecycle** `*** Add File:`, `*** Delete File:` and `*** Move to:` join text edits in the same
|
|
121
|
+
transaction and the same undo record. `*** End of File` pins a hunk to the file tail.
|
|
122
|
+
|
|
123
|
+
`@@` followed by a space is Codex's context mode; `@@` immediately followed by four anchor
|
|
124
|
+
characters is identity mode. Any rejection leaves every file byte-identical and reports every
|
|
125
|
+
independent problem with its patch line number and `[E_*`] code. Success returns a file-level summary
|
|
126
|
+
(A/M/R/D) followed by fresh `HASH│content` anchors for every changed line, so the next call can chain
|
|
127
|
+
without a `read`. To replace a whole file, use one `*** Update File:` identity hunk spanning every served line.
|
|
118
128
|
### `undo_last_edit`
|
|
119
129
|
|
|
120
130
|
```ts
|
|
121
131
|
undo_last_edit({ path: string })
|
|
122
132
|
```
|
|
123
133
|
|
|
124
|
-
Restores the previous file content, BOM, line endings and original anchors from a snapshot-pinned undo record. The record survives agent restarts. A file changed externally after the
|
|
134
|
+
Restores the previous file content, BOM, line endings and original anchors from a snapshot-pinned undo record. The record survives agent restarts. A file changed externally after the patch is refused with `[E_UNDO_STALE]`. A correlated cut's undo restores every member of the transaction, not only the named path.
|
|
125
135
|
|
|
126
136
|
## Shell view
|
|
127
137
|
|
|
128
|
-
The plugin installs a `tools/post-execute` listener for the host's `pwsh` and `
|
|
138
|
+
The plugin installs a `tools/post-execute` listener for the host's `pwsh`, `bash` and built-in `grep` tools. When the command is a whitelisted read-only file view, the captured stdout is replaced with the aligned file's `HASH│content` rows, and those rows are served (leased) exactly like a read — a following `apply_patch` can use them with no re-read. A built-in grep result whose matches are proven against disk is served the same way (see the table below).
|
|
129
139
|
|
|
130
140
|
| Command shape | Served as |
|
|
131
141
|
| --- | --- |
|
|
@@ -134,36 +144,24 @@ The plugin installs a `tools/post-execute` listener for the host's `pwsh` and `b
|
|
|
134
144
|
| `Get-Content <file> \| Select-Object -Skip N -First M` (either order) | disk lines N+1 through N+M |
|
|
135
145
|
| `Select-String <pattern> <file>` | the matched lines, with their line numbers |
|
|
136
146
|
| `rg <pattern> <file>` (explicit single file, no switches) | the matched lines, with their line numbers |
|
|
147
|
+
| built-in `grep` (`@deepseek-ai/dsh-tool-fs-search`) | the matched lines grouped per file, each row `HASH│content` |
|
|
137
148
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
## Encoding governance
|
|
141
|
-
|
|
142
|
-
The dsh host reintroduces a VS Code-style encoding model on top of the UTF-8 core. Read admission is deterministic:
|
|
143
|
-
|
|
144
|
-
1. explicit `read({ encoding })` hint;
|
|
145
|
-
2. BOM sniff (UTF-8, UTF-16LE/BE and UTF-32LE/BE BOMs);
|
|
146
|
-
3. strict UTF-8 decode;
|
|
147
|
-
4. the `autoGuessEncoding` gate (off by default);
|
|
148
|
-
5. Top-3 candidate preview on refusal, e.g. `not readable UTF-8 text — top guesses: gbk("你好"); try read({encoding: "<encoding>"}) or enable autoGuessEncoding to decode automatically`.
|
|
149
|
-
|
|
150
|
-
Text encodings offered to the model are `gbk`, `big5`, `shift_jis`, `euc-kr`, `windows-1251` and `utf16le` (case-insensitive), in addition to UTF-8 and BOM-detected Unicode.
|
|
149
|
+
The grep view applies the host's spill policy: at most 250 matches and at most 12,500 inline tokens (the host estimate `ceil(chars / 4) + 4`). A result over either cap, a failed call, a forged or stale match line, or non-ascending coordinates passes through byte-for-byte with no lease.
|
|
151
150
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
- a GBK file edited through `read` → `edit` stays GBK on disk (byte comparison, not text equality);
|
|
155
|
-
- a UTF-16LE file with a BOM keeps its BOM and its byte order;
|
|
156
|
-
- a UTF-8 BOM file round-trips byte-for-byte.
|
|
151
|
+
Alignment is strict: stdout lines are mapped to 1-based disk line numbers with a monotonic, unique match (search output's own line numbers are used as hints). Every served view is prefixed with the shared `[Bash view: <file> (lines a-b of N total)]` page header, so the positions the raw output used to name are not lost. Anything the view cannot prove — an unknown pipeline, a statement separator (`;`), a variable path (`$f`), a wildcard, formatted output, a truncated capture, a failed command — passes through byte-for-byte. The hook never guesses and never serves the wrong occurrence of a duplicated line.
|
|
152
|
+
## Encoding: UTF-8 only
|
|
157
153
|
|
|
158
|
-
|
|
154
|
+
The dsh host reads and writes UTF-8. Read admission is one rule with one exemption:
|
|
159
155
|
|
|
160
|
-
|
|
156
|
+
1. a leading UTF-8 BOM (`EF BB BF`) is stripped, so it never reaches an anchor;
|
|
157
|
+
2. the remaining bytes must be strict UTF-8.
|
|
161
158
|
|
|
162
|
-
|
|
159
|
+
A file that is not valid UTF-8 is refused with `[E_UNSUPPORTED_FILE]` naming the file path and telling the model to ask the user how to handle it — for example a GBK-encoded source file or a BOM-less UTF-16LE file. The host does not guess a code page, offers no reopen-with-encoding parameter, keeps no encoding/BOM memo, and does not re-encode on save. UTF-16/UTF-32 BOMs are still recognized by kind detection: they are named in the refusal (e.g. `UTF-16LE encoded text`) instead of being decoded. Convert a non-UTF-8 file with an external tool before editing it here.
|
|
163
160
|
|
|
161
|
+
`apply_patch` and `undo_last_edit` speak the same contract: they write UTF-8, and every file this plugin serves is UTF-8. The built-in `write` integration keeps the same contract for a profile that deliberately keeps `write` visible.
|
|
164
162
|
## Sandbox behavior
|
|
165
163
|
|
|
166
|
-
`
|
|
164
|
+
`apply_patch` and `undo_last_edit` carry the host's optional escalation fields `sandbox_permissions` and `justification` only when a confining filesystem backend is mounted. `dsh/sandbox.ts` mirrors the built-in `@deepseek-ai/dsh-tool-fs` controller: it validates the field pairing, repeats the standing mode without approval, and resolves a strictly wider mode through the host's approval channel before touching the filesystem. A provider `FS_SANDBOX_DENIED` error is mapped to the standard model-facing denial marker plus escalation hint; other errors pass through unchanged.
|
|
167
165
|
|
|
168
166
|
### Host-side `0xC0000142` workspace-write finding
|
|
169
167
|
|
|
@@ -184,7 +182,7 @@ The M3/M4 smoke runs therefore used `DSH_PERMISSION_MODE=danger-full-access`; th
|
|
|
184
182
|
| `pnpm run lint` | `oxlint .` + `knip --max-issues 0` |
|
|
185
183
|
| `pnpm run verify` | lint + format + typecheck + Windows coverage |
|
|
186
184
|
|
|
187
|
-
The build enforces its own packaging contract: host packages stay external, the
|
|
185
|
+
The build enforces its own packaging contract: host packages stay external, the bundle must not reference pi (there is no pi host integration), and no bare import outside the allowlist can slip in. See [`dsh/README.md`](dsh/README.md#build-and-packaging).
|
|
188
186
|
|
|
189
187
|
## Error and warning codes
|
|
190
188
|
|
|
@@ -195,7 +193,7 @@ Every refusal carries a bracketed code; `src/domain-errors.ts` is the source of
|
|
|
195
193
|
| `[E_ACCESS]` | The filesystem denied access to the target path. |
|
|
196
194
|
| `[E_BAD_PAYLOAD]` | Malformed or unknown tool payload; nothing is written. |
|
|
197
195
|
| `[E_BATCH_ABORT]` | A batch item failed; the whole batch was not applied. |
|
|
198
|
-
| `[E_EMPTY_RANGE]` | The
|
|
196
|
+
| `[E_EMPTY_RANGE]` | The patch would empty a non-empty file; use `write`. |
|
|
199
197
|
| `[E_FOREIGN_ANCHOR]` | The anchor belongs to a different file. |
|
|
200
198
|
| `[E_INTERNAL_INVARIANT]` | An internal invariant was violated. |
|
|
201
199
|
| `[E_LARGE_FILE]` | The file exceeds the served line or byte budget. |
|
|
@@ -203,37 +201,40 @@ Every refusal carries a bracketed code; `src/domain-errors.ts` is the source of
|
|
|
203
201
|
| `[E_MALFORMED_ANCHOR]` | The anchor token cannot be parsed. |
|
|
204
202
|
| `[E_NOOP_LOOP]` | Repeated identical no-op edits; refused with the current range served. |
|
|
205
203
|
| `[E_NOT_FOUND]` | The target file does not exist or is unreachable. |
|
|
204
|
+
| `[E_PATCH_SYNTAX]` | The apply_patch text is not valid syntax; nothing is written. |
|
|
206
205
|
| `[E_STALE_ANCHOR]` | A boundary anchor no longer resolves against the file. |
|
|
207
|
-
| `[E_STALE_RANGE]` | The served range changed between serve and
|
|
206
|
+
| `[E_STALE_RANGE]` | The served range changed between serve and patch. |
|
|
208
207
|
| `[E_SUSPICIOUS_TEXT]` | The replacement reproduces served anchor text (a copied `HASH│` echo). |
|
|
209
208
|
| `[E_TARGET_LOST]` | The target line identity can no longer be resolved. |
|
|
210
209
|
| `[E_UNDO_REVERT_FAILED]` | A correlated revert failed mid-way; a repair run finishes it. |
|
|
211
210
|
| `[E_UNDO_STALE]` | The undo record is stale relative to the file. |
|
|
212
211
|
| `[E_UNDO_UNAVAILABLE]` | There is no undo history for the path. |
|
|
212
|
+
| `[E_UNIQUE_MATCH]` | A fragment hunk matched zero or multiple places; candidates are served back. |
|
|
213
213
|
| `[E_UNKNOWN]` | An unclassified failure; the message carries the cause. |
|
|
214
214
|
| `[E_UNKNOWN_ANCHOR]` | The anchor was never served in this session. |
|
|
215
|
-
| `[E_UNSUPPORTED_FILE]` | Binary, image or directory targets are not editable text. |
|
|
215
|
+
| `[E_UNSUPPORTED_FILE]` | Binary, image or directory targets are not editable text; a non-UTF-8 file is refused with its path and an instruction to ask the user how to handle it. |
|
|
216
216
|
| `[E_UNVERIFIED_RANGE]` | The range interior cannot be verified (never served). |
|
|
217
|
-
| `[W_LITERAL_BYPASS]` | Literal `HASH│` bytes were written through `
|
|
218
|
-
| `[W_NEVER_SERVED_SHAPE]` | A never-served hint was attached to an accepted
|
|
219
|
-
| `[W_NOOP]` | The
|
|
220
|
-
| `[W_NOOP_INSERT]` | An insert-shaped
|
|
217
|
+
| `[W_LITERAL_BYPASS]` | Literal `HASH│` bytes were written through `apply_patch` and accepted with a warning. |
|
|
218
|
+
| `[W_NEVER_SERVED_SHAPE]` | A never-served hint was attached to an accepted patch. |
|
|
219
|
+
| `[W_NOOP]` | The patch produced no content change and was accepted as a no-op. |
|
|
220
|
+
| `[W_NOOP_INSERT]` | An insert-shaped hunk produced no change. |
|
|
221
221
|
| `[W_REVERSED_ANCHORS]` | Boundaries were reversed and healed. |
|
|
222
222
|
| `[W_SERVED_PREFIX_MISMATCH]` | A served prefix does not match; fresh rows were served. |
|
|
223
223
|
| `[W_UNICODE_LITERAL]` | A Unicode literal in the payload looks like a copied anchor. |
|
|
224
224
|
|
|
225
|
+
Range-family rejections render their current-row evidence exactly once: the headline plus one `Current range (fresh read):` block. A single-line anchor pair (`anchor_from` equals `anchor_to`) is listed once, and the distinct-anchor lists de-duplicate. `[E_UNKNOWN_ANCHOR]` and `[E_FOREIGN_ANCHOR]` also name each distinct anchor once.
|
|
226
|
+
|
|
225
227
|
## Current limitations
|
|
226
228
|
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
-
- **Undo preserves a legacy encoding only with recorded state.** The encoding memo lives in the plugin process and is version-gated; a fresh process or a file never governed-read in that process has no state to invert, so undo keeps the core's UTF-8 behavior.
|
|
229
|
+
- **Mutation stays on the local `node:fs` seam.** The read path consumes a `CoreReadIo` port (local or mounted `ctx.fs`), but `writeAtomic`, the `apply_patch` engine and undo remain local-only. `apply_patch` and `undo_last_edit` fail closed with `E_NOT_FOUND` when the target is reachable only through `ctx.fs`. A full mutation bridge needs target-keyed store state: the host's `ctx.fs` already exposes versioned writes in `0.2.2-alpha.3`, but the snapshot/lease store still keys on absolute paths and local stat identity (M6, see [`dsh/README.md`](dsh/README.md#ctxfs-deferral-m6)).
|
|
230
|
+
- **UTF-8 only.** Non-UTF-8 files are refused on read and must be converted with an external tool; the plugin does not decode legacy code pages, does not re-encode on save, and keeps no encoding state.
|
|
230
231
|
- **Windows-only.** The branch targets Windows; POSIX-only suites are excluded in `vitest.config.windows.ts`, and that exclusion list must shrink, never grow (see [`dsh/UPSTREAM.md`](dsh/UPSTREAM.md)).
|
|
231
232
|
|
|
232
233
|
## Repository layout
|
|
233
234
|
|
|
234
235
|
| Path | Role |
|
|
235
236
|
| --- | --- |
|
|
236
|
-
| `src/` |
|
|
237
|
+
| `src/` | hashline engine core derived from pi-better-edit (semantic source; upstream fixes are picked selectively, see [`dsh/UPSTREAM.md`](dsh/UPSTREAM.md)) |
|
|
237
238
|
| `dsh/` | DeepSeek Harness host layer — the only code that differs from upstream; map in [`dsh/README.md`](dsh/README.md) |
|
|
238
239
|
| `dsh/UPSTREAM.md` | upstream sync cursor, policy and Windows adaptation patches |
|
|
239
240
|
| `dsh/baseline/` | milestone measurement baselines (M0–M5) |
|
|
@@ -242,7 +243,7 @@ Every refusal carries a bracketed code; `src/domain-errors.ts` is the source of
|
|
|
242
243
|
|
|
243
244
|
## Upstream attribution
|
|
244
245
|
|
|
245
|
-
The hashline engine is [**pi-better-edit**](https://github.com/Rianico/pi-better-edit) by [Rianico](https://github.com/Rianico), used under the MIT license and synced at upstream `673964c` (`v2.16.0`; see [`dsh/UPSTREAM.md`](dsh/UPSTREAM.md)). This repository
|
|
246
|
+
The hashline engine is [**pi-better-edit**](https://github.com/Rianico/pi-better-edit) by [Rianico](https://github.com/Rianico), used under the MIT license and synced at upstream `673964c` (`v2.16.0`; see [`dsh/UPSTREAM.md`](dsh/UPSTREAM.md)). This repository is a self-maintained fork: upstream is the semantic source of the hashline core and a place to pick bug-fix commits from, recorded in `dsh/UPSTREAM.md`; there is no commit-for-commit sync promise. The `dsh/` adapter is ours, is never merged upstream, and contains only host wiring.
|
|
246
247
|
|
|
247
248
|
## License
|
|
248
249
|
|
package/cordis.patch.yml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# dsh-hashline-edit bundle patch: mount the host-plane plugin row.
|
|
2
2
|
# Installed via `dsh plugin --profile <name> add dsh-hashline-edit` (or a
|
|
3
|
-
# profile's bundle list). The plugin hooks agent/created
|
|
4
|
-
#
|
|
5
|
-
# editing or user config is needed.
|
|
3
|
+
# profile's bundle list). The plugin hooks agent/created, shadows the built-in
|
|
4
|
+
# read tool, and hides the native write/edit/str_replace_editor surface on each
|
|
5
|
+
# agent's own scope layer, so no preset editing or user config is needed.
|
|
6
6
|
- insert:
|
|
7
7
|
- id: dsh-hashline-edit
|
|
8
8
|
name: dsh-hashline-edit
|