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 Edited 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

```
.gitattributes
.gitignore
AGENTS.md
CONTRIBUTING.md
LICENSE
README.md
pipxclip-config.toml
pyproject.toml
src/pipxclip/__init__.py
src/pipxclip/cli.py
src/pipxclip/config.py
tests/test_cli.py
tests/test_config.py
```

# 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
````

# AGENTS.md

```
Do not make mistakes.
```

# Prompt

<- Cursor lands 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 PipxClip, which was formerly 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 (31)

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... 🏃

Thread Thread
 
effessdev profile image
EffessDev •

I can't believe I never knew this existed 🤦🤦🤦

I once used Google AI Studio to get an API key to use their embedding model. I always went straight to the dashboard 😢. For some reason they have aistudio.google.com/api-keys instead of aistudio.google.com/dashboard/api-keys

Thread Thread
 
unitbuilds profile image
UnitBuilds •

😂 Really? Before the agentic IDEs came out, when it was chatbot only, this is where I went. It was the only place for a long time where the models could actually fill their 1m token context! Even now, everywhere cuts context at 200k.

Thread Thread
 
effessdev profile image
EffessDev •

Oh! I thought gemini.google.com would actually allow us to use the full 1M context. I should start using AI Studio from now on...

Thread Thread
 
unitbuilds profile image
UnitBuilds •

Nope, mostly because 200k context is around 1 server-grade gpu worth. When you go over it, things get slower... And for them, way more expensive... Also added bonus, ai studio is the 'enterprise' end, so you can opt out of data collection for training, so it's actually safer than gemini directly.

Thread Thread
 
unitbuilds profile image
UnitBuilds •

btw, if you want to delve into qoder still after end of the month (when the promo ends), you could always create a qwencloud/alibaba account, you get around 1m tokens per model (hundreds of models, including photo, video and audio gen). Qoder is a bit limited on what they allow, but 14 models is still 14m tokens... If you go the extra mile, you can set up a discovery and failover procedure for a local hosted node.js and have it act as a 'model router' that way when 1 model exhausts, the next continues. Quite useful, especially when you start adding to the list, like cloudflare's workers ai, openrouter, gemini APIs, etc. what I ended up doing is setting up multiple email accounts, for cloudflare and gemini, you can spam it pretty well across accounts, but openrouter maxes requests per device too... Just more tiny ways to make the most out of what's free.

Thread Thread
 
effessdev profile image
EffessDev •

So there is no reason to use Gemini instead of Google AI Studio.

Thread Thread
 
unitbuilds profile image
UnitBuilds •

1 caveat... Dunno if it's still the case, but AI Studio instances clear after a while (I think a week). But generally speaking, yes, there's 0 practical reason to use gemini over ai studio if you're a developer.

Fun fact, it has some cool tricks too, that game that @jess made, the crossword puzzle just before I made mine, was made using the gemini build system in ai studio. It can write and run simple apps in-browser for quick testing. So it has extra benefits beyond standard gemini too.

Thread Thread
 
effessdev profile image
EffessDev • • Edited

Oh, like Claude.ai? That's cool! I was checking out those free models on OpenRouter. There are a lot of options here:

The best part is that I never have to leave VS Code! But it's not working properly as of now. I have to figure out the best model from that list.

Thread Thread
 
unitbuilds profile image
UnitBuilds •

Just a note, openrouter works on 10k requests free for non-paying customers, even if a model says free, so just go for Nemotron 3 Ultra and reserve it for hard-thinking work. That way you can make the most of it!

Thread Thread
 
unitbuilds profile image
UnitBuilds •

My top 2 when I started with the IDE development was OpenRouter for nemotron and Cloudflare workers ai, for Kimi K2.7 Code. The nice part of cloudflare is that if you have 20 email accounts, you create 20 api keys and they're not system locked, so you can use them all to the fullest! If you setup a failover system to switch to the next account and continue, you can get alot of work done, cuz it refreshes daily!

Thread Thread
 
effessdev profile image
EffessDev •

Oooh, I hit the api limits:

{"message":"Rate limit exceeded: free-models-per-day. Add 10 credits to unlock 1000 free model requests per day"...
Enter fullscreen mode Exit fullscreen mode

So, adding 10 credits would drastically increase the limits? If Cloudflare isn't system-locked, I should try that too. But let's save that for later because I have to make the most of Qoder as of now 😅

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
 
piekwerk profile image
Piekwerk •

Good to know the dirty-tree case already stops with an error, that was the main worry. And I had Revert backwards, I assumed git checkout under the hood. A snapshot of the affected files is the cleaner design, it sidesteps the uncommitted-work case I described.

On the restart loss, one option: the extension's workspace storage. context.storageUri is a per-workspace folder that survives restarts, so writing the pre-apply snapshot there, keyed by file path plus diff hash, keeps Revert working after a reopen without taking on a git dependency.

One small edge on the hash guard: it stores only the last diff's hash. Apply A, apply B, then paste A again, and the duplicate slips past because the stored hash is now B. A short list of applied hashes per session would close that.

Thread Thread
 
effessdev profile image
EffessDev •

Oh! Let me note that. That seems like the correct way to get it working.

And yeah that concern about the hash guard I NEVER thought about! I have to add a stack or something for both the hash & revert. I have to test whether all of that will be too heavy.

Collapse
 
effessdev profile image
EffessDev •

Would you mind expanding a bit on the Qwen trick? It has been a while I still don't get it 😅

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...

Thread Thread
 
effessdev profile image
EffessDev •

I've browsed through the chat history and added everything I could find in the README:

github.com/effessdev/reptclip-vsco...

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
 
mansio profile image
Mikhail •

Sure! It's about the same glob pattern doing double duty.

When you type a pattern like */.md to build context, it matches every .md file in the repo — including ones that were never meant to leave your machine, like personal notes in a docs/me/ folder. Nothing in the pattern distinguishes "code I want to share" from "private notes that happen to match."

Piekwerk hit this on your first post: his generated markdown included docs/me/prompt.md and docs/me/publishing.md — personal files that got swept in because they matched the pattern, then went straight into the clipboard and out to a hosted chatbot.

The same mechanism applies to Apply Diffs — whatever glob builds your context is the same one that decides what leaves your machine. A quick client-side check before copying (flagging common secret shapes, or sensitive paths like /me/, .env, etc.) turns that blind sweep into a safe, reliable default.

Thread Thread
 
effessdev profile image
EffessDev •

That's correct! I and @unitbuilds have discussed it before:

dev.to/unitbuilds/do-we-need-draft...

That's something that I absolutely should consider adding. Currently, it assumes anything that's sensitive is added to .gitignore. But if it's not, the extension gracefully adds it to the context.

But if you have ignored everything sensitive using .gitignore (which in most cases we do), you have nothing to worry about. But otherwise, even if temporarily, that will be added to the context 😢

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
 
effessdev profile image
EffessDev • • Edited

Thanks everyone, for the likes and the comments! 😊