dsh-patch-edit-plus 0.1.0
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 +16 -0
- package/CHANGELOG.zh.md +16 -0
- package/LICENSE +21 -0
- package/README.ja.md +124 -0
- package/README.ko.md +124 -0
- package/README.md +124 -0
- package/README.zh.md +123 -0
- package/assets/.gitkeep +0 -0
- package/cordis.patch.yml +6 -0
- package/dsh.plugin.json +21 -0
- package/lib/apply/engine.js +160 -0
- package/lib/apply/plan.js +79 -0
- package/lib/apply/seek.js +47 -0
- package/lib/config.js +45 -0
- package/lib/detect.js +29 -0
- package/lib/errors.js +125 -0
- package/lib/fsops.js +51 -0
- package/lib/index.js +39 -0
- package/lib/parse/codex.js +189 -0
- package/lib/parse/types.js +9 -0
- package/lib/parse/unified.js +243 -0
- package/lib/register.js +60 -0
- package/lib/shellops.js +111 -0
- package/lib/tool.js +208 -0
- package/lib/types/apply/engine.d.ts +39 -0
- package/lib/types/apply/plan.d.ts +31 -0
- package/lib/types/apply/seek.d.ts +21 -0
- package/lib/types/config.d.ts +66 -0
- package/lib/types/detect.d.ts +15 -0
- package/lib/types/errors.d.ts +79 -0
- package/lib/types/fsops.d.ts +28 -0
- package/lib/types/index.d.ts +23 -0
- package/lib/types/parse/codex.d.ts +28 -0
- package/lib/types/parse/types.d.ts +58 -0
- package/lib/types/parse/unified.d.ts +17 -0
- package/lib/types/register.d.ts +23 -0
- package/lib/types/shellops.d.ts +41 -0
- package/lib/types/tool.d.ts +46 -0
- package/package.json +103 -0
- package/screenshots.json +3 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Single `apply_patch` model-facing tool accepting git/unified diff (default) and Codex `apply_patch` syntax (opt-in via `allowCodexPatch`).
|
|
8
|
+
- Format auto-detection with an actionable hint when a recognized-but-disabled style arrives.
|
|
9
|
+
- Add / Update (multi-hunk) / Delete / Move operations; Delete and Move run through the sandbox-aware `ctx.shell` with paths passed only via environment variables.
|
|
10
|
+
- All-or-nothing two-phase engine: full read-only verification (context location with three tolerance levels, workspace containment, symlink rejection, duplicate-path detection) before any write.
|
|
11
|
+
- Official write-intent dance on every write: `fs/write-intent` waterfall → guarded `writeText` → `fs/observed` emit, so the read-before-write gate applies to patch writes.
|
|
12
|
+
- Four-element hunk-mismatch diagnostics (file + hunk index, search origin, expected preview with visible whitespace, actual excerpt) plus a targeted hint.
|
|
13
|
+
- `dryRun` parameter and `dryRunByDefault` config.
|
|
14
|
+
- Codex-semantics grouped output (added → modified → deleted) with per-file diffs capped by `maxDiffBytes` and replay-safe `presentationMeta`.
|
|
15
|
+
- Three-layer tool-name conflict protection (`rename` default / `skip` / `fail`) so name clashes with other `apply_patch` providers never break DSH startup.
|
|
16
|
+
- DSH `0.1.2-rc.1` ~ `0.1.5-rc.2` compatibility: single tool-authoring code path plus `typecheck:0.1.5` static verification against the 0.1.5-rc.2 peer packages; settings dual-API fallback (`installSection` / `register`).
|
package/CHANGELOG.zh.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
### 新增
|
|
6
|
+
|
|
7
|
+
- 唯一的模型可见工具 `apply_patch`:接受 git/unified diff(默认)与 Codex `apply_patch` 语法(经 `allowCodexPatch` 开启)。
|
|
8
|
+
- 格式自动识别;识别到「已识别但被关闭」的风格时返回可操作提示,而非泛化解析错误。
|
|
9
|
+
- Add / Update(多 hunk)/ Delete / Move 四类操作;Delete 与 Move 走沙箱感知的 `ctx.shell`,路径只经环境变量传递。
|
|
10
|
+
- 两阶段原子应用引擎:写入前完成全量只读验证(三级上下文定位、工作区围栏、symlink 拒绝、路径去重),任一失败零写入。
|
|
11
|
+
- 每次写入完整复刻官方 intent 舞蹈:`fs/write-intent` waterfall → 带 intent 的 `writeText` → `fs/observed` 事件,使 read-before-write 门禁对补丁写入生效。
|
|
12
|
+
- hunk 定位失败的四要素诊断(文件 + hunk 序号、搜索起始行号、空白可视化的期望行预览、文件实际片段)与针对性提示。
|
|
13
|
+
- `dryRun` 参数与 `dryRunByDefault` 配置。
|
|
14
|
+
- Codex 语义分组输出(added → modified → deleted),单文件 diff 受 `maxDiffBytes` 封顶,`presentationMeta` 满足回放纯函数约束。
|
|
15
|
+
- 三层工具名冲突防护(默认 `rename` / `skip` / `fail`),与其他 `apply_patch` 提供方的重名不会导致 DSH 启动失败。
|
|
16
|
+
- DSH `0.1.2-rc.1` ~ `0.1.5-rc.2` 兼容:单一代码路径 + `typecheck:0.1.5` 对 0.1.5-rc.2 peer 包的静态核验;settings 双 API 回退(`installSection` / `register`)。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-patch-edit-plus contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# dsh-patch-edit-plus
|
|
2
|
+
|
|
3
|
+
[DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 向けのパッチ形式ファイル編集。モデルに公開される単一の `apply_patch` ツールが **git/unified diff**(既定)と **Codex `apply_patch` 構文**(オプトイン)を受け付け、すべての変更を **オール・オア・ナッシング**で適用し、DSH の動作には一切干渉しません。
|
|
4
|
+
|
|
5
|
+
## DSH バージョン互換性
|
|
6
|
+
|
|
7
|
+
| DSH バージョン | ステータス | 備考 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `0.1.2-rc.1` | ✅ サポート対象 | ツールオーサリング契約(`defineTool` DSL、presentation intents)は 0.1.2〜0.1.5 でバイト単位で同一であり、1 つのコードパスが両方を担います。 |
|
|
10
|
+
| `0.1.3-alpha.x` | ✅ サポート対象 | 上記と同じ。 |
|
|
11
|
+
| `0.1.5-rc.2` | ✅ 静的検証済み | `npm run typecheck:0.1.5` は実際の `0.1.5-rc.2` ピアパッケージに対して `src/` を型チェックします。`readByteRange` の使用はなく、0.1.5 専用の PTC 識別子もありません。 |
|
|
12
|
+
|
|
13
|
+
設定の登録はデュアル API フォールバック(0.1.2 以降では `settings.installSection`、旧ホストでは `settings.register`)を使用します。
|
|
14
|
+
|
|
15
|
+
## そもそもなぜパッチなのか?(ツールルーティングガイド)
|
|
16
|
+
|
|
17
|
+
ツールの説明はモデルを明示的にルーティングします。
|
|
18
|
+
|
|
19
|
+
- **すでに読み込んだ 1 ファイル内の小さな単一変更** → ネイティブの `edit` ツールを優先(パッチはリテラル編集より多くのトークンを消費します)。
|
|
20
|
+
- **大きなファイル内の小さな変更** → `apply_patch`(ファイル全体をエコーする必要はありません)。
|
|
21
|
+
- **多数のファイルにまたがるバッチ変更** → `apply_patch`(1 回の呼び出しで、アトミックに検証されます)。
|
|
22
|
+
- **実質的な内容を持つ新規ファイル / 削除 / リネーム** → `apply_patch`。
|
|
23
|
+
|
|
24
|
+
## インストール
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dsh plugin --profile web add <path-to-dsh-patch-edit-plus>
|
|
28
|
+
dsh web --dump-config # verify the plugin row appears
|
|
29
|
+
# restart DSH
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 使い方
|
|
33
|
+
|
|
34
|
+
### Unified diff(既定、有効)
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
--- a/src/app.ts
|
|
38
|
+
+++ b/src/app.ts
|
|
39
|
+
@@ -10,4 +10,4 @@ export function main() {
|
|
40
|
+
init()
|
|
41
|
+
- start(oldPort)
|
|
42
|
+
+ start(newPort)
|
|
43
|
+
await shutdown()
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Codex apply_patch 構文(オプトイン)
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
*** Begin Patch
|
|
51
|
+
*** Update File: src/app.ts
|
|
52
|
+
@@
|
|
53
|
+
init()
|
|
54
|
+
- start(oldPort)
|
|
55
|
+
+ start(newPort)
|
|
56
|
+
*** Add File: docs/notes.md
|
|
57
|
+
+# Notes
|
|
58
|
+
*** Delete File: tmp/junk.txt
|
|
59
|
+
*** Move to: src/app.ts (via `*** Move to:` after *** Update File:)
|
|
60
|
+
*** End Patch
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`allowCodexPatch: true` で有効化します。無効時に Codex パッチが届くと、ツールは汎用のパースエラーではなく **実行可能なヒント**(有効化の方法、または unified diff での再試行方法)を返します。Codex 構文は GPT 系モデルにとって強い事前分布であり、無言の失敗はモデルを再試行ループに閉じ込めてしまいます。
|
|
64
|
+
|
|
65
|
+
### 操作
|
|
66
|
+
|
|
67
|
+
| 操作 | Unified diff | Codex | 実行パス |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| 追加 | `new file mode` + `/dev/null` | `*** Add File:` | `ctx.fs.writeText`(公式の intent dance) |
|
|
70
|
+
| 更新(マルチハンク) | `@@` ハンク | `@@` ハンク | `ctx.fs.writeText` |
|
|
71
|
+
| 削除 | `deleted file mode` + `/dev/null` | `*** Delete File:` | `ctx.shell`(サンドボックス対応、パスは env 経由) |
|
|
72
|
+
| 移動 / リネーム | `rename from/to` | `*** Move to:` | `ctx.shell` |
|
|
73
|
+
|
|
74
|
+
## 保証
|
|
75
|
+
|
|
76
|
+
- **オール・オア・ナッシング**: パッチ全体がまず現在のファイル内容に対して検証されます(コンテキスト位置、ワークスペース内包、シンボリックリンクポリシー、重複パス)。いずれかが失敗すると、1 バイトも書き込まれずに中止されます。
|
|
77
|
+
- **read-before-write ゲートの遵守**: すべての書き込みは公式 `write` ツールの intent dance を再現します — `fs/write-intent` ウォーターフォール → ガード付き `writeText` → `fs/observed` 発火。素の `writeText` は、DSH プロバイダが `fs/*` イベントを自らディスパッチしないため、ゲートを黙って迂回してしまいます。
|
|
78
|
+
- **正確な失敗診断**: ハンク不一致時、エラーにはファイル + ハンクインデックス、検索開始行、期待行のプレビュー(空白を可視化)、ファイルの実際の抜粋、および的を絞ったヒントが含まれます。
|
|
79
|
+
- **改行の忠実性**: CRLF ファイルは CRLF として書き戻されます。末尾改行なしの末尾は、パッチが末尾を変更しない限り保持されます。
|
|
80
|
+
- **純粋な追加**: プラグインは正確に 1 つのツールを登録し、`tools.restrict()` を決して呼び出さず、ネイティブツールを決して上書きせず、グローバルサービスも登録せず、アンロード時にすべてを削除します。他の `apply_patch` 提供元(`bainianlaoyao/dsh-codex-mode`、`shuind/dsh-codex-harness`、…)との名前競合はリネーム回避(`apply_patch_1`、`…_2`、…)で解決されるため、DSH の起動が失敗することはありません。
|
|
81
|
+
|
|
82
|
+
## 設定
|
|
83
|
+
|
|
84
|
+
| オプション | 既定値 | 説明 |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `toolName` | `apply_patch` | モデルに公開されるツール名。 |
|
|
87
|
+
| `conflictPolicy` | `rename` | ツール名が使用済みの場合の `rename` / `skip` / `fail`。 |
|
|
88
|
+
| `renameSuffix` | `_1` | リネーム回避で使用される接尾辞。 |
|
|
89
|
+
| `allowUnifiedDiff` | `true` | git/unified diff を受け付けます。 |
|
|
90
|
+
| `allowCodexPatch` | `false` | Codex `apply_patch` 構文を受け付けます。 |
|
|
91
|
+
| `deleteBackend` | `shell` | `shell` または `none`(削除/移動は構造化エラーを返します)。 |
|
|
92
|
+
| `shellDialect` | `auto` | `auto`(win32 では pwsh)/ `posix` / `pwsh`。 |
|
|
93
|
+
| `deleteCommand` / `moveCommand` | 組み込み | カスタムコマンドテンプレート。パスは依然として **env 経由のみ**(`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`)で渡され、コマンド文字列に補間されることはありません。 |
|
|
94
|
+
| `dryRunByDefault` | `false` | `dryRun: false` でない限り、呼び出しをドライランとして扱います。 |
|
|
95
|
+
| `followSymlinks` | `false` | パッチのパスがシンボリックリンクをたどることを許可します。 |
|
|
96
|
+
| `maxFiles` | `50` | パッチあたりのファイルセクションの最大数。 |
|
|
97
|
+
| `maxPatchBytes` | `524288` | パッチテキストの最大サイズ(バイト)。 |
|
|
98
|
+
| `maxDiffBytes` | `16384` | 結果メタデータに保持されるファイルあたりの最大 diff(それより大きい diff は空にされ、`truncated` とマークされます)。 |
|
|
99
|
+
|
|
100
|
+
## 制限事項(開示)
|
|
101
|
+
|
|
102
|
+
- **削除/移動は `ctx.shell` を通じて実行されます。** サンドボックスの強度は、ロードされたシェルエグゼキュータが強制する内容(`bash-sandbox` は境界を設け、`bash-local` は設けません)と同じであり、ネイティブ bash ツールと同じリスク態勢です。すべてのリクエストは `sandboxPolicy` を伴い、サンドボックスの事実を報告するため、「ポリシーによる拒否」と「コマンド失敗」を区別できます。
|
|
103
|
+
- **追加は親ディレクトリを作成しません。** これはネイティブ `write` ツールと同じです(`ctx.fs` に mkdir はありません)。エラーには不足しているディレクトリ名が示されます。
|
|
104
|
+
- ファジー/オフセットマッチングはありません。ハンクの位置は正確一致 → `trimEnd` → `trim` で、これは意図的なものです(ファジーマッチングは破壊的操作に対して安全ではありません。ロードマップに含まれています)。
|
|
105
|
+
- バイナリパッチは明確なエラーで拒否されます。
|
|
106
|
+
|
|
107
|
+
## 開発
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm install
|
|
111
|
+
npm run typecheck # against 0.1.2-rc.1 peers (devDependencies)
|
|
112
|
+
npm run typecheck:0.1.5 # against 0.1.5-rc.2 peers (static dual-version proof)
|
|
113
|
+
npm test # vitest, 84 tests
|
|
114
|
+
npm run lint
|
|
115
|
+
npm run build # lib/
|
|
116
|
+
npm run verify:source # static safety assertions (intent dance, no node:fs, …)
|
|
117
|
+
npm run smoke # load-level smoke against a stub host
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
ライブ DSH 上での手動検証(`dsh plugin --profile web add` → 再起動 → セッションでツールを呼び出す)は、意図的に運用者に委ねられています。
|
|
121
|
+
|
|
122
|
+
## ライセンス
|
|
123
|
+
|
|
124
|
+
MIT
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# dsh-patch-edit-plus
|
|
2
|
+
|
|
3
|
+
[DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness)를 위한 패치 스타일 파일 편집 도구입니다. 모델에 노출되는 단일 `apply_patch` 도구가 **git/unified diff**(기본)와 **Codex `apply_patch` 문법**(옵트인)을 받아들이고, 모든 변경을 **전부 아니면 전무(all-or-nothing)** 방식으로 적용하며, DSH의 동작에는 전혀 간섭하지 않습니다.
|
|
4
|
+
|
|
5
|
+
## DSH 버전 호환성
|
|
6
|
+
|
|
7
|
+
| DSH 버전 | 상태 | 비고 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `0.1.2-rc.1` | ✅ 지원됨 | 도구 작성 계약(`defineTool` DSL, 프레젠테이션 인텐트)은 0.1.2–0.1.5 전 구간에서 바이트 단위로 동일하므로 하나의 코드 경로가 양쪽을 모두 지원합니다. |
|
|
10
|
+
| `0.1.3-alpha.x` | ✅ 지원됨 | 위와 동일합니다. |
|
|
11
|
+
| `0.1.5-rc.2` | ✅ 정적 검증 완료 | `npm run typecheck:0.1.5`는 실제 `0.1.5-rc.2` peer 패키지를 대상으로 `src/`의 타입을 검사합니다. `readByteRange`를 사용하지 않으며, 0.1.5 전용 PTC 식별자도 없습니다. |
|
|
12
|
+
|
|
13
|
+
설정 등록은 이중 API 폴백을 사용합니다(0.1.2+ 에서는 `settings.installSection`, 이전 호스트에서는 `settings.register`).
|
|
14
|
+
|
|
15
|
+
## 왜 굳이 패치인가? (도구 라우팅 가이드)
|
|
16
|
+
|
|
17
|
+
도구 설명은 모델의 선택을 다음과 같이 명시적으로 유도합니다.
|
|
18
|
+
|
|
19
|
+
- **이미 읽은 파일 하나에 작은 변경 하나** → 네이티브 `edit` 도구를 우선 사용하십시오(패치는 리터럴 편집보다 토큰을 더 소모합니다).
|
|
20
|
+
- **큰 파일 내부의 작은 변경** → `apply_patch`(파일 전체를 되풀이해 출력할 필요가 없습니다).
|
|
21
|
+
- **여러 파일에 걸친 일괄 변경** → `apply_patch`(한 번의 호출로 원자적으로 검증됩니다).
|
|
22
|
+
- **내용이 상당한 새 파일 / 삭제 / 이름 변경** → `apply_patch`.
|
|
23
|
+
|
|
24
|
+
## 설치
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dsh plugin --profile web add <path-to-dsh-patch-edit-plus>
|
|
28
|
+
dsh web --dump-config # verify the plugin row appears
|
|
29
|
+
# restart DSH
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 사용법
|
|
33
|
+
|
|
34
|
+
### Unified diff (기본, 활성화됨)
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
--- a/src/app.ts
|
|
38
|
+
+++ b/src/app.ts
|
|
39
|
+
@@ -10,4 +10,4 @@ export function main() {
|
|
40
|
+
init()
|
|
41
|
+
- start(oldPort)
|
|
42
|
+
+ start(newPort)
|
|
43
|
+
await shutdown()
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Codex apply_patch 문법 (옵트인)
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
*** Begin Patch
|
|
51
|
+
*** Update File: src/app.ts
|
|
52
|
+
@@
|
|
53
|
+
init()
|
|
54
|
+
- start(oldPort)
|
|
55
|
+
+ start(newPort)
|
|
56
|
+
*** Add File: docs/notes.md
|
|
57
|
+
+# Notes
|
|
58
|
+
*** Delete File: tmp/junk.txt
|
|
59
|
+
*** Move to: src/app.ts (via `*** Move to:` after *** Update File:)
|
|
60
|
+
*** End Patch
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`allowCodexPatch: true`로 활성화합니다. 비활성 상태에서 Codex 패치가 들어오면 도구는 일반적인 파싱 오류 대신 **실행 가능한 힌트**(활성화 방법, 또는 unified diff로 재시도하는 방법)를 반환합니다. Codex 문법은 GPT 계열 모델에게 강한 사전 확률(prior)이며, 조용한 실패는 모델을 재시도 루프에 가두기 때문입니다.
|
|
64
|
+
|
|
65
|
+
### 연산
|
|
66
|
+
|
|
67
|
+
| 연산 | Unified diff | Codex | 실행 경로 |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| 추가 | `new file mode` + `/dev/null` | `*** Add File:` | `ctx.fs.writeText` (공식 인텐트 댄스) |
|
|
70
|
+
| 업데이트 (다중 hunk) | `@@` hunks | `@@` hunks | `ctx.fs.writeText` |
|
|
71
|
+
| 삭제 | `deleted file mode` + `/dev/null` | `*** Delete File:` | `ctx.shell` (샌드박스 인식, 경로는 환경 변수로 전달) |
|
|
72
|
+
| 이동 / 이름 변경 | `rename from/to` | `*** Move to:` | `ctx.shell` |
|
|
73
|
+
|
|
74
|
+
## 보장 사항
|
|
75
|
+
|
|
76
|
+
- **전부 아니면 전무(all-or-nothing)**: 전체 패치를 먼저 현재 파일 내용과 대조해 검증합니다(컨텍스트 위치, 워크스페이스 경계, 심볼릭 링크 정책, 중복 경로). 어느 하나라도 실패하면 단 1바이트도 기록하지 않고 중단합니다.
|
|
77
|
+
- **쓰기 전 읽기 게이트 준수**: 모든 쓰기는 공식 `write` 도구의 인텐트 댄스를 그대로 재현합니다 — `fs/write-intent` 워터폴 → 가드된 `writeText` → `fs/observed` 발생. DSH 프로바이더는 `fs/*` 이벤트를 스스로 발행하지 않으므로, 순수한 `writeText`는 게이트를 조용히 우회하게 됩니다.
|
|
78
|
+
- **정밀한 실패 진단**: hunk 불일치 시 오류에는 파일 + hunk 인덱스, 탐색 시작 줄, 예상 줄 미리보기(공백이 눈에 보이도록 처리), 파일의 실제 발췌, 그리고 표적화된 힌트가 담깁니다.
|
|
79
|
+
- **줄바꿈 충실도**: CRLF 파일은 CRLF로 다시 기록되며, 패치가 마지막 부분을 변경하지 않는 한 끝에 줄바꿈이 없는 파일의 꼬리도 그대로 보존됩니다.
|
|
80
|
+
- **순수 추가형**: 이 플러그인은 정확히 하나의 도구만 등록하고, `tools.restrict()`를 호출하지 않으며, 네이티브 도구를 재정의하지 않고, 전역 서비스를 등록하지 않으며, 언로드 시 모든 것을 제거합니다. 다른 `apply_patch` 제공자(`bainianlaoyao/dsh-codex-mode`, `shuind/dsh-codex-harness`, …)와의 이름 충돌은 이름 회피(`apply_patch_1`, `…_2`, …)로 해결되므로 DSH 시작이 실패하는 일이 없습니다.
|
|
81
|
+
|
|
82
|
+
## 설정
|
|
83
|
+
|
|
84
|
+
| 옵션 | 기본값 | 설명 |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `toolName` | `apply_patch` | 모델에 노출되는 도구 이름입니다. |
|
|
87
|
+
| `conflictPolicy` | `rename` | 도구 이름이 이미 사용 중일 때의 동작으로, `rename` / `skip` / `fail` 중 하나입니다. |
|
|
88
|
+
| `renameSuffix` | `_1` | 이름 회피에 사용되는 접미사입니다. |
|
|
89
|
+
| `allowUnifiedDiff` | `true` | git/unified diff를 허용합니다. |
|
|
90
|
+
| `allowCodexPatch` | `false` | Codex `apply_patch` 문법을 허용합니다. |
|
|
91
|
+
| `deleteBackend` | `shell` | `shell` 또는 `none`(`none`이면 삭제/이동이 구조화된 오류를 반환합니다). |
|
|
92
|
+
| `shellDialect` | `auto` | `auto`(win32에서는 pwsh) / `posix` / `pwsh` 중 하나입니다. |
|
|
93
|
+
| `deleteCommand` / `moveCommand` | 내장 | 사용자 지정 명령 템플릿입니다. 경로는 여전히 **환경 변수로만**(`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`) 전달되며, 명령 문자열에 끼워 넣어서는 안 됩니다. |
|
|
94
|
+
| `dryRunByDefault` | `false` | `dryRun: false`가 아닌 한 모든 호출을 드라이 런으로 취급합니다. |
|
|
95
|
+
| `followSymlinks` | `false` | 패치 경로가 심볼릭 링크를 통과하도록 허용합니다. |
|
|
96
|
+
| `maxFiles` | `50` | 패치당 최대 파일 섹션 수입니다. |
|
|
97
|
+
| `maxPatchBytes` | `524288` | 패치 텍스트의 최대 크기(바이트)입니다. |
|
|
98
|
+
| `maxDiffBytes` | `16384` | 결과 메타데이터에 보관되는 파일별 최대 diff 크기입니다(이보다 큰 diff는 비워지고 `truncated`로 표시됩니다). |
|
|
99
|
+
|
|
100
|
+
## 한계 (공개)
|
|
101
|
+
|
|
102
|
+
- **삭제/이동은 `ctx.shell`을 통해 실행됩니다.** 샌드박스 강도는 로드된 셸 실행기가 실제로 강제하는 수준을 따릅니다(`bash-sandbox`는 격리하고, `bash-local`은 격리하지 않습니다) — 네이티브 bash 도구와 동일한 위험 구도입니다. 모든 요청은 `sandboxPolicy`를 함께 전달하고 샌드박스 관련 사실을 보고하므로, "정책 거부"와 "명령 실패"를 구별할 수 있습니다.
|
|
103
|
+
- **추가(Add)는 상위 디렉터리를 생성하지 않습니다.** 이는 네이티브 `write` 도구와 동일한 동작이며(`ctx.fs`에는 mkdir이 없습니다), 오류 메시지에 누락된 디렉터리 이름이 표시됩니다.
|
|
104
|
+
- 퍼지/오프셋 매칭은 지원하지 않습니다. hunk 위치 탐색은 정확 일치 → `trimEnd` → `trim` 순으로 의도적으로 수행합니다(퍼지 매칭은 파괴적 연산에 안전하지 않으며, 로드맵에 올라 있습니다).
|
|
105
|
+
- 바이너리 패치는 명확한 오류와 함께 거부됩니다.
|
|
106
|
+
|
|
107
|
+
## 개발
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm install
|
|
111
|
+
npm run typecheck # against 0.1.2-rc.1 peers (devDependencies)
|
|
112
|
+
npm run typecheck:0.1.5 # against 0.1.5-rc.2 peers (static dual-version proof)
|
|
113
|
+
npm test # vitest, 84 tests
|
|
114
|
+
npm run lint
|
|
115
|
+
npm run build # lib/
|
|
116
|
+
npm run verify:source # static safety assertions (intent dance, no node:fs, …)
|
|
117
|
+
npm run smoke # load-level smoke against a stub host
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
실제 DSH에서의 수동 검증(`dsh plugin --profile web add` → 재시작 → 세션에서 도구 호출)은 의도적으로 운영자에게 맡깁니다.
|
|
121
|
+
|
|
122
|
+
## 라이선스
|
|
123
|
+
|
|
124
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# dsh-patch-edit-plus
|
|
2
|
+
|
|
3
|
+
Patch-style file editing for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness): one model-facing `apply_patch` tool that accepts **git/unified diff** (default) and **Codex `apply_patch` syntax** (opt-in), applies every change **all-or-nothing**, and stays completely out of DSH's way.
|
|
4
|
+
|
|
5
|
+
## DSH version compatibility
|
|
6
|
+
|
|
7
|
+
| DSH version | Status | Notes |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `0.1.2-rc.1` | ✅ supported | Tool authoring contract (`defineTool` DSL, presentation intents) is byte-identical across 0.1.2–0.1.5; one code path serves both. |
|
|
10
|
+
| `0.1.3-alpha.x` | ✅ supported | Same as above. |
|
|
11
|
+
| `0.1.5-rc.2` | ✅ statically verified | `npm run typecheck:0.1.5` typechecks `src/` against the real `0.1.5-rc.2` peer packages. No `readByteRange` usage, no 0.1.5-only PTC identifiers. |
|
|
12
|
+
|
|
13
|
+
Settings registration uses the dual-API fallback (`settings.installSection` on 0.1.2+, `settings.register` on older hosts).
|
|
14
|
+
|
|
15
|
+
## Why patch at all? (tool routing guide)
|
|
16
|
+
|
|
17
|
+
The tool description explicitly routes the model:
|
|
18
|
+
|
|
19
|
+
- **Single small change in one already-read file** → prefer the native `edit` tool (a patch costs more tokens than a literal edit).
|
|
20
|
+
- **Small change inside a LARGE file** → `apply_patch` (no need to echo the whole file).
|
|
21
|
+
- **Batched changes across MANY files** → `apply_patch` (one call, verified atomically).
|
|
22
|
+
- **New file with substantial content / delete / rename** → `apply_patch`.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dsh plugin --profile web add <path-to-dsh-patch-edit-plus>
|
|
28
|
+
dsh web --dump-config # verify the plugin row appears
|
|
29
|
+
# restart DSH
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Unified diff (default, enabled)
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
--- a/src/app.ts
|
|
38
|
+
+++ b/src/app.ts
|
|
39
|
+
@@ -10,4 +10,4 @@ export function main() {
|
|
40
|
+
init()
|
|
41
|
+
- start(oldPort)
|
|
42
|
+
+ start(newPort)
|
|
43
|
+
await shutdown()
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Codex apply_patch syntax (opt-in)
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
*** Begin Patch
|
|
51
|
+
*** Update File: src/app.ts
|
|
52
|
+
@@
|
|
53
|
+
init()
|
|
54
|
+
- start(oldPort)
|
|
55
|
+
+ start(newPort)
|
|
56
|
+
*** Add File: docs/notes.md
|
|
57
|
+
+# Notes
|
|
58
|
+
*** Delete File: tmp/junk.txt
|
|
59
|
+
*** Move to: src/app.ts (via `*** Move to:` after *** Update File:)
|
|
60
|
+
*** End Patch
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Enable with `allowCodexPatch: true`. When a Codex patch arrives while disabled, the tool returns an **actionable hint** (how to enable or how to retry in unified diff) instead of a generic parse error — Codex syntax is a strong prior for GPT-family models and a silent failure traps them in retry loops.
|
|
64
|
+
|
|
65
|
+
### Operations
|
|
66
|
+
|
|
67
|
+
| Operation | Unified diff | Codex | Execution path |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| Add | `new file mode` + `/dev/null` | `*** Add File:` | `ctx.fs.writeText` (official intent dance) |
|
|
70
|
+
| Update (multi-hunk) | `@@` hunks | `@@` hunks | `ctx.fs.writeText` |
|
|
71
|
+
| Delete | `deleted file mode` + `/dev/null` | `*** Delete File:` | `ctx.shell` (sandbox-aware, env-passed path) |
|
|
72
|
+
| Move / rename | `rename from/to` | `*** Move to:` | `ctx.shell` |
|
|
73
|
+
|
|
74
|
+
## Guarantees
|
|
75
|
+
|
|
76
|
+
- **All-or-nothing**: the whole patch is verified against current file content first (context location, workspace containment, symlink policy, duplicate paths); any failure aborts with zero bytes written.
|
|
77
|
+
- **Read-before-write gate respected**: every write replicates the official `write` tool's intent dance — `fs/write-intent` waterfall → guarded `writeText` → `fs/observed` emit. A bare `writeText` would silently bypass the gate because DSH providers never dispatch `fs/*` events themselves.
|
|
78
|
+
- **Precise failure diagnostics**: on hunk mismatch the error carries file + hunk index, search origin line, expected-line preview (whitespace made visible), the file's actual excerpt, and a targeted hint.
|
|
79
|
+
- **Newline fidelity**: CRLF files are written back as CRLF; no-trailing-newline tails are preserved unless the patch changes the tail.
|
|
80
|
+
- **Pure addition**: the plugin registers exactly one tool, never calls `tools.restrict()`, never overrides a native tool, registers no global services, and removes everything on unload. Name conflicts with other `apply_patch` providers (`bainianlaoyao/dsh-codex-mode`, `shuind/dsh-codex-harness`, …) are resolved by rename avoidance (`apply_patch_1`, `…_2`, …) so DSH never fails to start.
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
| Option | Default | Description |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `toolName` | `apply_patch` | Model-facing tool name. |
|
|
87
|
+
| `conflictPolicy` | `rename` | `rename` / `skip` / `fail` when the tool name is taken. |
|
|
88
|
+
| `renameSuffix` | `_1` | Suffix used by rename avoidance. |
|
|
89
|
+
| `allowUnifiedDiff` | `true` | Accept git/unified diffs. |
|
|
90
|
+
| `allowCodexPatch` | `false` | Accept Codex `apply_patch` syntax. |
|
|
91
|
+
| `deleteBackend` | `shell` | `shell` or `none` (Delete/Move return a structured error). |
|
|
92
|
+
| `shellDialect` | `auto` | `auto` (pwsh on win32) / `posix` / `pwsh`. |
|
|
93
|
+
| `deleteCommand` / `moveCommand` | built-in | Custom command templates. Paths still arrive **only via env** (`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`); never interpolate them into the command string. |
|
|
94
|
+
| `dryRunByDefault` | `false` | Treat calls as dry runs unless `dryRun: false`. |
|
|
95
|
+
| `followSymlinks` | `false` | Allow patch paths to traverse symlinks. |
|
|
96
|
+
| `maxFiles` | `50` | Max file sections per patch. |
|
|
97
|
+
| `maxPatchBytes` | `524288` | Max patch text size in bytes. |
|
|
98
|
+
| `maxDiffBytes` | `16384` | Max per-file diff kept in the result metadata (larger diffs are emptied and marked `truncated`). |
|
|
99
|
+
|
|
100
|
+
## Limitations (disclosed)
|
|
101
|
+
|
|
102
|
+
- **Delete/Move run through `ctx.shell`.** The sandbox strength is whatever the loaded shell executor enforces (`bash-sandbox` fences; `bash-local` does not) — the same risk posture as the native bash tool. Every request carries a `sandboxPolicy` and reports sandbox facts so "policy denied" is distinguishable from "command failed".
|
|
103
|
+
- **Add does not create parent directories.** This matches the native `write` tool (`ctx.fs` has no mkdir); the error names the missing directory.
|
|
104
|
+
- No fuzzy/offset matching: hunk location is exact → `trimEnd` → `trim`, deliberately (fuzzy matching is unsafe for destructive ops; it is on the roadmap).
|
|
105
|
+
- Binary patches are rejected with a clear error.
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm install
|
|
111
|
+
npm run typecheck # against 0.1.2-rc.1 peers (devDependencies)
|
|
112
|
+
npm run typecheck:0.1.5 # against 0.1.5-rc.2 peers (static dual-version proof)
|
|
113
|
+
npm test # vitest, 84 tests
|
|
114
|
+
npm run lint
|
|
115
|
+
npm run build # lib/
|
|
116
|
+
npm run verify:source # static safety assertions (intent dance, no node:fs, …)
|
|
117
|
+
npm run smoke # load-level smoke against a stub host
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Manual verification on a live DSH (`dsh plugin --profile web add` → restart → call the tool in a session) is intentionally left to the operator.
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# dsh-patch-edit-plus
|
|
2
|
+
|
|
3
|
+
面向 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 的补丁风格文件编辑插件:只提供 **1 个**模型可见工具 `apply_patch`,接受 **git/unified diff**(默认开启)与 **Codex `apply_patch` 语法**(可选开启),全量验证后**原子应用**,对 DSH 既有功能零负面影响。
|
|
4
|
+
|
|
5
|
+
## DSH 版本适配矩阵
|
|
6
|
+
|
|
7
|
+
| DSH 版本 | 状态 | 说明 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `0.1.2-rc.1` | ✅ 支持 | 工具编写契约(`defineTool` DSL、presentation 视图)在 0.1.2–0.1.5 间逐字节未变,单一代码路径服务两个版本。 |
|
|
10
|
+
| `0.1.3-alpha.x` | ✅ 支持 | 同上。 |
|
|
11
|
+
| `0.1.5-rc.2` | ✅ 静态核验 | `npm run typecheck:0.1.5` 用真实 `0.1.5-rc.2` peer 包对 `src/` 做类型核验;不调用 `readByteRange`,不含 0.1.5-only PTC 字符串。 |
|
|
12
|
+
|
|
13
|
+
settings 注册走双 API 回退:0.1.2+ 的 `settings.installSection` 优先,旧版回退 `settings.register`。
|
|
14
|
+
|
|
15
|
+
## 工具路由指引(为什么该用谁)
|
|
16
|
+
|
|
17
|
+
工具 description 内置了明确的路由规则:
|
|
18
|
+
|
|
19
|
+
- **单个已读文件里的单点小改** → 优先原生 `edit`(patch 反而多花 token)。
|
|
20
|
+
- **大文件里的小改动** → `apply_patch`(无需回显整个文件)。
|
|
21
|
+
- **跨文件批量改动** → `apply_patch`(一次调用、原子验证)。
|
|
22
|
+
- **新建大内容文件 / 删除 / 重命名** → `apply_patch`。
|
|
23
|
+
|
|
24
|
+
## 安装
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dsh plugin --profile web add <dsh-patch-edit-plus 路径>
|
|
28
|
+
dsh web --dump-config # 确认插件行出现
|
|
29
|
+
# 重启 DSH
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 使用示例
|
|
33
|
+
|
|
34
|
+
### unified diff(默认)
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
--- a/src/app.ts
|
|
38
|
+
+++ b/src/app.ts
|
|
39
|
+
@@ -10,4 +10,4 @@ export function main() {
|
|
40
|
+
init()
|
|
41
|
+
- start(oldPort)
|
|
42
|
+
+ start(newPort)
|
|
43
|
+
await shutdown()
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Codex apply_patch 语法(需开启)
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
*** Begin Patch
|
|
51
|
+
*** Update File: src/app.ts
|
|
52
|
+
@@
|
|
53
|
+
init()
|
|
54
|
+
- start(oldPort)
|
|
55
|
+
+ start(newPort)
|
|
56
|
+
*** Add File: docs/notes.md
|
|
57
|
+
+# Notes
|
|
58
|
+
*** Delete File: tmp/junk.txt
|
|
59
|
+
*** End Patch
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
在配置中设 `allowCodexPatch: true` 开启。关闭状态下收到 Codex 补丁时,工具返回**可操作提示**(如何开启、或改用 unified diff),而不是泛化的解析错误 —— Codex 语法是 GPT 系模型的强先验,沉默失败会让模型陷入重试循环。
|
|
63
|
+
|
|
64
|
+
### 四类操作
|
|
65
|
+
|
|
66
|
+
| 操作 | unified diff | Codex | 执行路径 |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| Add | `new file mode` + `/dev/null` | `*** Add File:` | `ctx.fs.writeText`(官方 intent 舞蹈) |
|
|
69
|
+
| Update(多 hunk) | `@@` hunk | `@@` hunk | `ctx.fs.writeText` |
|
|
70
|
+
| Delete | `deleted file mode` + `/dev/null` | `*** Delete File:` | `ctx.shell`(沙箱感知,路径走 env) |
|
|
71
|
+
| Move / rename | `rename from/to` | `*** Move to:` | `ctx.shell` |
|
|
72
|
+
|
|
73
|
+
## 保证
|
|
74
|
+
|
|
75
|
+
- **全量先验证后写入**:整个补丁先对当前文件内容做验证(上下文定位、工作区围栏、symlink 策略、路径去重),任一失败即零写入中止。
|
|
76
|
+
- **read-before-write 门禁生效**:每次写入完整复刻官方 `write` 工具的 intent 舞蹈 —— `fs/write-intent` waterfall → 带 intent 的 `writeText` → `fs/observed` 事件。DSH 的 provider 从不分发 `fs/*` 事件,裸调 `writeText` 会静默绕过门禁。
|
|
77
|
+
- **精准失败诊断**:hunk 定位失败时,错误包含文件 + hunk 序号、搜索起始行号、期望行预览(空白字符可视化)、文件实际片段和针对性提示。
|
|
78
|
+
- **换行保真**:CRLF 文件按原风格写回;无尾换行状态保持,除非补丁本身改动了文件尾。
|
|
79
|
+
- **纯增量**:只注册 1 个工具、绝不调用 `tools.restrict()`、不覆盖原生工具、不注册全局服务、卸载即完全恢复。与其他 `apply_patch` 提供方(`bainianlaoyao/dsh-codex-mode`、`shuind/dsh-codex-harness` 等)的重名冲突通过改名避让解决(`apply_patch_1`、`…_2`…),不会导致 DSH 启动失败。
|
|
80
|
+
|
|
81
|
+
## 配置项
|
|
82
|
+
|
|
83
|
+
| 选项 | 默认值 | 说明 |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `toolName` | `apply_patch` | 模型可见工具名。 |
|
|
86
|
+
| `conflictPolicy` | `rename` | 重名时 `rename` / `skip` / `fail`。 |
|
|
87
|
+
| `renameSuffix` | `_1` | 改名避让使用的后缀。 |
|
|
88
|
+
| `allowUnifiedDiff` | `true` | 接受 git/unified diff。 |
|
|
89
|
+
| `allowCodexPatch` | `false` | 接受 Codex `apply_patch` 语法。 |
|
|
90
|
+
| `deleteBackend` | `shell` | `shell` 或 `none`(Delete/Move 返回结构化错误)。 |
|
|
91
|
+
| `shellDialect` | `auto` | `auto`(win32 上 pwsh)/ `posix` / `pwsh`。 |
|
|
92
|
+
| `deleteCommand` / `moveCommand` | 内置 | 自定义命令模板。路径仍然**只经 env** 传入(`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`),请勿把路径插值进命令串。 |
|
|
93
|
+
| `dryRunByDefault` | `false` | 默认按 dry run 处理,除非调用显式传 `dryRun: false`。 |
|
|
94
|
+
| `followSymlinks` | `false` | 允许补丁路径穿越符号链接。 |
|
|
95
|
+
| `maxFiles` | `50` | 单补丁最大文件段数。 |
|
|
96
|
+
| `maxPatchBytes` | `524288` | 补丁文本字节上限。 |
|
|
97
|
+
| `maxDiffBytes` | `16384` | 结果元数据中单文件 diff 上限(超出置空并标记 `truncated`)。 |
|
|
98
|
+
|
|
99
|
+
## 限制(明确披露)
|
|
100
|
+
|
|
101
|
+
- **Delete/Move 走 `ctx.shell`**:沙箱强度取决于装载的 shell 执行器(`bash-sandbox` 有围栏;`bash-local` 没有)—— 与原生 bash 工具的风险姿态一致。每次请求都携带 `sandboxPolicy` 并回传沙箱事实,"策略拒绝"与"命令失败"可区分。
|
|
102
|
+
- **Add 不创建父目录**:与原生 `write` 工具一致(`ctx.fs` 无 mkdir);错误信息会指出缺失的目录。
|
|
103
|
+
- 不做模糊/偏移匹配:定位容错为精确 → `trimEnd` → `trim` 三级,刻意保守(模糊匹配对删除类操作不安全,已列入路线图)。
|
|
104
|
+
- 二进制补丁被拒绝并给出明确错误。
|
|
105
|
+
|
|
106
|
+
## 开发
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm install
|
|
110
|
+
npm run typecheck # 针对 0.1.2-rc.1 peer 包(devDependencies)
|
|
111
|
+
npm run typecheck:0.1.5 # 针对 0.1.5-rc.2 peer 包(双版本静态核验)
|
|
112
|
+
npm test # vitest,84 个用例
|
|
113
|
+
npm run lint
|
|
114
|
+
npm run build # lib/
|
|
115
|
+
npm run verify:source # 静态安全断言(intent 舞蹈、无 node:fs 等)
|
|
116
|
+
npm run smoke # stub 宿主加载冒烟
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
实机验证(`dsh plugin --profile web add` → 重启 → 会话调用)由部署者手动执行。
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
package/assets/.gitkeep
ADDED
|
File without changes
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Assembly layer for the dsh profiles whose bundles list includes this package.
|
|
2
|
+
# A pure addition: one insert row with an id no shipped entry uses. No shipped
|
|
3
|
+
# row is disabled, renamed, or reconfigured here.
|
|
4
|
+
- insert:
|
|
5
|
+
- id: dsh-patch-edit-plus
|
|
6
|
+
name: dsh-patch-edit-plus
|
package/dsh.plugin.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dsh-patch-edit-plus",
|
|
3
|
+
"name": "dsh-patch-edit-plus",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": {
|
|
6
|
+
"en": "Patch-style file editing: one apply_patch tool for git/unified diff (default) and Codex apply_patch syntax (opt-in), with all-or-nothing application and sandbox-aware delete/move.",
|
|
7
|
+
"zh": "补丁风格文件编辑:单个 apply_patch 工具,接受 git/unified diff(默认)与 Codex apply_patch 语法(可选开启),全量验证后原子应用,删除/移动走沙箱感知 shell。"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/drscrewdriver/dsh-patch-edit-plus",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/drscrewdriver/dsh-patch-edit-plus.git"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"engines": {
|
|
16
|
+
"dsh": ">=0.1.2-rc.1 <0.2.0-0"
|
|
17
|
+
},
|
|
18
|
+
"components": {
|
|
19
|
+
"host": "lib/index.js"
|
|
20
|
+
}
|
|
21
|
+
}
|