dsh-patch-edit-plus 0.4.6 → 0.4.8

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.
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Model-facing routing guidance for the patch tool.
3
+ *
4
+ * DSH 0.2.2-rc.1 ships its own `apply_patch` inside `dsh-tool-fs` and, with it,
5
+ * the first official guidance text for the tool: one description line plus a
6
+ * `tool:apply_patch` system-prompt section placed at `TOOL_APPLY_PATCH`
7
+ * (1350, between edit 1300 and glob 1400). Older hosts have no such section,
8
+ * so a plugin-provided patch tool is only described by its tool description —
9
+ * a weaker, per-call surface. This module reproduces the official shape on
10
+ * every host line: one short line in the description, one section in the
11
+ * system prompt.
12
+ *
13
+ * What is borrowed from 0.2.2 and what is not:
14
+ * - Borrowed: the explicit "prefer apply_patch over separate edit calls"
15
+ * phrasing, the hunk-context advice, "a rejected patch changes nothing",
16
+ * and the scope-aware empty text when the tool is absent.
17
+ * - Not borrowed: "not rolled back". Verification is all-or-nothing here, but
18
+ * the write phase is sequential without rollback, so claiming atomicity
19
+ * after the first byte would be false. The text says what is true instead.
20
+ * @module dsh-patch-edit-plus/guidance
21
+ */
22
+ /** The short routing line carried by the tool description (repeated every call). */
23
+ export const PATCH_ROUTING_LINE = 'Prefer apply_patch over separate edit calls when a change spans several files, several separated regions of one file, or adds/deletes/moves files; a single small replacement in an already-read file is still cheaper through the native edit tool.';
24
+ /**
25
+ * The full guidance paragraph registered as a system-prompt section. The
26
+ * description line stays short on purpose: the section is what keeps the model
27
+ * honest across turns, exactly as the official 0.2.2 composition does.
28
+ */
29
+ export const PATCH_GUIDANCE_TEXT = 'Prefer apply_patch over separate edit calls when a change spans several files, several separated regions of one file, or adds, deletes or moves files. Give every hunk enough surrounding context lines to match exactly one place in the current file. Every hunk is matched against the current file content before anything is written, so a rejected patch changes nothing — re-read the file and retry with a corrected patch instead of falling back to many small edits. Once writing starts the operations run in order and stop at the first failure.';
30
+ /** Stable section name; namespaced so it cannot collide with the host's `tool:apply_patch`. */
31
+ export const PATCH_GUIDANCE_SECTION = 'plugin:patch-edit-plus:apply_patch';
32
+ /** Order used when the host predates the `TOOL_APPLY_PATCH` constant (0.2.2 added it at 1350). */
33
+ export const PATCH_GUIDANCE_ORDER = 1350;
34
+ /**
35
+ * Register the patch guidance section. Returns the disposer, or `null` when the
36
+ * host has no system-prompt service (very old lines) — never throws.
37
+ *
38
+ * @param ctx - plugin context; the service is probed, never declared in `inject`,
39
+ * so a host without `systemPrompt` keeps loading instead of stalling.
40
+ * @param toolName - the name the tool actually registered under.
41
+ * @param mode - `'on'` always registers, `'auto'` is decided by the caller,
42
+ * `'off'` never registers.
43
+ */
44
+ export function registerPatchGuidance(ctx, toolName, mode) {
45
+ if (mode === 'off')
46
+ return null;
47
+ const prompt = resolveSystemPrompt(ctx);
48
+ if (prompt === undefined) {
49
+ if (mode === 'on') {
50
+ console.warn('[dsh-patch-edit-plus] guidance is "on" but this host has no systemPrompt service; the tool description still carries the routing line.');
51
+ }
52
+ return null;
53
+ }
54
+ const text = (view) => {
55
+ const scope = view?.scope;
56
+ return isRegistered(ctx, toolName, scope) ? PATCH_GUIDANCE_TEXT : '';
57
+ };
58
+ try {
59
+ const disposer = prompt.section({
60
+ name: PATCH_GUIDANCE_SECTION,
61
+ order: resolveOrder(prompt),
62
+ text,
63
+ });
64
+ return typeof disposer === 'function' ? disposer : null;
65
+ }
66
+ catch (error) {
67
+ console.warn('[dsh-patch-edit-plus] system-prompt section unavailable; the tool description still carries the routing line:', error);
68
+ return null;
69
+ }
70
+ }
71
+ /** Probe `ctx.systemPrompt` through the cordis inject gate without declaring the dependency. */
72
+ function resolveSystemPrompt(ctx) {
73
+ try {
74
+ const candidate = ctx.systemPrompt;
75
+ if (typeof candidate !== 'object' || candidate === null)
76
+ return undefined;
77
+ const face = candidate;
78
+ return typeof face.section === 'function' ? face : undefined;
79
+ }
80
+ catch {
81
+ // Undeclared service access throws on cordis; absence is the correct reading.
82
+ return undefined;
83
+ }
84
+ }
85
+ /**
86
+ * Resolve the section order across host generations.
87
+ *
88
+ * `TOOL_APPLY_PATCH` only exists from 0.2.2; 0.1.0-rc.8 has no
89
+ * `getSectionOrder` at all. A non-finite order makes `section()` throw, so
90
+ * every step of the chain is checked and finally backed by the literal 1350.
91
+ */
92
+ function resolveOrder(prompt) {
93
+ const resolve = prompt.getSectionOrder;
94
+ if (typeof resolve !== 'function')
95
+ return PATCH_GUIDANCE_ORDER;
96
+ const exact = resolve.call(prompt, 'TOOL_APPLY_PATCH');
97
+ if (typeof exact === 'number' && Number.isFinite(exact))
98
+ return exact;
99
+ const edit = resolve.call(prompt, 'TOOL_EDIT');
100
+ if (typeof edit === 'number' && Number.isFinite(edit))
101
+ return edit + 50;
102
+ return PATCH_GUIDANCE_ORDER;
103
+ }
104
+ /** Scope-aware presence check, mirroring the official `tool:apply_patch` section. */
105
+ function isRegistered(ctx, toolName, scope) {
106
+ try {
107
+ const present = scope === undefined
108
+ ? ctx.tools.get(toolName)
109
+ : ctx.tools.get(toolName, scope);
110
+ return present !== undefined;
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
package/lib/index.js CHANGED
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import { resolveConfig } from './config.js';
16
16
  import { registerApplyPatchTool } from './register.js';
17
+ import { registerPatchGuidance } from './guidance.js';
17
18
  import { installSettingsCompat } from './compat.js';
18
19
  import { readVolatileBoolean } from './config.js';
19
20
  import { Config } from './config.js';
@@ -52,6 +53,7 @@ export function apply(ctx, config) {
52
53
  // 的 setSource 在 attach 时先于 onChange 调用),否则是组合条目。
53
54
  let readSource = () => compositionEntry;
54
55
  let disposer = null;
56
+ let guidanceDisposer = null;
55
57
  let registeredKey = null;
56
58
  const rejudge = () => {
57
59
  const next = resolveConfig(readSource());
@@ -59,7 +61,19 @@ export function apply(ctx, config) {
59
61
  if (key === registeredKey)
60
62
  return;
61
63
  disposer?.();
62
- disposer = registerApplyPatchTool(ctx, next) ?? null;
64
+ disposer = null;
65
+ guidanceDisposer?.();
66
+ guidanceDisposer = null;
67
+ // `auto` guidance stays silent when rename avoidance had to step aside:
68
+ // another patch tool already owns the preferred name and carries its own
69
+ // guidance, so a second paragraph would only fight it.
70
+ let registeredName = null;
71
+ disposer = registerApplyPatchTool(ctx, next, undefined, (name) => { registeredName = name; }) ?? null;
72
+ if (disposer !== null && registeredName !== null) {
73
+ const wanted = next.guidance === 'on' || (next.guidance === 'auto' && registeredName === next.toolName);
74
+ if (wanted)
75
+ guidanceDisposer = registerPatchGuidance(ctx, registeredName, next.guidance);
76
+ }
63
77
  registeredKey = key;
64
78
  };
65
79
  ctx.effect(() => {
@@ -67,6 +81,8 @@ export function apply(ctx, config) {
67
81
  return () => {
68
82
  disposer?.();
69
83
  disposer = null;
84
+ guidanceDisposer?.();
85
+ guidanceDisposer = null;
70
86
  registeredKey = null;
71
87
  };
72
88
  });
package/lib/register.js CHANGED
@@ -12,10 +12,13 @@ import { buildApplyPatchTool } from './tool.js';
12
12
  const MAX_RENAME_ATTEMPTS = 9;
13
13
  /**
14
14
  * Resolve an unused tool name and register the frozen definition.
15
+ * @param onRegistered - called with the name actually registered; the caller
16
+ * uses it to decide whether the system-prompt guidance should attach
17
+ * (`auto` stays silent when rename avoidance had to step aside).
15
18
  * @returns the registration disposer, or `null` when registration was skipped.
16
19
  * @throws {@link PatchError} only under `conflictPolicy: 'fail'`.
17
20
  */
18
- export function registerApplyPatchTool(ctx, cfg, logger) {
21
+ export function registerApplyPatchTool(ctx, cfg, logger, onRegistered) {
19
22
  const log = logger ?? resolveLogger(ctx);
20
23
  const tools = ctx.tools;
21
24
  const base = cfg.toolName;
@@ -46,7 +49,9 @@ export function registerApplyPatchTool(ctx, cfg, logger) {
46
49
  if (definition === null)
47
50
  return null;
48
51
  try {
49
- return tools.register(definition);
52
+ const disposer = tools.register(definition);
53
+ onRegistered?.(resolvedName);
54
+ return typeof disposer === 'function' ? disposer : null;
50
55
  }
51
56
  catch (error) {
52
57
  if (cfg.conflictPolicy === 'fail')
package/lib/tool.js CHANGED
@@ -1,14 +1,15 @@
1
1
  /**
2
2
  * The single model-facing `apply_patch` tool definition.
3
3
  *
4
- * The description carries an explicit routing guide so the model reaches for
5
- * the native `edit` tool on cheap single-point changes instead of paying for
6
- * a patch, and reserves `apply_patch` for large-file small edits, cross-file
7
- * batch edits, creation, deletion and renames.
4
+ * The description carries the short routing line; the long-form guidance is a
5
+ * system-prompt section (see guidance.ts) so it survives across turns instead
6
+ * of only riding on the per-call tool schema. Before 0.2.2 no host shipped
7
+ * either surface for `apply_patch`, which is why the plugin has to supply both.
8
8
  * @module dsh-patch-edit-plus/tool
9
9
  */
10
10
  import * as dshToolsNamespace from '@deepseek-ai/dsh-tools';
11
11
  import { PatchError, codexDisabledError, limitError, unifiedDisabledError } from './errors.js';
12
+ import { PATCH_ROUTING_LINE } from './guidance.js';
12
13
  import { detectFormat } from './detect.js';
13
14
  import { resolveSandboxPolicy } from './policy.js';
14
15
  import { parseCodexPatch } from './parse/codex.js';
@@ -16,12 +17,7 @@ import { parseUnifiedDiff } from './parse/unified.js';
16
17
  import { buildPlan } from './apply/plan.js';
17
18
  import { applyPatch, verifyPatch } from './apply/engine.js';
18
19
  const ROUTING_GUIDE = [
19
- 'When to use which editing tool:',
20
- '- Single small change in ONE already-read file: prefer the native edit tool (cheaper than a patch).',
21
- '- apply_patch is the right choice when: (a) a SMALL change sits inside a LARGE file (a patch avoids echoing the whole file), '
22
- + '(b) the same call must change MANY files at once (batch), (c) a new file with substantial content is created, '
23
- + '(d) a file is deleted or renamed, or (e) several disjoint spots in one file change at once.',
24
- '- Do not use apply_patch for trivial one-line single-file edits when the native edit tool is available.',
20
+ PATCH_ROUTING_LINE,
25
21
  ].join('\n');
26
22
  /**
27
23
  /** Build the (frozen) tool definition. The tool name is passed in resolved
@@ -13,6 +13,15 @@ export type ConflictPolicy = 'rename' | 'skip' | 'fail';
13
13
  export type DeleteBackend = 'shell' | 'none';
14
14
  /** Shell command dialect for the constant delete/move templates. */
15
15
  export type ShellDialect = 'auto' | 'posix' | 'pwsh';
16
+ /**
17
+ * System-prompt guidance mode.
18
+ * - `auto` — register the patch guidance unless the tool had to be renamed
19
+ * (another patch tool already owns the preferred name, e.g. a host-shipped
20
+ * `apply_patch`), so two guidance texts never compete.
21
+ * - `on` — always register it.
22
+ * - `off` — never register it; the tool description keeps the routing line.
23
+ */
24
+ export type GuidanceMode = 'auto' | 'on' | 'off';
16
25
  /** Deployment-owned configuration. */
17
26
  export interface Config {
18
27
  /** Model-facing tool name. Defaults to `apply_patch`. */
@@ -37,6 +46,8 @@ export interface Config {
37
46
  deleteCommand?: string;
38
47
  /** Custom move command template; paths arrive via env `DSH_PATCH_SOURCE`/`DSH_PATCH_TARGET`. */
39
48
  moveCommand?: string;
49
+ /** System-prompt guidance registration. Defaults to `auto`. */
50
+ guidance?: GuidanceMode;
40
51
  /** Treat `apply_patch` calls as dry runs unless the call passes `dryRun: false`. Defaults to false. */
41
52
  dryRunByDefault?: boolean;
42
53
  /** Allow patch paths to traverse symbolic links. Defaults to false (symlinks are rejected). */
@@ -59,6 +70,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
59
70
  shellDialect: z<"auto" | "posix" | "pwsh", "auto" | "posix" | "pwsh", "defined">;
60
71
  deleteCommand: z<string, string, "plain">;
61
72
  moveCommand: z<string, string, "plain">;
73
+ guidance: z<"auto" | "on" | "off", "auto" | "on" | "off", "defined">;
62
74
  dryRunByDefault: z<boolean, boolean, "defined">;
63
75
  followSymlinks: z<boolean, boolean, "defined">;
64
76
  maxFiles: z<number, number, "defined">;
@@ -74,6 +86,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
74
86
  shellDialect: z<"auto" | "posix" | "pwsh", "auto" | "posix" | "pwsh", "defined">;
75
87
  deleteCommand: z<string, string, "plain">;
76
88
  moveCommand: z<string, string, "plain">;
89
+ guidance: z<"auto" | "on" | "off", "auto" | "on" | "off", "defined">;
77
90
  dryRunByDefault: z<boolean, boolean, "defined">;
78
91
  followSymlinks: z<boolean, boolean, "defined">;
79
92
  maxFiles: z<number, number, "defined">;
@@ -91,6 +104,7 @@ export interface ResolvedConfig {
91
104
  shellDialect: ShellDialect;
92
105
  deleteCommand?: string;
93
106
  moveCommand?: string;
107
+ guidance: GuidanceMode;
94
108
  dryRunByDefault: boolean;
95
109
  followSymlinks: boolean;
96
110
  maxFiles: number;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Model-facing routing guidance for the patch tool.
3
+ *
4
+ * DSH 0.2.2-rc.1 ships its own `apply_patch` inside `dsh-tool-fs` and, with it,
5
+ * the first official guidance text for the tool: one description line plus a
6
+ * `tool:apply_patch` system-prompt section placed at `TOOL_APPLY_PATCH`
7
+ * (1350, between edit 1300 and glob 1400). Older hosts have no such section,
8
+ * so a plugin-provided patch tool is only described by its tool description —
9
+ * a weaker, per-call surface. This module reproduces the official shape on
10
+ * every host line: one short line in the description, one section in the
11
+ * system prompt.
12
+ *
13
+ * What is borrowed from 0.2.2 and what is not:
14
+ * - Borrowed: the explicit "prefer apply_patch over separate edit calls"
15
+ * phrasing, the hunk-context advice, "a rejected patch changes nothing",
16
+ * and the scope-aware empty text when the tool is absent.
17
+ * - Not borrowed: "not rolled back". Verification is all-or-nothing here, but
18
+ * the write phase is sequential without rollback, so claiming atomicity
19
+ * after the first byte would be false. The text says what is true instead.
20
+ * @module dsh-patch-edit-plus/guidance
21
+ */
22
+ import type { Context } from '@deepseek-ai/cordis';
23
+ import type { GuidanceMode } from './config.js';
24
+ /** The short routing line carried by the tool description (repeated every call). */
25
+ export declare const PATCH_ROUTING_LINE = "Prefer apply_patch over separate edit calls when a change spans several files, several separated regions of one file, or adds/deletes/moves files; a single small replacement in an already-read file is still cheaper through the native edit tool.";
26
+ /**
27
+ * The full guidance paragraph registered as a system-prompt section. The
28
+ * description line stays short on purpose: the section is what keeps the model
29
+ * honest across turns, exactly as the official 0.2.2 composition does.
30
+ */
31
+ export declare const PATCH_GUIDANCE_TEXT = "Prefer apply_patch over separate edit calls when a change spans several files, several separated regions of one file, or adds, deletes or moves files. Give every hunk enough surrounding context lines to match exactly one place in the current file. Every hunk is matched against the current file content before anything is written, so a rejected patch changes nothing \u2014 re-read the file and retry with a corrected patch instead of falling back to many small edits. Once writing starts the operations run in order and stop at the first failure.";
32
+ /** Stable section name; namespaced so it cannot collide with the host's `tool:apply_patch`. */
33
+ export declare const PATCH_GUIDANCE_SECTION = "plugin:patch-edit-plus:apply_patch";
34
+ /** Order used when the host predates the `TOOL_APPLY_PATCH` constant (0.2.2 added it at 1350). */
35
+ export declare const PATCH_GUIDANCE_ORDER = 1350;
36
+ /** The slice of the `systemPrompt` service this module needs. */
37
+ export interface SystemPromptFace {
38
+ section(section: {
39
+ name: string;
40
+ order: number;
41
+ text: (view?: unknown) => string;
42
+ }): unknown;
43
+ getSectionOrder?(name: string): number | undefined;
44
+ }
45
+ /**
46
+ * Register the patch guidance section. Returns the disposer, or `null` when the
47
+ * host has no system-prompt service (very old lines) — never throws.
48
+ *
49
+ * @param ctx - plugin context; the service is probed, never declared in `inject`,
50
+ * so a host without `systemPrompt` keeps loading instead of stalling.
51
+ * @param toolName - the name the tool actually registered under.
52
+ * @param mode - `'on'` always registers, `'auto'` is decided by the caller,
53
+ * `'off'` never registers.
54
+ */
55
+ export declare function registerPatchGuidance(ctx: Context, toolName: string, mode: GuidanceMode): (() => void) | null;
@@ -17,7 +17,10 @@ export interface LoggerFace {
17
17
  }
18
18
  /**
19
19
  * Resolve an unused tool name and register the frozen definition.
20
+ * @param onRegistered - called with the name actually registered; the caller
21
+ * uses it to decide whether the system-prompt guidance should attach
22
+ * (`auto` stays silent when rename avoidance had to step aside).
20
23
  * @returns the registration disposer, or `null` when registration was skipped.
21
24
  * @throws {@link PatchError} only under `conflictPolicy: 'fail'`.
22
25
  */
23
- export declare function registerApplyPatchTool(ctx: Context, cfg: ResolvedConfig, logger?: LoggerFace): (() => void) | null;
26
+ export declare function registerApplyPatchTool(ctx: Context, cfg: ResolvedConfig, logger?: LoggerFace, onRegistered?: (name: string) => void): (() => void) | null;
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * The single model-facing `apply_patch` tool definition.
3
3
  *
4
- * The description carries an explicit routing guide so the model reaches for
5
- * the native `edit` tool on cheap single-point changes instead of paying for
6
- * a patch, and reserves `apply_patch` for large-file small edits, cross-file
7
- * batch edits, creation, deletion and renames.
4
+ * The description carries the short routing line; the long-form guidance is a
5
+ * system-prompt section (see guidance.ts) so it survives across turns instead
6
+ * of only riding on the per-call tool schema. Before 0.2.2 no host shipped
7
+ * either surface for `apply_patch`, which is why the plugin has to supply both.
8
8
  * @module dsh-patch-edit-plus/tool
9
9
  */
10
10
  import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
package/locale/de.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Patch-Editor",
4
+ "description": "Dateieditierung im Patch-Format für DSH: ein einziges apply_patch-Werkzeug, das git/unified-Diff standardmäßig annimmt (Codex-apply_patch-Syntax optional), ganz oder gar nicht angewendet, nachdem der Dateiinhalt geprüft wurde; liest vor dem Schreiben, löscht und verschiebt mit Sandboxbewusstsein, und kann vorher eine dryRun-Prüfung ausführen."
5
+ }
6
+ }
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Patch Edit Plus",
4
+ "description": "Patch-style file editing for DSH: one apply_patch tool accepting git/unified diff (default) and Codex apply_patch syntax (opt-in), applied all-or-nothing against verified file content, with read-before-write intent, sandbox-aware delete/move, and an optional dry-run check."
5
+ }
6
+ }
package/locale/es.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Edición por patch",
4
+ "description": "Edición de archivos por patch para DSH: un solo tool apply_patch que acepta el diff git/unified por defecto y la sintaxis Codex apply_patch opcional, aplicada en todo o nada contra el contenido verificado del archivo, con intención de lectura antes de escritura, borrado/movimiento consciente de la sandbox, y comprobación dry-run opcional."
5
+ }
6
+ }
package/locale/fr.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Édition par patch",
4
+ "description": "Édition de fichiers par patch pour DSH : un seul outil apply_patch qui accepte le diff git/unified par défaut et la syntaxe Codex apply_patch en option ; appliqué en bloc ou pas du tout, après vérification du contenu du fichier ; lit avant d'écrire, supprime et déplace en tenant compte de la sandbox, et peut d'abord faire un contrôle dry-run."
5
+ }
6
+ }
package/locale/it.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Editor di patch",
4
+ "description": "Editing a patch per DSH: un solo tool apply_patch che accetta il diff git/unified per default e la sintassi Codex apply_patch come opzione; applicato in tutto o in niente, dopo la verifica del contenuto del file; legge prima di scrivere, cancella e sposta tenendo conto della sandbox, e può prima fare un controllo dry-run."
5
+ }
6
+ }
package/locale/ja.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "パッチ編集",
4
+ "description": "DSH 用のパッチ式ファイル編集:単一の apply_patch ツールが既定で git/unified diff を受け入れ、任意で Codex apply_patch 構文も選べる。固まり全体を一度だけ適用し、現在のファイル内容と照合してから書き込む。読込前置の意図、サンドボックス配慮の削除・移動、任意の dry-run 事前検査を備える。"
5
+ }
6
+ }
package/locale/ko.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "패치 편집",
4
+ "description": "DSH용 패치식 파일 편집: apply_patch 도구 하나가 기본으로 git·unified diff를 받고, 고르면 Codex apply_patch 문법도 쓸 수 있다. 묶음 전체를 한 번에만 적용하고, 현재 파일 내용과 대조한 뒤에 기록한다. 읽기 우선 의도와 샌드박스 배려 삭제·이동, 임의의 dry-run 사전 검사를 갖춘다."
5
+ }
6
+ }
package/locale/ru.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Редактор по patch",
4
+ "description": "Редактирование файлов по patch для DSH: один tool apply_patch принимает git/unified diff по умолчанию и syntax Codex apply_patch по выбору, применяется целиком или ничего против проверенного содержимого файла; читает перед записью; delete/move учитывает sandbox; может сначала сделать dry-run проверку."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "补丁式编辑",
4
+ "description": "为 DSH 提供补丁式文件编辑:单个 apply_patch 工具,默认接受 git/unified diff,另可选 Codex apply_patch 语法;整块应用,先按当前文件内容校验再写入,含读后写意图、沙箱感知的删除与移动,以及可选的 dry-run 预检。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-patch-edit-plus",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Patch-style file editing for DeepSeek Harness: one apply_patch tool accepting git/unified diff (default) and Codex apply_patch syntax (opt-in), with all-or-nothing application, read-before-write intent dance, sandbox-aware delete/move, (declarative volatile settings).",
5
5
  "type": "module",
