Skip to content

Commit a881610

Browse files
authored
Merge pull request #2 from mnismt/feat/opx-v1
2 parents 7fa06f1 + 85942d0 commit a881610

File tree

8 files changed

+1189
-647
lines changed

8 files changed

+1189
-647
lines changed

docs/opx.md

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
# OPX v1: Overwrite Patch XML — Product Requirements Document (PRD)
2+
3+
## Summary
4+
5+
OPX is an original XML-based change protocol for Overwrite that LLMs produce and the extension applies to the workspace.
6+
7+
Operations supported:
8+
9+
- Create files
10+
- Patch specific regions of files (search-and-replace)
11+
- Replace entire files
12+
- Remove files
13+
- Move/rename files
14+
15+
## Goals
16+
17+
- Replace the existing immediately with a new, original protocol.
18+
- Keep the format minimal and highly reliable for LLMs to generate.
19+
- Map OPX operations directly onto our existing apply engine with no behavior changes beyond parsing.
20+
21+
## Non‑Goals
22+
23+
- No phased rollout or legacy compatibility.
24+
- No telemetry changes.
25+
- No new runtime dependencies.
26+
27+
---
28+
29+
## Protocol Specification
30+
31+
### Top-level
32+
33+
- One or more `<edit>` elements may appear at the top level.
34+
- Optionally, edits may be wrapped in a single `<opx>...</opx>` container. The container is ignored by the parser.
35+
36+
### Edit element
37+
38+
Element: `<edit>`
39+
- Required attributes:
40+
- `file`: path to the target file (prefer workspace-relative)
41+
- `op`: operation type (see below)
42+
- Optional attributes:
43+
- `root`: VS Code workspace root folder name for multi-root workspaces
44+
45+
Supported `op` values and semantics:
46+
- `new` — create a new file. Requires a `<put>` child.
47+
- `patch` — search-and-replace a region. Requires both `<find>` and `<put>` children.
48+
- `replace` — replace entire file contents. Requires a `<put>` child.
49+
- `remove` — delete file. No children required.
50+
- `move` — rename/move file. Requires a `<to file="..."/>` child.
51+
52+
### Children of `<edit>`
53+
54+
- `<why>` (optional): A brief sentence describing the change for this edit.
55+
- `<find>` (for `op="patch"`):
56+
- Optional attribute: `occurrence="first|last|N"` to disambiguate repeated matches.
57+
- Contains a literal block delimited by `<<<` (start) and `>>>` (end) on their own lines.
58+
- `<put>` (for `op="new"`, `op="patch"`, `op="replace"`):
59+
- Contains a literal block delimited by `<<<` and `>>>` on their own lines.
60+
- `<to file="..."/>` (for `op="move"`):
61+
- Self-closing element with a required `file` attribute specifying destination path.
62+
63+
### Literal content blocks
64+
65+
- Inside `<find>` and `<put>`, the payload must be wrapped between lines containing only `<<<` and `>>>`.
66+
- The parser takes all text strictly between those markers. Surrounding whitespace/newlines around markers are trimmed.
67+
- Parser auto-heal: to mitigate Markdown/chat copy issues, if a line inside `<find>`/`<put>` contains only `<` or `<<`, it is treated as `<<<`; if it contains only `>` or `>>`, it is treated as `>>>`. This repair happens before extraction and affects only full-line markers.
68+
69+
### Path rules
70+
71+
- Prefer workspace-relative paths (e.g., `src/lib/logger.ts`).
72+
- `file://` URIs and absolute paths are tolerated but not required.
73+
- Do not reference paths outside the workspace.
74+
75+
### Examples
76+
77+
New file
78+
```xml
79+
<edit file="src/utils/strings.ts" op="new">
80+
<why>Create utility module</why>
81+
<put>
82+
<<<
83+
export function titleCase(s: string): string {
84+
return s.split(/\s+/).map(w => w ? w[0]!.toUpperCase() + w.slice(1) : w).join(' ')
85+
}
86+
>>>
87+
</put>
88+
</edit>
89+
```
90+
91+
Patch region
92+
```xml
93+
<edit file="src/api/users.ts" op="patch">
94+
<why>Add timeout and error logging</why>
95+
<find occurrence="first">
96+
<<<
97+
export async function fetchUser(id: string) {
98+
const res = await fetch(`/api/users/${id}`);
99+
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
100+
return res.json();
101+
}
102+
>>>
103+
</find>
104+
<put>
105+
<<<
106+
async function withTimeout<T>(p: Promise<T>, ms = 10000): Promise<T> {
107+
const t = new Promise<never>((_, r) => setTimeout(() => r(new Error('Request timed out')), ms));
108+
return Promise.race([p, t]);
109+
}
110+
111+
export async function fetchUser(id: string) {
112+
try {
113+
const res = await withTimeout(fetch(`/api/users/${id}`), 10000);
114+
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
115+
return res.json();
116+
} catch (err) {
117+
console.error('[api] fetchUser error', err);
118+
throw err;
119+
}
120+
}
121+
>>>
122+
</put>
123+
</edit>
124+
```
125+
126+
Replace entire file
127+
```xml
128+
<edit file="src/config/index.ts" op="replace">
129+
<put>
130+
<<<
131+
export interface AppConfig {
132+
apiBaseUrl: string;
133+
enableTelemetry: boolean;
134+
maxConcurrentJobs: number;
135+
}
136+
137+
export const config: AppConfig = {
138+
apiBaseUrl: process.env.API_BASE_URL || 'http://localhost:3000',
139+
enableTelemetry: process.env.TELEMETRY === '1',
140+
maxConcurrentJobs: Number(process.env.MAX_JOBS || 4),
141+
};
142+
>>>
143+
</put>
144+
</edit>
145+
```
146+
147+
Remove file
148+
```xml
149+
<edit file="tests/legacy/user-auth.spec.ts" op="remove" />
150+
```
151+
152+
Move / rename file
153+
```xml
154+
<edit file="src/lib/flags.ts" op="move">
155+
<to file="src/lib/feature-flags.ts" />
156+
</edit>
157+
```
158+
159+
---
160+
161+
## Architecture Changes (Immediate)
162+
163+
All changes below are applied at once to switch the system fully to OPX v1.
164+
165+
### Parser — OPX only
166+
167+
File: [src/utils/xml-parser.ts](file:///Users/minhthanh/Work/Side/overwrite/src/utils/xml-parser.ts)
168+
169+
- Remove legacy parsing and markers; accept only OPX tags.
170+
- New parse path:
171+
- Accept either top-level `<edit>` elements or a single `<opx>` wrapper containing `<edit>` elements.
172+
- For each `<edit>`:
173+
- Read attributes `file`, `op`, optional `root`.
174+
- `op -> action` mapping:
175+
- `new``create`
176+
- `patch``modify`
177+
- `replace``rewrite`
178+
- `remove``delete`
179+
- `move``rename`
180+
- For `patch`: extract `<find>` (with optional `occurrence`) and `<put>` blocks.
181+
- For `new|replace`: extract `<put>` block.
182+
- For `move`: read `<to file="..."/>` into `newPath`.
183+
- Literal extraction uses `<<<`/`>>>` only.
184+
- Return the same `ParseResult` and `FileAction[]` types used by the apply engine.
185+
186+
### Preprocessor (Apply tab)
187+
188+
File: [src/webview-ui/src/components/apply-tab/preprocess.ts](file:///Users/minhthanh/Work/Side/overwrite/src/webview-ui/src/components/apply-tab/preprocess.ts)
189+
190+
- Extend normalization to `<edit ...>` and `<to .../>` tags (lowercase keys, normalize quotes).
191+
- Remove legacy tag handling (`<file>`, `<new/>`, etc.).
192+
- Lint rules:
193+
- `<edit>` must include `file` and `op`.
194+
- `op="patch"` requires both `<find>` and `<put>`.
195+
- `op="move"` requires `<to file="..."/>`.
196+
197+
### Apply Pipeline (no changes)
198+
199+
File: [src/providers/file-explorer/file-action-handler.ts](file:///Users/minhthanh/Work/Side/overwrite/src/providers/file-explorer/file-action-handler.ts)
200+
201+
- No behavior changes required. The parser still emits `action` in `{create|modify|rewrite|delete|rename}` and the same `changes`/`newPath` fields.
202+
203+
### Prompt Text
204+
205+
File: [src/prompts/xml-instruction.ts](file:///Users/minhthanh/Work/Side/overwrite/src/prompts/xml-instruction.ts)
206+
207+
- Replace the entire instruction constant with OPX-only wording and examples.
208+
- Remove references to legacy format.
209+
210+
---
211+
212+
## DX
213+
214+
- OPX is simpler to read and write:
215+
- One element per edit (`<edit>`), with an explicit `op` attribute.
216+
- Clear, distinct delimiters `<<<` / `>>>` for literal content.
217+
- Optional `<why>` enables short, per-edit intent
218+
219+
---
220+
221+
## Testing Plan (Immediate)
222+
223+
Parser unit tests (targeted):
224+
- Valid cases: `new`, `patch`, `replace`, `remove`, `move`.
225+
- Occurrence handling: `first`, `last`, numeric `N`, ambiguous without `occurrence` (error).
226+
- Marker parsing: mixed whitespace around `<<<`/`>>>`.
227+
- Error cases: missing attributes, missing required children, multiple `<put>`/`<find>`.
228+
229+
Webview tests (Apply tab):
230+
- Lint/normalization for `<edit>`/`<to>` tags.
231+
- Preview renders correct summary and per-row apply functions.
232+
233+
Manual smoke:
234+
- Dev playground at http://localhost:5173/.
235+
- Paste each example from this document and verify preview/apply success.
236+
237+
---
238+
239+
## Risks & Mitigations
240+
241+
- LLM adherence to the new format:
242+
- Keep instruction text concise and provide 3–4 OPX examples.
243+
- Use stable tags/attributes and simple markers.
244+
- Content delimiter collision:
245+
- `<<<`/`>>>` are uncommon; if conflicts appear, OPX v1.1 can add an optional custom `marker` attribute.
246+
- Markdown copy truncation of markers:
247+
- The parser auto-heals full-line `<`/`<<`/`>`/`>>` into `<<<`/`>>>` inside `<find>`/`<put>` blocks.
248+
- Guidance: ask models to emit OPX inside a single fenced block (e.g., ```xml … ```), and copy from within the fence.
249+
250+
---
251+
252+
## Acceptance Criteria
253+
254+
- Legacy input is no longer accepted; OPX-only responses are parsed and applied.
255+
- All five operations (`new`, `patch`, `replace`, `remove`, `move`) work end-to-end via Apply tab.
256+
- Instruction text is replaced with OPX-only content.
257+
- Unit tests for OPX pass; Apply tab previews and applies OPX responses successfully.
258+
259+
---
260+
261+
## Implementation Checklist
262+
263+
1) Parser
264+
- [x] Remove legacy parsing path and `===` marker support.
265+
- [x] Implement OPX-only parsing with `<<<`/`>>>` markers.
266+
- [x] Map `op` → internal actions.
267+
268+
2) Preprocessor
269+
- [x] Normalize/lint `<edit>` and `<to>`.
270+
- [x] Remove legacy tag handling and lints.
271+
272+
3) Prompt text
273+
- [x] Replace instruction in [src/prompts/xml-instruction.ts](file:///Users/minhthanh/Work/Side/overwrite/src/prompts/xml-instruction.ts) with OPX.
274+
275+
4) Tests
276+
- [x] Add OPX parser tests.
277+
- [x] Update/Add webview tests for OPX samples.
278+
279+
5) Manual smoke
280+
- [ ] Validate examples from this PRD in the dev playground.
281+
282+
---
283+
284+
## Release Checklist (OPX v1)
285+
286+
- [x] Replace prompt instructions with OPX-only (src/prompts/xml-instruction.ts)
287+
- [x] Parser OPX-only (src/utils/xml-parser.ts) and preprocessor normalization (src/webview-ui/.../preprocess.ts)
288+
- [x] Backend tests green: `pnpm test`
289+
- [x] Webview tests green: `pnpm -C src/webview-ui test --run`
290+
- [ ] Optional manual smoke in dev playground (http://localhost:5173/)
291+
- [ ] Bump version in package.json if releasing
292+
- [ ] Package: `pnpm package` (or `pnpm vscode:package`)
293+

0 commit comments

Comments
 (0)