Directed Creation: What a 3B Model Can Actually Do to My Files A developer built a local file-editing tool called vibe.py around the 3-billion-parameter Qwen2.5-Coder model running via Ollama. The tool reads a single file, asks the model to rewrite it, shows a diff, and requires explicit user approval before writing. The developer emphasizes 'directed creation,' where the AI performs labor but the human retains decision-making, avoiding agentic autonomy. This started as an abstract way I think about AI. I wanted to manipulate files inside my own local system, on my own PC, with no internet and no API key. And look, agents far more advanced and more useful than what I built here already exist. I know that. But this is free, it's easy to make through vibe coding, it runs on my own machine, and it does exactly the narrow thing I want. That's the whole trade. So I built a small tool around a 3-billion-parameter model running locally through Ollama and wired it to touch my files directly. This is the honest report: what it does well, exactly where it falls on its face, and the one rule that keeps the whole thing safe to use. The model is qwen2.5-coder:3b . For scale, the models you read about have hundreds of billions of parameters. This one is small enough to run on a laptop and answer in a second or two. That smallness is the whole point, and the whole constraint. The script is even called vibe.py , because that's what it is: vibe coded, on purpose, into something specific. Here's the tool's core. It's deliberately not agentic: python def cmd edit args : with open args.file, "r" as f: original = f.read prompt = f"""Here is the full content of {args.file}: {original} Instruction: {args.instruction} Output ONLY the complete new file content. No explanation, no markdown fences.""" new content = strip fences ask model prompt if not show diff original, new content, args.file : return if input "\nApply this change? y/n : " .strip .lower == "y": with open args.file, "w" as f: f.write new content print "Applied." else: print "Discarded — file untouched." Read what it does, and especially what it refuses to do. It reads one file. It asks the model for the entire file back, rewritten. It shows me a diff. And it does not write a single byte until I type y . No tool-calling. No agent deciding to modify six files on its own. One file in, one diff out, one human approval. I made that choice on purpose, and the rest of this post is why it was the right one. There's a hidden cost in "give me the entire file back": the model has to reproduce every line it isn't changing, perfectly, or it quietly corrupts the file. For a small model that reproduction is exactly where the risk lives. Hold that thought, we walk straight into it. vibe.py didn't come from nowhere. It's one script in a personal system I've been building across two WSL2 machines, wired together in VS Code with the extensions, the connections between the machines, private GitHub hubs for organization, and a stack of small scripts that each do one precise thing. vibe.py is one of those scripts. This demo is really about it working through the others, to set up a Dev.to target from a selected file inside the system. I won't lay the whole thing out here, that's its own post, and I'm building a full demo of how the dual-machine setup fits together. But the one idea worth taking is the reason vibe.py is shaped the way it is. I call it directed creation . Directed creation means this: I let AI do the work , not the thinking or the creating. Yes, you can vibe code. But you have to be very focused on what you're vibe coding, testing, and iterating toward. The model doesn't decide what gets built or whether it's right. It does the labor once I've decided the shape. The moment you let it do the thinking too, you're not directing anymore, you're just hoping. vibe.py is that idea compressed into one script. The model does the rote part: read the file, propose the whole rewrite. It never reaches the part that matters: deciding the change is correct and writing it to disk. That's mine. The y/n prompt is the exact line where the labor ends and the direction begins. So when you watch the tests below, you're not watching a tool being clever. You're watching that line get held. Before trusting a local model with anything real, I throw it a warmup. The tool has a read-only explain command that runs the same model path as edit without touching disk. If it hallucinates on the warmup, I stop and reach for a real model instead. It passed clean. Read the actual file, summarized the actual commands, invented nothing. Then the simplest possible edit: add a docstring to a two-line function. The diff was perfectly scoped. Kept the signature, kept the body, dropped the docstring in between and nothing else. This is squarely inside what it's good at. The model isn't reasoning here, it's decorating, and small models decorate fine. A clean diff is a claim though, not a fact. The tool prints Applied. , that's the tool's word for it. So the habit, every time, is to cat the file and look. The claim and the disk have to agree, and the only way to know is to check. Hold onto that, it becomes the whole point at the end. A docstring is free. So I gave it something with an actual bug: python def apply discount price, percent : return price - price percent The bug: it's meant to take percent as a whole number, 20 for 20% off, but it treats 20 as 2000%. I asked the tool to fix exactly that. This one's a real result, because the model had to get the intent , that "20 means twenty percent", and turn it into the right math, while leaving the function shape alone. A small model can do this. This is the useful part, and it's genuinely useful. Now the interesting part. I gave it an instruction I knew was underspecified: make it safe That's not a real spec. Safe against what? Negative prices? A percent over 100? A non-number input? I didn't say, on purpose. I wanted to see what it does when I haven't drawn the line for it. I ran it three times. Same three words each time. Here's what came back. Run one produced this, and it looked sophisticated. It reached for Python's Decimal type "for precise floating point arithmetic", exactly the kind of phrase that makes you nod and hit yes: I approved it. Y . It looked careful, it looked like it knew what it was doing. It was broken. Watch: The line decimal.Decimal str percent + '/100' builds the string "20/100" and hands it to Decimal . But Decimal doesn't do division, it tries to read "20/100" as one number, fails, and throws InvalidOperation . The "safe" version crashes on every single call. And I'd already said yes, because the diff looked competent. That's the whole lesson of this post in one screenshot. A small model will hand you confident, well-commented code that doesn't work, and if you're skimming, you'll wave it through. Run three , same instruction, gave me something completely different, and this time actually good: Type checks, a negative-price guard, a cap at 100%. Genuinely more defensible than my one-liner. And I rejected it. N . Why reject the good one? Look closely: it quietly renamed the parameter percent to discount percent . Anything calling apply discount price, percent=20 by keyword breaks. It improved the inside of the function and silently changed the interface, a real cost buried in a good-looking diff. Saying no kept my one working line intact: So: three runs, three answers. One that crashes, one I wrongly approved, one that was solid but I rightly turned down. It was never consistent. The only constant in the whole test was me reading the diff. That's not a flaw in the tool, that's the tool doing exactly what it's built to do. It proposes, endlessly and unpredictably. I decide, every single time. The y/n prompt isn't a convenience. It's the entire safety model. The model can propose anything. It can change nothing. One more, a different kind of ceiling. The tool can read a folder's structure and summarize what the project does. No files opened, just the shape. I pointed it at a real project of mine and asked. The folder is a security awareness-check tool. The model read awareness-check , recipients.txt , a couple of scripts, and confidently decided it was about "emotional intelligence or positive psychology", with "surveys, quizzes", maybe "analyzing facial expressions". Completely wrong, and completely fluent about being wrong. That's the ceiling in its purest form. It saw a shape and pattern-matched it into a plausible story, with zero signal the story was made up. On structure it's never seen, a small model doesn't say "I'm not sure". It guesses, confidently, and the guess reads exactly like knowledge. That's the failure you have to keep in your head every time it tells you something you can't immediately check. After using this for real, here's the honest map of a 3B model with direct file access. It's reliable for: It is not reliable for: That last one is the rule the whole tool is built around, and it's the thing I'd tell anyone wiring a local model to their files: A local model's output is a draft, never a verdict. The diff is what it claims. The file on disk is what's true. Always check the second one. Every stage of every test above ended with a cat , reading the actual file, not trusting the tool's Applied. . That's not paranoia. Small models have shown me a clean diff and then not written it. The check is cheap. The corruption isn't. Honestly? It's flawed, but it's not bad. Once you give it specific instructions the prompt attempts are decent, and that's the whole trick, be specific. Where it earns its place is the small stuff: editing or analyzing reports, journals, markdown or text files, or just adding some data to a file by asking vibe to do it instead of typing it in myself. And if I'd rather type it manually, I do. The tool doesn't take the wheel, it just saves me the drive when I don't feel like driving. That's the honest scope. It's not writing my software. It's one adaptive, genuinely useful piece of the larger system I've built, and it's free. Pretty cool, honestly. And there's something fun about watching these raw little scripts connect and mash together and just work. I could pipe all of this to a frontier model and get a smarter result. Sometimes I do, for real reasoning, the kind of thing where being wrong is expensive. I reach for the bigger tool then. But the small local model gives me something the big one can't. It runs offline, it costs nothing, it touches only my machine, and it's fast enough to sit inside my actual workflow instead of interrupting it. The trick was never making it smarter. It's knowing exactly where it stops being good, building the tool so it can't act past that line on its own, and staying the one who decides at every write. That's directed creation, and it's bigger than local models. The tool isn't valuable because the AI is good. It's valuable because I know precisely where the AI stops being good, and I drew the line there myself instead of hoping the model would stay inside a line nobody drew. I let it do the work. Not the thinking. This tool is one piece of a two-machine setup I built on WSL2, one side for building, one for security work, the same local-AI layer wired into both. I'm putting together a full walkthrough of how it fits together. If that's something you'd want to read, follow and it'll land here. Check out my website: tagzauthor.com https://www.tagzauthor.com?utm source=devto Support TagzAuthor: ko-fi.com/tagzauthor //ko-fi.com/tagzauthor My author page: Amazon Bookstore //amazon.com/stores/author/B0DGDTFWZY