Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Click it to see full diagnostics, including per-editor MCP configuration status
| **Set structured value** | Update a JSON, YAML, or TOML key with diff preview |
| **Search text** | Find pattern matches across workspace files (results in output channel) |
| **Create file** | Scaffold a new file and open it in the editor |
| **Append to file** | Append content to an existing file |
| **Read structured value** | Read a JSON/YAML/TOML key and copy to clipboard |
| **Merge patch (three-way)** | Apply a stale patch using three-way merge (v0.2.0+) |

Expand Down Expand Up @@ -147,7 +148,7 @@ The extension detects outdated CLI builds and warns with upgrade guidance. It re
Set `patchloom.path` in settings, or add the CLI to your `PATH`.

**CLI compatibility warning**
Run `Patchloom: Open Releases` to download the latest release. The extension requires 0.1.0 or newer; 0.2.0 is recommended.
Run `Patchloom: Open Releases` to download the latest release. The extension requires 0.1.0 or newer; 0.4.0 is recommended.

**MCP config not injected**
Run `Patchloom: Configure MCP` and select the target editor config.
Expand Down Expand Up @@ -182,7 +183,7 @@ File bugs and feature requests at [patchloom/patchloom-vscode/issues](https://gi
## Requirements

- VS Code 1.90 or newer (or compatible editors: Cursor, Windsurf, VSCodium)
- [Patchloom CLI](https://github.com/patchloom/patchloom) 0.1.0 or newer (0.2.0+ recommended for patch merge and strict transactions)
- [Patchloom CLI](https://github.com/patchloom/patchloom) 0.1.0 or newer (0.4.0+ recommended for latest features including append, AST ops, and --confirm)

## Contributing

Expand Down
1 change: 1 addition & 0 deletions src/commands/batchApply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { activeWorkspaceFolder } from "../workspace/readiness.js";
export const BATCH_TEMPLATE = [
"replace src/example.ts \"old text\" \"new text\"",
"doc.set package.json version \"2.0.0\"",
"file.append src/example.ts \"new appended line\"",
"tidy.fix src/example.ts",
""
].join("\n");
Expand Down
31 changes: 31 additions & 0 deletions src/commands/quickActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,28 @@ export async function runQuickAction(): Promise<void> {
await vscode.window.showInformationMessage(`Created ${relativePath.trim()}.`);
}
},
{
label: "Append to file",
description: "Append content to an existing file",
detail: "Builds `patchloom append <file> --content <text>`",
run: async () => {
const target = await pickWorkspaceFileTarget("Select a file to append to with Patchloom");
if (!target) {
return;
}

const content = await vscode.window.showInputBox({
prompt: "Content to append",
placeHolder: "new line of text",
validateInput: (value) => value.length > 0 ? undefined : "Content is required."
});
if (content === undefined) {
return;
}

await previewAndMaybeApply(binaryPath, target, buildAppendQuickAction(target.absolutePath, content));
}
},
{
label: "Read structured value",
description: "Read a value from JSON, YAML, or TOML",
Expand Down Expand Up @@ -861,6 +883,15 @@ export function buildCreateQuickAction(filePath: string): PlannedQuickAction {
};
}

export function buildAppendQuickAction(targetPath: string, content: string): PlannedQuickAction {
return {
title: `Append to ${path.basename(targetPath)}`,
targetPath,
targetArgIndices: [1],
args: ["append", targetPath, "--content", content]
};
}

export function buildDocGetQuickAction(targetPath: string, selector: string): PlannedQuickAction {
return {
title: `Get ${selector} from ${path.basename(targetPath)}`,
Expand Down
14 changes: 11 additions & 3 deletions test/unit/batchApply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import assert from "node:assert/strict";
import test from "node:test";
import { buildBatchTemplate, parseBatchOperationCount } from "../../src/commands/batchApply.js";

test("buildBatchTemplate returns line-oriented format with three operations", () => {
test("buildBatchTemplate returns line-oriented format with four operations", () => {
const template = buildBatchTemplate();
const lines = template.split("\n").filter((line) => line.trim().length > 0);

assert.equal(lines.length, 3);
assert.equal(lines.length, 4);
assert.ok(lines[0].startsWith("replace "), "first line should be a replace operation");
assert.ok(lines[1].startsWith("doc.set "), "second line should be a doc.set operation");
assert.ok(lines[2].startsWith("tidy.fix "), "third line should be a tidy.fix operation");
assert.ok(lines[2].startsWith("file.append "), "third line should be a file.append operation");
assert.ok(lines[3].startsWith("tidy.fix "), "fourth line should be a tidy.fix operation");
});

test("buildBatchTemplate ends with a newline", () => {
Expand Down Expand Up @@ -65,3 +66,10 @@ test("buildBatchTemplate tidy.fix line has a file path", () => {
assert.ok(tidyLine, "template should contain a tidy.fix line");
assert.match(tidyLine, /tidy\.fix \S+/, "tidy.fix should have a file path");
});

test("buildBatchTemplate file.append line has file and quoted content", () => {
const lines = buildBatchTemplate().split("\n");
const appendLine = lines.find((l) => l.startsWith("file.append "));
assert.ok(appendLine, "template should contain a file.append line");
assert.match(appendLine, /file\.append \S+ ".+"/, "file.append should have file and quoted content");
});
13 changes: 12 additions & 1 deletion test/unit/quickActions.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
buildAppendQuickAction,
buildCreateQuickAction,
buildDocAppendQuickAction,
buildDocDeleteQuickAction,
Expand Down Expand Up @@ -408,7 +409,7 @@ test("retargetQuickAction works with md commands", () => {
assert.equal(retargeted.args[1], "table-append");
});

// --- patch merge Quick Action (v0.2.0) ---
// --- patch merge Quick Action (v0.2.0+) ---

test("buildPatchMergeQuickAction builds a patch merge command", () => {
const action = buildPatchMergeQuickAction("/workspace/demo/changes.patch", false);
Expand All @@ -434,3 +435,13 @@ test("retargetQuickAction works with patch merge command", () => {
assert.equal(retargeted.args[0], "patch");
assert.equal(retargeted.args[1], "merge");
});

// --- append Quick Action (reflecting patchloom 0.4.0+) ---

test("buildAppendQuickAction builds an append command", () => {
const action = buildAppendQuickAction("/workspace/demo/log.txt", "new log entry");

assert.equal(action.title, "Append to log.txt");
assert.deepEqual(action.targetArgIndices, [1]);
assert.deepEqual(action.args, ["append", "/workspace/demo/log.txt", "--content", "new log entry"]);
});
Loading