Skip to content

Commit 5acd69b

Browse files
authored
feat: align extension with patchloom CLI 0.15.2 (#198)
Update recommended CLI version and install docs for npm/Scoop, add the md insert-after-section Quick Action and batch template examples for fuzzy replace and sibling markdown sections, and re-verify MCP/CLI contracts against 0.15.2 (selector, --new, --contain, batch, agent-rules). Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
1 parent 352e444 commit 5acd69b

8 files changed

Lines changed: 110 additions & 11 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ src/
4747
workspace/readiness.ts Workspace readiness: environment detection, folder selection
4848
test/
4949
unit/ Unit tests (node:test, dependency-injected, no VS Code API)
50-
batchApply.test.ts Batch template and operation count parsing (12 tests)
50+
batchApply.test.ts Batch template and operation count parsing (13 tests)
5151
binary.test.ts Binary discovery, managed install, compatibility, workspace env (59 tests)
5252
binaryDiscovery.test.ts Real executable discovery on PATH (13 tests)
5353
initializeProject.test.ts Status display, agents file classification, formatError (26 tests)
@@ -56,7 +56,7 @@ test/
5656
outputChannel.test.ts Output channel logging wrapper (10 tests)
5757
patchloomCli.test.ts Patchloom CLI integration with real binary + managed install e2e MCP (34 tests incl. e2e)
5858
propertyBased.test.ts Property-based tests with fast-check (13 tests)
59-
quickActions.test.ts Quick action command building, path containment, patch merge (50 tests)
59+
quickActions.test.ts Quick action command building, path containment, patch merge (51 tests)
6060
verifyMcp.test.ts MCP server verify and JSON-RPC response parsing (15 tests)
6161
downloadIntegration.test.ts HTTP download, redirect, streaming SHA-256 (9 tests)
6262
suite/

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ Or search for **Patchloom** in the Extensions view (`Ctrl+Shift+X` / `Cmd+Shift+
2828
1. Install the [Patchloom CLI](https://github.com/patchloom/patchloom) (or run **Patchloom: Install Patchloom** from the command palette)
2929
```sh
3030
brew install patchloom/tap/patchloom # macOS / Linux (Homebrew)
31+
npm install -g patchloom # npm (Node.js)
3132
curl -LsSf https://github.com/patchloom/patchloom/releases/latest/download/patchloom-installer.sh | sh # shell script
3233
cargo install patchloom # from source
34+
scoop install patchloom # Windows (Scoop; requires the official bucket)
3335
```
3436
2. Open a project and run **Patchloom: Setup Workspace**
3537

@@ -87,9 +89,10 @@ Click it to see full diagnostics, including per-editor MCP configuration status
8789
| **Append to file** | Append content to an existing file |
8890
| **Prepend to file** | Prepend content to the start of an existing file (CLI 0.9+) |
8991
| **Read structured value** | Read a JSON/YAML/TOML key and copy to clipboard |
92+
| **Insert after section** | Insert a sibling markdown section after a full section body (CLI 0.14+) |
9093
| **Merge patch (three-way)** | Apply a stale patch using three-way merge (v0.2.0+) |
9194

92-
Workspace Quick Actions and Batch Apply pass `--contain` so CLI paths stay inside the workspace root (CLI 0.10+). Patch merge skips containment when the patch file may live outside the workspace.
95+
Workspace Quick Actions and Batch Apply pass `--contain` so CLI paths stay inside the workspace root (CLI 0.10+). Containment is relative to the effective working directory (the workspace folder). Patch merge skips containment when the patch file may live outside the workspace.
9396

9497
### Batch operations
9598

@@ -151,7 +154,7 @@ The extension detects outdated CLI builds and warns with upgrade guidance. It re
151154
Set `patchloom.path` in settings, or add the CLI to your `PATH`.
152155

153156
**CLI compatibility warning**
154-
Run `Patchloom: Open Releases` to download the latest release. The extension requires 0.3.0 or newer; 0.10.0 is recommended.
157+
Run `Patchloom: Open Releases` to download the latest release. The extension requires 0.3.0 or newer; 0.15.2 is recommended.
155158

156159
**MCP config not injected**
157160
Run `Patchloom: Configure MCP` and select the target editor config.
@@ -186,7 +189,7 @@ File bugs and feature requests at [patchloom/patchloom-vscode/issues](https://gi
186189
## Requirements
187190

188191
- VS Code 1.90 or newer (or compatible editors: Cursor, Windsurf, VSCodium)
189-
- [Patchloom CLI](https://github.com/patchloom/patchloom) 0.3.0 or newer (0.10.0+ recommended for 54 MCP tools, correct preview exit codes, optional `--contain` path guarding, schema-driven MCP descriptions, and agent reliability fixes)
192+
- [Patchloom CLI](https://github.com/patchloom/patchloom) 0.3.0 or newer (0.15.2+ recommended for 56 MCP tools, JSON `applied` honesty, doc query envelopes, `md insert-after-section`, optional `--contain` path guarding, fuzzy replace floors, and agent reliability fixes)
190193

191194
## Contributing
192195

src/commands/batchApply.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import { activeWorkspaceFolder } from "../workspace/readiness.js";
77

88
export const BATCH_TEMPLATE = [
99
"replace src/example.ts \"old text\" \"new text\"",
10+
"replace src/example.ts \"typo_here\" \"fixed\" --fuzzy --min-fuzzy-score 0.80",
1011
"doc.set package.json version \"2.0.0\"",
1112
"file.append src/example.ts \"new appended line\"",
13+
"md.insert_after_section README.md \"## Config\" \"## FAQ\"",
1214
"tidy.fix src/example.ts",
1315
""
1416
].join("\n");

src/commands/quickActions.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,44 @@ export async function runQuickAction(): Promise<void> {
584584
await previewAndMaybeApply(binaryPath, target, buildMdInsertAfterHeadingQuickAction(target.absolutePath, heading, content));
585585
}
586586
},
587+
{
588+
label: "Insert after section",
589+
description: "Insert a sibling section after a full markdown section body (CLI 0.14+)",
590+
detail: "Builds `patchloom md insert-after-section <file> --heading <h> --content <text>`",
591+
run: async () => {
592+
const target = await pickWorkspaceFileTarget("Select a markdown file for Patchloom insert-after-section");
593+
if (!target) {
594+
return;
595+
}
596+
597+
if (!isMarkdownPath(target.absolutePath)) {
598+
await vscode.window.showWarningMessage(
599+
`${target.relativePath} is not a markdown file.`
600+
);
601+
return;
602+
}
603+
604+
const heading = await vscode.window.showInputBox({
605+
prompt: "Heading whose full section ends before the insertion",
606+
placeHolder: "## Config",
607+
validateInput: (value) => value.length > 0 ? undefined : "Heading is required."
608+
});
609+
if (heading === undefined) {
610+
return;
611+
}
612+
613+
const content = await vscode.window.showInputBox({
614+
prompt: "Sibling content to insert after the section body",
615+
placeHolder: "## FAQ\n\nCommon questions.",
616+
validateInput: (value) => value.length > 0 ? undefined : "Content is required."
617+
});
618+
if (content === undefined) {
619+
return;
620+
}
621+
622+
await previewAndMaybeApply(binaryPath, target, buildMdInsertAfterSectionQuickAction(target.absolutePath, heading, content));
623+
}
624+
},
587625
{
588626
label: "Insert before heading",
589627
description: "Insert content before a markdown heading",
@@ -1017,6 +1055,15 @@ export function buildMdInsertAfterHeadingQuickAction(targetPath: string, heading
10171055
};
10181056
}
10191057

1058+
export function buildMdInsertAfterSectionQuickAction(targetPath: string, heading: string, content: string): PlannedQuickAction {
1059+
return {
1060+
title: `Insert after section "${heading}" in ${path.basename(targetPath)}`,
1061+
targetPath,
1062+
targetArgIndices: [2],
1063+
args: ["md", "insert-after-section", targetPath, "--heading", heading, "--content", content]
1064+
};
1065+
}
1066+
10201067
export function buildMdInsertBeforeHeadingQuickAction(targetPath: string, heading: string, content: string): PlannedQuickAction {
10211068
return {
10221069
title: `Insert before "${heading}" in ${path.basename(targetPath)}`,

test/unit/batchApply.test.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ import {
66
parseBatchOperationCount
77
} from "../../src/commands/batchApply.js";
88

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

13-
assert.equal(lines.length, 4);
13+
assert.equal(lines.length, 6);
1414
assert.ok(lines[0].startsWith("replace "), "first line should be a replace operation");
15-
assert.ok(lines[1].startsWith("doc.set "), "second line should be a doc.set operation");
16-
assert.ok(lines[2].startsWith("file.append "), "third line should be a file.append operation");
17-
assert.ok(lines[3].startsWith("tidy.fix "), "fourth line should be a tidy.fix operation");
15+
assert.ok(lines[1].startsWith("replace ") && lines[1].includes("--fuzzy"), "second line should be fuzzy replace");
16+
assert.ok(lines[2].startsWith("doc.set "), "third line should be a doc.set operation");
17+
assert.ok(lines[3].startsWith("file.append "), "fourth line should be a file.append operation");
18+
assert.ok(lines[4].startsWith("md.insert_after_section "), "fifth line should be md.insert_after_section");
19+
assert.ok(lines[5].startsWith("tidy.fix "), "sixth line should be a tidy.fix operation");
1820
});
1921

2022
test("buildBatchTemplate ends with a newline", () => {
@@ -78,6 +80,20 @@ test("buildBatchTemplate file.append line has file and quoted content", () => {
7880
assert.match(appendLine, /file\.append \S+ ".+"/, "file.append should have file and quoted content");
7981
});
8082

83+
test("buildBatchTemplate includes fuzzy replace and md.insert_after_section examples", () => {
84+
const lines = buildBatchTemplate().split("\n");
85+
const fuzzyLine = lines.find((l) => l.includes("--fuzzy"));
86+
const sectionLine = lines.find((l) => l.startsWith("md.insert_after_section "));
87+
assert.ok(fuzzyLine, "template should contain a fuzzy replace example");
88+
assert.match(fuzzyLine, /--min-fuzzy-score/, "fuzzy replace should include min-fuzzy-score");
89+
assert.ok(sectionLine, "template should contain md.insert_after_section");
90+
assert.match(
91+
sectionLine,
92+
/md\.insert_after_section \S+ ".+" ".+"/,
93+
"md.insert_after_section should use path + heading + content positionals"
94+
);
95+
});
96+
8197
test("buildBatchApplyArgs prefixes global --contain before batch --apply", () => {
8298
assert.deepEqual(buildBatchApplyArgs(), ["--contain", "batch", "--apply"]);
8399
});

test/unit/patchloomCli.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ describe("managed install end-to-end MCP", { timeout: 120_000 }, async () => {
788788
}) + "\n");
789789

790790
// Call doc_set to change port from 3000 to 8080 (relative path).
791-
// MCP param is `selector` (CLI arg name); not `key`.
791+
// MCP param is `selector` (CLI selector path); verified against patchloom 0.15.x.
792792
child.stdin!.write(JSON.stringify({
793793
jsonrpc: "2.0", id: 3, method: "tools/call",
794794
params: {

test/unit/quickActions.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
buildDocPrependQuickAction,
1414
buildDocSetQuickAction,
1515
buildMdInsertAfterHeadingQuickAction,
16+
buildMdInsertAfterSectionQuickAction,
1617
buildMdInsertBeforeHeadingQuickAction,
1718
buildMdReplaceSectionQuickAction,
1819
buildMdTableAppendQuickAction,
@@ -422,6 +423,22 @@ test("buildMdInsertAfterHeadingQuickAction builds a md insert-after-heading comm
422423
]);
423424
});
424425

426+
test("buildMdInsertAfterSectionQuickAction builds a md insert-after-section command", () => {
427+
const action = buildMdInsertAfterSectionQuickAction(
428+
"/workspace/demo/README.md",
429+
"## Config",
430+
"## FAQ\n\nCommon questions."
431+
);
432+
433+
assert.equal(action.title, 'Insert after section "## Config" in README.md');
434+
assert.deepEqual(action.targetArgIndices, [2]);
435+
assert.deepEqual(action.args, [
436+
"md", "insert-after-section", "/workspace/demo/README.md",
437+
"--heading", "## Config",
438+
"--content", "## FAQ\n\nCommon questions."
439+
]);
440+
});
441+
425442
test("buildMdInsertBeforeHeadingQuickAction builds a md insert-before-heading command", () => {
426443
const action = buildMdInsertBeforeHeadingQuickAction("/workspace/demo/CHANGELOG.md", "## v1.0.0", "## v1.1.0\n\n- New feature");
427444

walkthrough/install.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ and installation.
1414
brew install patchloom/tap/patchloom
1515
```
1616

17+
## npm
18+
19+
```bash
20+
npm install -g patchloom
21+
# or one-shot: npx patchloom --version
22+
```
23+
24+
## Scoop (Windows)
25+
26+
```bash
27+
scoop bucket add patchloom https://github.com/patchloom/scoop-bucket
28+
scoop install patchloom
29+
```
30+
1731
## Cargo
1832

1933
```bash

0 commit comments

Comments
 (0)