DEV Community

Cover image for I Built a VS Code Extension to Paste Your Project into Free Chatbots and Apply the Diffs in One Click! 🔥
EffessDev
EffessDev

Posted on

I Built a VS Code Extension to Paste Your Project into Free Chatbots and Apply the Diffs in One Click! 🔥

Let me show exactly how it works with an example 🛻

The extension adds a new "ReptClip" tab in the VS Code bottom panel:

I am going to use this extension to redesign my website's landing page. First, we need to prepare the context.

Specify which files to add to the context in the "Files to include" input. You can use glob patterns like **/*.ts.

Since I want to update the landing page, let's add page.tsx. You will get suggestions as you type:

Click "Enter" or "Tab" to accept the suggestion:

If it matches any files, it will turn green. Otherwise, it will turn yellow.

Let's add AGENTS.md too:

This order is preserved in the context output. page.tsx will appear after AGENTS.md.

Now, click "Enter" again. The context will be copied directly to your clipboard. It looks something like this:

# Project structure

```
.github/workflows/deploy.yml
.gitignore
.vscode/settings.json
AGENTS.md
LICENSE.md
README.md
components.json
eslint.config.mjs
next.config.ts
package-lock.json
package.json
postcss.config.mjs
public/favicon.ico
public/favicon.svg
read/ble-basics-in-esp-idf.md
read/esp-idf-vscode-setup-guide.md
read/header-files-in-c.md
read/wired-embedded-protocols.md
reptclip-config.toml
src/app/footer.tsx
src/app/globals.css
src/app/layout.tsx
src/app/navbar.tsx
src/app/not-found.tsx
src/app/page.tsx
src/app/read/[id]/[chapter]/page.tsx
src/app/read/[id]/page.tsx
src/app/read/page.tsx
src/app/sitemap.ts
src/components/content-list.tsx
src/components/explore-projects-button.tsx
src/components/feature-card.tsx
src/components/layout/top-nav.tsx
src/components/page-header.tsx
src/components/page-section.tsx
src/components/post-component.tsx
src/components/post-list.tsx
src/components/project-list.tsx
src/components/scroll-header.tsx
src/components/site-footer.tsx
src/components/social-links.tsx
src/components/theme-provider.tsx
src/components/ui/action-link.tsx
src/components/ui/badge.tsx
src/components/ui/button.tsx
src/components/ui/card.tsx
src/components/ui/dropdown-menu.tsx
src/components/ui/mode-toggle.tsx
src/lib/courses.ts
src/lib/labels.ts
src/lib/markdown.tsx
src/lib/posts.ts
src/lib/types.ts
src/lib/utils.ts
tsconfig.json
```

## Output format

Provide all file modifications as Search/Replace blocks inside a single 4-backtick code block.

### Formatting Rules

1. **File Headers:** Every set of edits must begin with the exact relative file path on its own line (e.g., `path/to/file.ext`), even if there is only a single file in the project.
2. **Exact Matching:** The `SEARCH` block must contain an *exact, verbatim* copy of existing code, including identical whitespace, indentation, and surrounding context lines to uniquely identify the match.
3. **Uniqueness:** Provide sufficient surrounding unchanged lines in the `SEARCH` block to ensure it matches exactly **one** location in the target file.
4. **New Files & Deletions:**
   - To create a new file, use an empty `SEARCH` block.
   - To delete a file or a code block, use an empty `REPLACE` block.
5. **No Placeholders:** Do not use ellipses (`...`), comments like `# rest of code unchanged`, or omitted lines inside `SEARCH` or `REPLACE` blocks.
6. **Sequential Edits:** If editing multiple parts of the same file, list the `SEARCH/REPLACE` blocks in top-to-bottom order as they appear in the file.

### Example

````
src/models/user.py
<<<<<<< SEARCH
class User:
    def __init__(self, name):
        self.name = name

    def to_dict(self):
        return {"name": self.name}
=======
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def to_dict(self):
        return {
            "name": self.name,
            "email": self.email,
        }
>>>>>>> REPLACE

src/controllers/user_controller.py
<<<<<<< SEARCH
from src.models.user import User
=======
from src.models.user import User
from src.utils.validator import validate_email
>>>>>>> REPLACE
<<<<<<< SEARCH
def create_user(name):
    return User(name)
=======
def create_user(name, email):
    validate_email(email)
    return User(name, email)
>>>>>>> REPLACE

src/utils/validator.py
<<<<<<< SEARCH
=======
def validate_email(email):
    if "@" not in email:
        raise ValueError("Invalid email address")
>>>>>>> REPLACE
````

# src/app/page.tsx

```
File contents
```

# Prompt

<- Cursor stays here; you can quickly start typing
Enter fullscreen mode Exit fullscreen mode

Now, go to any free chatbot and paste the context using Ctrl + V:

Type your prompt, and send:

The chatbot will generate the diffs inside a code block:

Copy it to your clipboard, and go back to VS Code.

Click "Apply Diffs" in the ReptClip tab. The extension reads the diffs from the clipboard and applies them to the files:

Let's start the development server and check out the new landing page:

The landing page has been redesigned.

If you didn't like the changes, clicking "Revert" reverts all affected files to the previous state.

If at least one of the diffs is faulty, you'll get this:

It also lets you customize the context:

State is stored on a per-project basis. So, when you come back, your configuration will not be gone or mixed up with other projects.

If you want to use the extension purely for context, uncheck Diff format to remove the ## Output format section.

Here are a few edge cases explained:

  • Files to include **/*.py main.py: All Python files are included, but main.py comes last.
  • Files to include **/*.py src/**/*.py: All Python files are included, but the Python files in src come last.
  • Files to include **/*.py, files to exclude secret.py: All Python files are included, except secret.py.

So, that's basically what the extension does. If you liked it, a ⭐ on GitHub would really help A LOT! Please feel free to share it with friends or teammates who are looking for a $0 AI coding setup.

It's inspired by my CLI app ReptClip, so I named it ReptClip for VS Code.

Also, please tell me what you think in the comments. It really helps me improve the extension further. Big thanks to @piekwerk and @mansio for their suggestions during development!

Also, thanks to @unitbuilds for informing me about the limited-time free access to Qwen 3.8 Flash in Qoder. It wouldn't have been possible without it.

I purposefully kept the source code minimal and lightweight. Feel free to submit pull requests. I review them every day!

Total cost to build this: $0.

I created this extension specifically to help developers who can't afford paid coding agents, giving everyone access to a smooth AI workflow for free.

Thanks for reading 🙂🎉🎉

Top comments (13)

Collapse
 
mansio profile image
Mikhail •

The file-order fix (*/.py main.py) landed exactly where piekwerk and I both pointed on your last post — target file right above the prompt, not buried in the middle. Good to see it shipped.