6
6
  "repository": {
@@ -18,17 +18,42 @@
18
18
  "types": "./lib/types/client/index.d.ts",
19
19
  "default": "./lib/client.js"
20
20
  },
21
+ "./locale/*.json": "./locale/*.json",
21
22
  "./package.json": "./package.json"
22
23
  },
23
24
  "files": [
24
25
  "lib/",
26
+ "locale/",
25
27
  "cordis.patch.yml",
26
28
  "dsh.plugin.json",
27
29
  "screenshots.json",
28
30
  "README.md",
29
31
  "README.zh.md",
32
+ "README.de.md",
33
+ "README.es.md",
34
+ "README.fr.md",
35
+ "README.it.md",
36
+ "README.ja.md",
37
+ "README.ko.md",
38
+ "README.ru.md",
30
39
  "CHANGELOG.md",
31
40
  "CHANGELOG.zh.md",
41
+ "CHANGELOG.de.md",
42
+ "CHANGELOG.es.md",
43
+ "CHANGELOG.fr.md",
44
+ "CHANGELOG.it.md",
45
+ "CHANGELOG.ja.md",
46
+ "CHANGELOG.ko.md",
47
+ "CHANGELOG.ru.md",
48
+ "INSTALL.md",
49
+ "INSTALL.zh.md",
50
+ "INSTALL.de.md",
51
+ "INSTALL.es.md",
52
+ "INSTALL.fr.md",
53
+ "INSTALL.it.md",
54
+ "INSTALL.ja.md",
55
+ "INSTALL.ko.md",
56
+ "INSTALL.ru.md",
32
57
  "LICENSE",
33
58
  "assets/"
34
59
  ],
@@ -58,7 +83,7 @@
58
83
  },
59
84
  "engines": {
60
85
  "node": ">=20",
61
- "dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5"
86
+ "dsh": ">=0.1.1-rc.2 <0.3.0"
62
87
  },
63
88
  "license": "MIT",
64
89
  "keywords": [