One thing on Apply Diffs specifically: the uniqueness rule (#3) is enforced by asking the model to comply, not by checking it. Nothing stops a SEARCH block from matching an unintended spot if the surrounding context wasn't unique enough — same failure shape as the ASCII-alignment bug from your PR, models reason in tokens, not exact structural position, so "unique enough" is exactly the kind of thing they get subtly wrong. A match-count check before applying (refuse on 0 or 2+ matches) would turn rule #3 from a prompt-level hope into something guaranteed.

Also worth flagging given piekwerk's docs/me/ catch on your first post: the same glob pattern that pulls files into context for Apply Diffs also pulls them into the clipboard payload that leaves the machine. A cheap grep tripwire for secret-shaped strings before copy would catch the accidental case.

Collapse
 
effessdev profile image
EffessDev •

This is the exact reason why I love your comments! Thanks for taking the time to read it so deeply 🙂

At first, no matter how many times I retried applying the diffs, it kept failing. I tried allowing any type of line ending, but it still failed on longer blocks. Once I made the matching more flexible, like allowing slightly different spacing and minor variations, it finally started applying diffs correctly.

Good point on the uniqueness issue. Let me quickly check what happens. I think I have done something to handle it.

Collapse
 
mansio profile image
Mikhail •

Makes sense on the whitespace flexibility — long blocks on Windows vs POSIX line endings are a nightmare.

One quick edge case to check when you look at it: if the matcher is now looser (flexible spacing), does it stop and throw an error if it finds 2+ fuzzy matches, or does it just take the first one? Loosening the spacing usually makes non-unique matches more likely, so an explicit match-count guard is even more useful there.

Thread Thread
 
effessdev profile image
EffessDev •

Ah found it!

Currently, in case of a non-unique match, the diff isn't applied at all, and we get an error. It never silently picks the first.

You make me realize how important it is to document these things. I should be stricter about the documentation from now on...

Collapse
 
effessdev profile image
EffessDev • • Edited

Would you mind explaining a bit more about the docs/me catch? I read it a few times, but I still couldn't understand it exactly. 🤔

Collapse
 
piekwerk profile image
Piekwerk •

Thanks for the shoutout, glad the suggestions helped. Tried the new paste flow today and it's a real improvement, the context lands in one shot and the order control with main.py last is the detail I didn't know I needed.

One edge case worth a look before someone hits it: what happens on Apply Diffs when the working tree is already dirty. If a diff fails and I click Revert, I lose my own uncommitted changes too, because git checkout restores the last commit, not the state just before Apply. The fix that worked for me is treating Apply as a checkpoint, stash or commit first so Revert has a clean floor to return to, or have the extension snapshot the affected files before writing. Ran into exactly this with agent edits a while back: half my refactor was uncommitted, a diff overwrote part of it, and Revert then wiped the rest.

Also ran the Qwen trick from the screenshots, no issues on a Next.js repo. Nice build.

Collapse
 
effessdev profile image
EffessDev • • Edited

Yes the suggestions absolutely helped! Thanks for trying it out! Really means a lot! 🥲

Applying diffs throws an error and stops in case of any problems, even if it's a single block. If the working tree is dirty, the diff isn't even applied. So you don't have to revert it.

And, the extension doesn't rely on Git at all. It manually stores the contents of all the files in a diff before applying the diff. Clicking revert simply restores it. But since it's not stored permanently, you will lose it if you reopen VS Code. This was a deliberate decision to quickly get it up and running. I will be fixing it soon 🙂

The purpose of Revert is to simply revert the diff you just applied. So, I wouldn't rely on it that much. But it exists as a quick way to revert misclicks 👆 Also, it stores a hash of the last diff, so there is no problem with clicking Apply Diff again. It will show a notification that the diff was already applied!

Collapse
 
ivannovazzi profile image
Ivan Annovazzi •

You add page.tsx first, then AGENTS.md, but then say page.tsx appears after it. Is the output actually reversing the input order, or is that a typo? Reversed ordering would be confusing once someone has 10+ files in context

Collapse
 
effessdev profile image
EffessDev • • Edited

Ah, sorry 😅 Actually, even though I added AGENTS.md second, it was inserted before page.tsx. If you check the screenshot. That's what became confusing. It's the same order as the input order.

Collapse
 
unitbuilds profile image
UnitBuilds •

Great job honestly! How about next up, create a chrome plugin, so you can use a chatbot online (like gemini) and it pipes it through? If you already have the infrastructure to apply code sets from a pasted prompt, then this is the natural evolution of it, so it becomes a seamless integration and lets you spam any browser only AI feature to the fullest!

Specifically, ai studio... If you can manage a hook into that, then it'd be rock solid!

Collapse
 
effessdev profile image
EffessDev •

Haha. I really am thinking about it. All thanks to you! For reminding me about the limited-time free access! The only problem is that I have no idea how that actually works under the hood 😅 But I'll eventually figure it out 🔥

Collapse
 
unitbuilds profile image
UnitBuilds •

The concept is simple. If you look at my MCP-Lite, it works via the AOM. So a chrome extension that lets you select the AOM node to monitor, maybe with a capture button as a starter, so it takes that node, fetches it's content, then diffs it over the last applied. That way it doesnt repeat. You already have all the framework you'd need for the applying side, that revert system can double as a diffing system. So flow would be, you prompt gemini, it answers, you click capture. Capture takes the full chat and forwards it to the vs code extension. It diffs, then applies the remainder. If you want to upgrade it, you can do the diffing in the AOM as a marker system, so it only reads on from last marker. That way it can continue, without re-capturing the full chat history. But that's optimization, the initial implementation should be quite easy to implement!

Thread Thread
 
effessdev profile image
EffessDev •

I don't know whether you would believe but it already looks really complicated 😅 Let me read it a couple more times, and search what a few of those words mean... 🏃